ES5、6、7浅析
ECMA Script
- 它是一种由ECMA组织(前身为欧洲计算机制造商协会)制定和发布的脚本语言规范
- 而我们学的JavaScript是ECMA的实现, 但术语ECMAScript和JavaScript平时表达同一个意思
- JS包含三个部分:
- ECMAScript(核心)
- 浏览器端扩展
- DOM(文档对象模型)
- BOM(浏览器对象模型)
- 服务器端扩展
- Node
- ES的几个重要版本
- ES5 : 09年发布
- ES6(ES2015) : 15年发布, 也称为ECMA2015
- ES7(ES2016) : 16年发布, 也称为ECMA2016 (变化不大)
- 教程
- ES5
- http://www.zhangxinxu.com/wordpress/2012/01/introducing-ecmascript-5-1/
- http://www.ibm.com/developerworks/cn/web/wa-ecma262/
- ES6
- http://es6.ruanyifeng.com/
- ES7
- http://www.w3ctech.com/topic/1614
- ES5
ES5
ES5兼容性
- IE8只支持defineProperty、getOwnPropertyDescriptor的部分特性和JSON的新特性
- IE9不支持严格模式, 其它都可以
- IE10和其他主流浏览器都支持了
- PC端开发需要注意IE9以下的兼容, 但移动端开发时不需要
严格模式
- 在ES5中,除了正常运行模式(混杂模式),ES5还添加了第二种运行模式:"严格模式"(strict mode),这种模式使得Javascript在更严格的语法条件下运行
- 严格模式的作用
-
消除Javascript语法的一些不合理、不严谨之处,减少一些怪异行为
- 消除代码运行的一些不安全之处,保证代码运行的安全
- 为未来新版本的Javascript做好铺垫
-
- 使用严格模式
- 在全局或函数的第一条语句定义为: 'use strict';如果浏览器不支持, 只解析为一条简单的语句, 没有任何副作用 语法和行为改变:
- 必须用var声明变量
- 创建eval作用域
- 禁止this指向window
- 对象不能有重名的属性
- 函数不能有重名的形参
- 教程:http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html
<script type="text/javascript"> 'use strict'; // 使用严格模式 var str = 'string'; // 必须使用var来声明变量 function Person(name, age) { this.name = name; this.age = age; } // Person(); // 禁止自定义的函数中的this指向window new Person('zh', 18); var str = '123'; eval('var str = "234"; alert(str)'); // 创建eval作用域不会污染全局变量 alert(str); // 对象不能有重名的属性 var obj = { name: '123', name: '234' } <script
JSON对象
-
JSON.stringify(obj/arr)
-
将js对象(数组)转换为json对象(数组)
-
-
JSON.parse(json)
-
将json对象(数组)转换为js对象(数组)
-
ES5对Object的扩展
- Object.create(prototype[, descriptors]) : 创建一个新的对象
-
作用: 以指定对象为原型创建新的对象
-
为新的对象指定新的属性, 并对属性进行描述
-
value : 指定值
-
writable : 标识当前属性值是否是可修改的, 默认为false
-
configurable: 标识当前属性是否可以被删除 默认为false
-
enumerable: 标识当前属性是否能用for in 枚举 默认为false
- 例:
var obj = { uername: 'zh', age: 18 }; var obj1 = Object.create(obj, { sex: { value: '女', writable: true, configurable: true, enumerable: true } })Object.defineProperties(object, descriptors)
-
-
Object.defineProperties(object, descriptors)
-
作用:为指定对象定义扩展多个属性
-
get:用来获取当前属性值得回调函数
-
set:修改当前属性值得触发的回调函数,并且实参即为修改后的值
-
存取器属性:setter,getter一个用来存值,一个用来取值
- 例:
var obj2 = { firstName: '123', lastName: '234' } Object.defineProperties(obj2, { fullName: { get: function () { // 惰性求值,获取扩展属性的值,获取扩展属性值的get方法自动调用 return this.firstName + ' '+ this.lastName; }, set: function (data) { // 监听扩展属性,当扩展属性发生变化的时候会自动调用,会将变化的值作为实参注入到set函数 console.log('Set()---' + data); var names = data.split(' '); this.firstName = names[0]; this.lastName = names[1]; } } }) obj2.firstName = '456' console.log(obj2.fullName); // 456 234 console.log(obj2); // {firstName: "456", lastName: "234"} obj2.fullName = 'change fullName' console.log(obj2.fullName); // change fullName
-
-
get propertyName(){}
-
用来得到当前属性值的回调函数
-
-
set propertyName(){}
-
用来监视当前属性值变化的回调函数
-
- 例:
var obj = { firstName: '111', lastName: '222', get fullName() { return this.firstName + ' ' + this.lastName; }, set fullName(data) { var names = data.split(' '); this.firstName = names[0]; this.lastName = names[1]; } }; console.log(obj); obj.fullName = '333 444'; console.log(obj);
Array扩展
- Array.prototype.indexOf(value) : 得到值在数组中的第一个下标
- Array.prototype.lastIndexOf(value) : 得到值在数组中的最后一个下标
- Array.prototype.forEach(function(item, index){}) : 遍历数组
- Array.prototype.map(function(item, index){ }) : 遍历数组返回一个新的数组
-
var arr = [1, 2, 3, 4, 5, 6, 7, 8]; var newArr = arr.map(function (item, index) { return item + 10; }) console.log(newArr); // [11, 12, 13, 14, 15, 16, 17, 18]
-
- Array.prototype.filter(function(item, index){ }) : 遍历过滤出一个新的子数组
-
var arr = [1, 2, 3, 4, 5, 6, 7, 8]; var newArr1 = arr.filter((item, index) => item > 3 ) console.log(newArr1); // [4, 5, 6, 7, 8]
-
Function扩展
-
Function.prototype.bind(obj)
-
作用: 将函数内的this绑定为obj, 并将函数返回
-
特点:绑定完this不会立即调用当前函数,而是将函数返回
-
bind传参的方式和call一样,通常用来指定回调函数的this
-
-
bind()与call()和apply()的区别
-
bind()与call()和apply()都能指定函数中的this
-
call()/apply()是立即执行函数
-
bind()是将函数返回
- call()和apply的传参形式不同
- foo.call(obj, 123); // 直接从第二个参数开始,依次传入
- foo.apply(obj, [123]); // 第二参数必须是数组,传入放在数组里
-
ES6
关键字拓展
let关键字
- 作用:与var类似, 用于声明一个变量
- 特点:
- 在块作用域内有效
-
不能重复声明
-
不会预处理, 不存在提升
- 例:
{ // console.log(sum); // 不会预处理(预解析),不存在变量提升 let sum = 123; // let sum = 123; // 不能重复声明 } // console.log(sum); // 在块级作用域内有效,在ES5及之前没有块级作用域
- 应用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>01_let关键字</title> </head> <body> <button>测试1</button> <br> <button>测试2</button> <br> <button>测试3</button> <br> <script type="text/javascript"> let btns = document.getElementsByTagName('button'); // 闭包 // for (let i = 0; i < btns.length; i++) { // (function (i) { // btns[i].onclick = function () { // alert(i); // } // })(i) // } // 可代替闭包 for (let i = 0; i < btns.length; i++) { btns[i].onclick = function () { alert(i); } } </script> </body> </html>
const关键字
- 作用:定义一个常量
- 特点:
- 不能修改
- 块作用域有效
- 应用:保存应用需要的常量数据
变量的解构赋值
- 从数组或对象中提取值, 对多个变量进行赋值
- 数组的解构赋值
// 解构数组 let arr = [1, 3, 5, 'abc', true]; let [, , a, b] = arr // 5 "abc"
- 对象的解构赋值
// 对象的解构赋值 let { username, age, xxx } = obj; // 相当于 // let username = obj.username; // 由于let不能重复声明变量,故会在控制台中报错 // let age = obj.age; // let xxx = obj.xxx;
- 数组的解构赋值
模板字符串
-
模板字符串简化了字符串的拼接
-
模板字符串必须用 `` 包含
- 变化的部分使用${xxx}定义
- 例:
let obj = { userName: 'zh', age: 18 }; let str = '我的名字叫:' + obj.userName + ',我的年龄是' + obj.age; let tempalteStr = `我的名字叫:${obj.userName},我的年龄是${obj.age}`;
简化的对象写法
-
省略同名的属性值
-
省略方法的function
- 例:
let username = 'zh'; let age = 18; let obj = { // 同名属性可省略不写 username, age, getName() { // 可以省略函数的function return this.username; } };
函数扩展
箭头函数
-
作用: 定义匿名函数
-
基本语法:
-
没有参数: () => console.log('xxxx')
-
一个参数: i => i+2
-
大于一个参数:(i,j) => i+j
- 函数体不用大括号:默认返回结果
-
函数体如果有多个语句: 需要用{}包围,若有需要返回的内容,需要手动返回
-
-
箭头函数的特点:
-
简洁
-
箭头函数没有自己的this,箭头函数的this不是调用的时候决定的,而是在定义的时候处在的对象就是它的this
-
箭头函数的this看外层的是否有函数。如果有,外层函数的this就是内部箭头函数的this,如果没有,则this是window。
- 例
let obj2 = { name: '123', age: 18, text: () => { btn3.onclick = () => { console.log(this); // window console.log(this === window); // true } } } obj2.text();
-
-
形参的默认值
-
当不传入参数的时候默认使用形参里的默认值
- 例:
function Point(x = 1 , y = 1) { this.x = x; this.y = y; }
三点运算符
rest(可变)参数
-
用来取代arguments 但比 arguments 灵活,只能是最后部分形参参数
-
使用三点运算符时,只能把三点运算符放在最后面
- 例
function foo(a, ...value) { console.log(arguments); // 伪数组 Arguments(4) [1, 2, 3, 4, callee: (...)] console.log(value); // 数组 [2, 3, 4] } foo(1, 2, 3, 4);
扩展运算符
- 例:
var arr = [1,6] var arr1 = [2,3,4,5]; arr = [1,...arr1,6]; console.log(arr); // [1, 2, 3, 4, 5, 6] console.log(...arr); // 1 2 3 4 5 6
class类
-
通过class定义类/实现类的继承
-
在类中通过constructor定义构造方法
-
通过new来创建类的实例
-
通过extends来实现类的继承
-
通过super调用父类的构造方法
-
重写从父类中继承的一般方法
- 例
// 定义一个人物的类 class Person { constructor (name,age) { // 表示类的构造方法 this.name = name; this.age = age; } // 类的一般方法 showName() { console.log(this.name); } } let person = new Person ('000', 18); console.log(person); person.showName(); // 子类 class Child extends Person { constructor (name,age,salary) { super(name, age); // 调用父类的构造方法 this.salary = salary; } showName () { console.log(this.name, this.age, this.salary) } } let child = new Child('www', 18, 10000 ); console.log(child); child.showName();
Promise对象
-
Promise对象
-
代表了未来某个将要发生的事件(通常是一个异步操作)
-
有了promise对象, 可以将异步操作以同步的流程表达出来, 避免了层层嵌套的回调函数(俗称'回调地狱')
-
ES6的Promise是一个构造函数, 用来生成promise实例
-
-
使用promise基本步骤(2步)
-
创建promise对象
let promise = new Promise((resolve, reject) => { //初始化promise状态为 pending //执行异步操作 if(异步操作成功) { resolve(value);//修改promise的状态为fullfilled } else { reject(errMsg);//修改promise的状态为rejected } })
-
调用promise的then()
promise.then(function( result => console.log(result), errorMsg => alert(errorMsg) ))
-
-
promise对象的3个状态
-
pending: 初始化状态
-
fullfilled: 成功状态
-
rejected: 失败状态
-
- 例:
// 创建promiise对象 let promise = new Promise((resolve, reject) => { // 初始化promise状态:pending console.log(111); // 执行异步操作,通常是发送ajax请求,开启定时器 setTimeout(() => { console.log(333); // 根据异步任务的返回结果取修改promise的状态 // 异步任务执行成功 // resolve('成功标记'); // 修改promise的状态为 fullfilled:成功的状态 // 异步任务执行失败 reject('失败标记');// 修改promise的状态为 rejected:失败的状态 }, 2000) }) console.log(222); promise.then((data) => { // 成功的回调 console.log(data,'成功了') }, (error) => { // 失败的回调 console.log(error,'失败了')
Symbol
- ES5中对象的属性名都是字符串,容易造成重名,污染环境。ES6中的添加了一种原始数据类型symbol(已有的原始数据类型:String, Number, boolean, null, undefined, 对象)
-
特点:
-
Symbol属性值对应的值是唯一的,解决命名冲突问题(id)
-
Symbol值不能与其他数据进行计算,包括同字符串拼串
-
for in, for of遍历时不会遍历symbol属性。
-
-
使用
-
调用Symbol函数得到symbol值
let symbol = Symbol(); let obj = {}; obj[symbol] = 'hello';
-
传参标识
let symbol = Symbol('one'); let symbol2 = Symbol('two'); console.log(symbol);// Symbol('one') console.log(symbol2);// Symbol('two')
-
内置Symbol值
-
除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。
- Symbol.iterator:对象的Symbol.iterator属性,指向该对象的默认遍历器方法
-
- 例:
// 创建symbol属性值 let symbol = Symbol(); console.log(symbol); let obj = {username: 'zh', age: 18}; obj[symbol] = 'symbol'; // 不能使用obj.symbol = '111' console.log(obj[symbol]); // for in, for of 不能遍历symbol属性 for (let i in obj) { console.log(i); } // 每创建一个symbol的值都需要通过 let symbol2 = Symbol('one'); let symbol3 = Symbol('two'); console.log(symbol2 == symbol3); // false console.log(symbol2, symbol3); // Symbol(one) Symbol(two) // 可以去定义常量 const Person_key = Symbol('person_key'); console.log(Person_key)
-
Iterator遍历器
-
iterator是一种接口机制,为各种不同的数据结构提供统一的访问机制
-
作用:
-
为各种数据结构,提供一个统一的、简便的访问接口;
-
使得数据结构的成员能够按某种次序排列
-
ES6创造了一种新的遍历命令for...of循环,Iterator接口主要供for...of遍历。
-
-
工作原理:
-
创建一个指针对象(遍历器对象),指向数据结构的起始位置。
-
第一次调用next方法,指针自动指向数据结构的第一个成员
-
接下来不断调用next方法,指针会一直往后移动,直到指向最后一个成员
-
每调用next方法返回的是一个包含value和done的对象,{value: 当前成员的值,done: 布尔值}
-
value表示当前成员的值,done对应的布尔值表示当前的数据的结构是否遍历结束。
-
当遍历结束的时候返回的value值是undefined,done值为false
-
-
当使用for of去遍历某一个数据结构的时候,首先去找Symbol.iterator,找到了就去遍历,没找到就不能
-
使用三点运算符、解构赋值,就是默认去调用iterator接口
-
- 原生具备iterator接口的数据(可用for of遍历)
-
Array
-
arguments
-
set容器
-
map容器
-
String
-
注意:普通的对象(Object)没有部署Iterator接口
-
- 为普通的对象部署iterator接口
let targetData = { [Symbol.iterator]: function () { let nextIndex = 0; // 记录指针的位置 return { // 遍历器对象 next: () => { return nextIndex < this.length ? { value: this[nextIndex++], done: false } : { value: undefined, done: true }; } } } }
Generator函数
-
概念:
-
ES6提供的解决异步编程的方案之一
-
Generator函数是一个状态机,内部封装了不同状态的数据,
-
用来生成遍历器对象
-
可暂停函数(惰性求值), yield可暂停,next方法可启动。每次返回的是yield后的表达式结果
-
-
特点:
-
function 与函数名之间有一个星号
-
内部用yield表达式来定义不同的状态
function* generatorExample(){ let result = yield 'hello'; // 状态值为hello yield 'generator'; // 状态值为generator }
-
generator函数返回的是指针对象(iterator),而不会执行函数内部逻辑
-
调用next方法函数内部逻辑开始执行,遇到yield表达式停止,返回{value: yield后的表达式结果/undefined, done: false/true}
-
再次调用next方法会从上一次停止时的yield处开始,直到最后
-
yield语句返回结果通常为undefined, 当调用next方法时传参内容会作为启动时yield语句的返回值。
-
- 例:
// generator函数 function* myGenerator() { console.log('第一次执行'); let result = yield 'hello'; // 当调用第一次 MG.next() 时,指针停在此处 console.log('第二次执行'); console.log(result); // aaaaa yield 'generator' console.log('遍历完成') return "完成" } let MG = myGenerator(); // 返回一个遍历器对象/遍历器对象(Iterator) console.log(MG.next()); //done属性反映当前generator是否遍历完成,false表示没有遍历完 console.log(MG.next('aaaaa')); // next()调用的时候传参,传入的参数会作为next方法启动时候的返回值 console.log(MG.next()); //
async函数
- 概念:真正意义上去解决异步回调的问题,同步流程表达异步操作
-
本质:Generator的语法糖
-
语法:
async function foo(){ await 异步操作; await 异步操作; }
-
特点:
-
不需要像Generator去调用next方法,遇到await等待,当前的异步操作完成就往下执行
-
返回的总是Promise对象,可以用then方法进行下一步操作
-
async取代Generator函数的星号*,await取代Generator的yield
-
语意上更为明确,使用简单
-
字符串扩展
-
includes(str) : 判断是否包含指定的字符串
-
startsWith(str) : 判断是否以指定字符串开头
-
endsWith(str) : 判断是否以指定字符串结尾
-
repeat(count) : 重复指定次数
Number扩展
-
二进制与八进制数值表示法: 二进制用0b, 八进制用0o
-
Number.isFinite(i) : 判断是否是有限大的数
-
Number.isNaN(i) : 判断是否是NaN
-
Number.isInteger(i) : 判断是否是整数
-
Number.parseInt(str) : 将字符串转换为对应的数值
-
Math.trunc(i) : 直接去除小数部分
Array扩展
-
Array.from(v) : 将伪数组对象或可遍历对象转换为真数组
-
Array.of(v1, v2, v3) : 将一系列值转换成数组
-
find(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素
let arr2 = [1,2,3,4,5,6,7,8]; var result = arr2.find((item, index) => { return item > 4; }) console.log(result) // 5
-
findIndex(function(value, index, arr){return true}) : 找出第一个满足条件返回true的元素下标
对象扩展
- Object.is(v1, v2):判断2个数据是否完全相等。Object.is() 是根据字符串来进行判断
- Object.assign(target, source1, source2..):将源对象的属性复制到目标对象上
let obj = {}; let obj1 = {username:"anverson", age: 42}; let obj2 = {sex: "女"}; Object.assign(obj, obj1); // 将源对象的属性复制到目标对象上 Object.assign(obj, obj2); console.log(obj); // {username: "anverson", age: 42, sex: "女"}
-
直接操作 __proto__ 属性(ES6之前不能直接操作__proto__而需要通过prototype操作)
let obj2 = {}; obj2.__proto__ = obj1;
深度克隆
-
拷贝数据
- 基本数据类型:拷贝后会生成一份新的数据,修改拷贝以后的数据不会影响原数据,存储的是该对象的实际数据
- 对象/数组:拷贝后不会生成新的数据,而是引用。修改拷贝以后的数据会影响原来的数据,存储的是该对象在栈中引用(引用地址),真实的数据存放在堆内存里
-
浅拷贝(对象/数组):拷贝引用,拷贝后的数据会影响原来的数据,使得原数据不安全
-
深拷贝(深度克隆):拷贝的时候生成新数据,拷贝后的数据不会影响原来的数据
-
拷贝数据的方法:
-
直接赋值给一个变量:基本数据类型为深拷贝,对象/数组为浅拷贝
-
Object.assign():浅拷贝
- Array.prototye.concat():深拷贝,数组中有对象时对象为浅拷贝
- Array.prototype.slice():深拷贝,数组中有对象时对象为浅拷贝
- JSON.parse(JSON.stringfiy()):彻底的深拷贝(深度克隆),但拷贝的数据中不能有函数
-
- 深度克隆实现
// 检测数据类型函数 function checkedType(target) { return Object.prototype.toString.call(target).slice(8, -1); } // 实现深度克隆 ---> 对象/数组 function clone(target) { let result, targetType = checkedType(target); if (targetType === 'Object') { result = {}; } else if (targetType === 'Array') { result = [] } else { return target; } // 遍历目标数据 for (i in target) { let value = target[i]; // 判断目标结构里的每一项值是否存在对象/数组 if (checkedType(value) === 'Object' || checkedType(value) === 'Array') { // 继续遍历获取到的value result[i] = clone(value); } else { result[i] = value; } } return result; }
Set和Map
-
Set容器 : 无序不可重复的多个value的集合体
-
Set()
-
Set(array)
-
add(value)
-
delete(value)
-
has(value)
-
clear()
-
size
-
-
Map容器 : 无序的 key不重复的多个key-value的集合体
-
Map()
-
Map(array)
-
set(key, value):添加
-
get(key)
-
delete(key)
-
has(key)
-
clear()
-
size
-
- 例
// set let set = new Set([1, 3, 5, 5, 4, 8]); // 会将重复的5删除 console.log(set.size, set); set.add(7); console.log(set.size, set); console.log(set.has(8)); console.log(set.has(0)); set.clear(); console.log(set.size, set); // map let map = new Map([['aaa', 'username'], ['ccc', 'username']]); console.log(map.size, map); map.set('age', 111); console.log(map.size, map); map.delete('aaa'); console.log(map.size, map);
for of循环
- 作用
-
遍历数组
-
遍历Set
-
遍历Map
-
遍历字符串
-
遍历伪数组
-
- 例
// 利用set给数组去重 function removeSame(arr) { let newArr = []; let set = new Set(arr); for (i of set) { newArr.push(i) } return newArr; }
ES7
指数运算符(幂)
-
指数运算符(幂): **
- 例:
console.log(3**3); // 3的3次幂 --- 9
Array.prototype.includes(value)
-
判断数组中是否包含指定value
- 例
let arr = [1,2,6,'abc']; console.log(arr.includes('a')); // false