ECMAScript6.0(简称ES6)是JavaScript语言的下一代标准, 已经在2015年6月正式发布了. 它的目标是使JavaScript可以用来编写复杂的大型应用程序, 成为企业级开发语言. 各个浏览器对ECMAScript6的支持可以查看 http://kangax.github.io/compat-table/es6/.
Babel转码器
Babel是一个广泛的ECMAScript6转码器, 可以将ECMAScript6代码转为ECMAScript5代码. 通过这种方式就不用担心当前环境是否支持ECMAScript6.
Babel提供了一个REPL在线编译器, 可以在线将ECMAScript6代码转为ECMAScript5代码.
Babel还提供了Babel-cli
工具, 可以使用命令行转码.
1 | # npm是node.js包管理工具,使用npm命令必须先安装node.js |
基本用法
1 | # 转码结果输出到标准输出 |
let和const关键字
ECMAScript6新增了let关键字, 用来声明变量. 它的用法与var关键字类似, 但是用let关键字声明的变量, 只在let关键字所在的代码块内有效, 不会和var关键字一样发生变量提升.
1 | { |
ECMAScript6明确规定, 区块中存在let和const关键字, 这个区块对这些声明的变量和常量, 从一开始就形成了封闭作用域. 只要在声明之前使用这些变量和常量, 就会报错.
在代码块中, 使用let关键字声明变量之前, 该变量是不可用的, 这在语法上称为TDZ
(暂时性死区).
let关键字不允许在相同作用域中重复声明同一变量, let关键字为JavaScript新增了块级作用域, ECMAScript6引入了块级作用域, 明确允许在块级作用域中声明函数.
const关键字用来声明一个只读常量, 声明过后, 常量的值就不能改变. const关键字声明的常量也是不提升的, 也存在TDZ
, 只能在声明之后使用常量.
1 | if (true) { |
对于复合类型的常量, 常量名不指向数据, 而是指向数据所在的地址. const关键字只保证变量名指向地址不变, 并不保证该地址的数据不变.
1 | const foo = { |
ECMAScript6规定var关键字和function关键字声明的全局变量, 依旧是全局对象的属性; let关键字, const关键字和class关键字声明的全局变量和常量, 不属于全局对象的属性
变量的解构赋值
1 | // 数组的解构赋值 |
ECMAScript6内部使用严格相等相等符(===)判断一个值. 如果一个数组成员不严格等于undefined,默认值不会生效.
1 | // 对象的解构赋值 |
对象的解构与数组有一些不同. 数组的元素是按次序排序的, 变量的取值由它的位置决定, 而对象的属性没有次序, 变量必须与属性同名, 才能取得正确值.
1 | // 函数参数的解构赋值 |
字符串的扩展
1 | // includes(): 返回布尔值, 表示是否找到参数字符串 |
1 | // repeat()方法返回一个新字符串, 表示将源字符串重复n次 |
1 | // padStart()方法用于头部补全 |
1 | // 模板字符串是增强版的字符串, 用反引号标识. 它可以当作普通字符串使用 |
数值的扩展
在ECMAScript5开始, 严格模式中八进制不再允许使用前缀
0
表示, 要使用前缀0o
表示
1 | // Number.isFinite()方法: 检查一个数字是否无穷(infinity) |
1 | // Number.isNaN()方法: 检查一个值是否为NaN(not a number) |
它们与传统的全局方法isFinite()和isNaN()的区别, 传统方法先调用Number将非数值的值转为数值, 在进行判断, 这两个新方法只对数值有效, 非数值一律返回false.
Number.parseInt(), Number.parseFloat():
ECMAScript6将全局方法parseInt()和parseFloat()移植到了Number对象上, 作用完全保持不变.
Number.isInteger():
用来判断一个值是否为整数, 在JavaScript内部, 整数和浮点数是同样的存储方法, 4和4.0被视为同一个值.
安全整数和Number.isSafeInteger():
JavaScript能准确表示的整数范围在-2^53
到2^53
之间(不含两个端点), 超过范围, 无法精确表示.
ECMAScript6引入了Number.MAX_SAFE_INTEGER
和Number.MIN_SAFE_INTEGER
两个常量, 用来表示这个范围的上下限.
Number.isSafeInteger()方法用来判断一个整数是否在这个范围内.
Math对象的扩展
- Math.trunc()方法用于去除一个数的小数部分, 返回整数部分.
- Math.sign()方法用来判断一个数是正数, 负数还是零.
- Math.cbrt()方法用来计算一个数的立方根.
- Math.fround()方法返回一个数的单精度浮点数形式.
- Math.hypot方法返回所有参数的平方和的平方根.
数组的扩展
1 | // Array.from()方法用于将两类对象转为真正的数组: |
1 | // Array.of()方法: 将一组值,转为数组形式 |
1 | // 数组实例的copyWithin()方法 |
1 | // 数组实例的find()方法: 找出第一个符合条件的数组元素 |
1 | // fill()方法使用给定值, 填充一个数组 |
ECMAScript6提供三个新方法–entries(), key()和values(), 用于遍历数组. 唯一区别是key()方法是对键名的遍历, values()方法是对键值的遍历,entries()方法是对键值对的遍历.
函数的扩展
1 | // 箭头函数 |
箭头函数注意点:
- 函数体内的this对象, 就是定义时所在的对象, 而不是使用时所在的对象
- 不可以当作构造函数, 不可以使用new关键字创建实例对象, 否则抛出一个错误
- 不可以使用arguments对象, 该对象在函数体中不存在. 如要使用Rest参数代替
- 不可以使用yield关键字, 箭头函数不能用作Generator函数
1 | // ECMAScript6允许给函数的参数设置默认值 |
扩展运算符
1 | // 扩展运算符(spread)是三个点( ... ) |
对象的扩展
1 | // ECMAScript6允许在对象中, 可以不写属性值 |
1 | // Object.assign()方法用于对象的合并 |
如果目标对象与源对象有同名属性, 或多个源对象有同名属性, 则后面的属性会覆盖前面的属性. Object.assign()方法实现的是浅拷贝, 而不是深拷贝. 如果源对象某个属性值是对象, 那目标杜希昂拷贝得到的是这个对象的引用.
Object.assign()方法的用处:
- 为对象添加属性
- 为对象添加方法
- 克隆对象
- 合并多个对象
- 为属性指定默认值
ECMAScript6属性的遍历方法:
- for..in循环遍历对象自身和继承的可枚举属性(不含Symbol属性)
- Object.keys(obj)方法返回一个数组, 包含对象自身的(不含继承的)所有可枚举属性(不含Symbol属性)
- Object.getOwnPropertyNames(obj)方法返回一个数组, 包含对象自身的所有属性(包含不可枚举属性, 不含Symbol属性)
- Object.getOwnPropertySymbols(obj)方法返回一个数组, 包含对象自身的所有Symbol属性
- Reflect.ownKeys(obj)方法返回一个数组, 包含对象自身的所有属性, 不管是属性名是Symbol或字符串, 也不管是否可枚举
1 | // ECMAScript5对象属性名都是字符串, 容易造成属性名的冲突 |
点运算符后面总是字符串, 所以不会读取mySymbol作为标识符所指代的那个值, 导致bar的属性名实际上是一个字符串, 而不是一个Symbol的值.
Symbol作为属性名, 该属性不出现在for..in, for..of循环中, 也不会被Object.keys()方法和Object.getOwnPropertyNames()方法返回. 但是他也不是私有属性, Object.getOwnPropertySymbols()方法可以获取指定对象的所有Symbol属性名.
Reflect.ownKeys()方法可以返回所有类型的键名, 包括常规键名和Symbol键名.
1 | const obj = { |
1 | // Symbol.for()方法可以重新使用同一个Symbol的值 |
Set和Map数据结构
ECMAScript6提供了新的数据结构Set. 它类似数组, 但元素值都是唯一的, 无重复值. Set本身是一个构造函数, 可以用来生成Set数据结构
1 | var bar = new Set(); |
Set实例对象的属性和方法
- Set.prototype.constructor属性: 构造函数, 默认是Set函数
- Set.prototype.size属性: 返回Set实例对象的成员总数
- add(value)方法: 添加某个值, 返回Set结构本身
- delete(value)方法: 删除某个值, 返回一个布尔值, 表示删除是否成功
- has(value)方法: 返回一个布尔值, 表示该值是否为Set的成员
- clear()方法: 清除所有成员, 没有返回值
JavaScript对象本质上是键值对的集合(Hash结构), 传统上只能用字符串当作键, 给使用带来了限制. 为了解决这个问题, ECMAScript6提供了Map数据结构, 它类似对象, 也是键值对的集合, 但是”键”的范围不限于字符串, 各种类型的值(包括对象)都可以当作键.
原生Map提供三个遍历器生成函数和一个遍历方法
- keys(): 返回键名的遍历器
- values(): 返回键值的遍历器
- entries(): 返回所有成员的遍历器
- forEach(): 遍历Map的所有成员
Generator函数
Generator函数从语法上可以把它理解成是一个状态机, 封装了多个内部状态. 执行Generator函数会返回一个遍历器对象, 他除了状态机, 还是一个遍历器对象生成函数, 返回的遍历器对象, 可以一次遍历Generator函数内部的每一个状态.
Generator函数形式上是一个普通函数, 但有两个特点, function关键字与函数名之间有一个星号, 函数体内部使用yield语句定义不同的内部状态.
1 | function* Gener() { |
Promise对象
1 | // 基本使用 |
Promise实例对象可以使用then()方法分别指定Resolved状态和Reject状态的回调函数. then()方法可以接收两个回调函数作为参数. 第一个回调函数是Promise对象的状态变为Resolved时调用, 第二个回调函数是Promise对象的状态变为Reject时调用. 第二个回调函数是可选的, 不一定要提供, 这两个回调函数都可以接收Promise对象传出的值作为参数. then()方法返回的是一个新的Promise实例(不是原来的Promise实例), 我们可以使用链式写法, 即then()方法之后再调用另一个then()方法.
Promise实例对象的catch()方法, 也就是.then(null, rejection)
的别名, 用于指定发生错误时的回调函数. 不要在then()方法里面定义Reject状态的回调函数(即then()方法的第二个参数), 而是使用catch()方法.
Class
1 | class Person { |
上面定义了一个”类”, 里面有一个constructor()方法, 这就是构造函数, this关键字代表实例对象. 由于类的方法默认定义在prototype对象上面, 所以类的新方法可以添加在prototype对象上面. Object.assign()方法可以很方便一次向类添加多个方法.
1 | class Person { |
类内部所有定义的方法都是不可枚举的(non-enumerable).
constructor()方法是类的默认方法, 通过new关键字创建实例对象时, 自动调用该方法. 一个类必须有constructor()方法, 如果没有显示定义, 会默认添加一个空的constructor()方法. contructor()方法默认返回的实例对象(即this)可以指定返回另一个对象.
Class继承
1 | // Class之间可以使用extends关键字实现继承 |
Object.getPrototypeOf()方法可以从子类上获取父类, 可以使用该方法判断一个类是否继承至另一个类
super关键字
- 作为函数调用时(即super(…args)), super代表父类的构造函数
- 作为对象调用时(即super.prop或者super.method()), super代表父类, 此时super既可以引用父类实例的属性和方法, 也可以引用父类的静态方法
Class静态方法
类相当于实例的原型, 所有在类中定义的方法, 都会被实例继承. 在一个方法前加上static关键字, 表示该方法不会被实例继承, 而是直接通过类来调用, 这被称为静态方法
.