# ES6 Features
Table of Contents
ECMAScript 6,也称为 ECMAScript 2015,是 ECMAScript 最新版本(注: 已经不是最新版本)。ES6 对于这门语言来说是一个很重要的更新,而且这是 2009 年后的 ES5 标准之后的第一次更新。现在主流的 JavaScript 引擎都已经实现这些功能——查看详情。
By lukehoban
这里你可以查看ES6 规范的全部标准。
ES6 包含以下新功能:
- arrows 箭头函数
- classes 类
- enhanced object literals 对象字面量
- template strings 字符串模版
- destructuring 解构赋值
- default + rest + spread 默认值/rest 参数/扩展运算符
- let + const
- iterators + for..of 迭代器/for..of
- generators 生成器
- Unicode 统一码
- modules 模块
- module loaders 模块加载器
- Map + Set + Weakmap + Weakset
- proxies 代理
- symbols 符号
- subclassable built-ins 内置子类
- promises
- Math + Number + String + Array + Object APIs
- binary and octal literals 二进制和八进制字面量
- Reflect Api
- tail calls 尾调用
ECMAScript 6 功能
Arrows 箭头函数
箭头函数是使用=>符号简写的函数。它们在语法上类似于 C#,Java 8 以及 CoffeeScript 中的功能。他们对于语句式体和表达式体(直接返回值)都有很好的支持。不同于普通函数,箭头函数不绑定 this, 从自己作用域上一层继承 this
// 表达式体var odds = evens.map((v) => v + 1)var nums = evens.map((v, i) => v + i)var pairs = evens.map((v) => ({ even: v, odd: v + 1 }))
// 语句式体nums.forEach((v) => { if (v % 5 === 0) fives.push(v)})
// 词法thisvar bob = { _name: 'Bob', _friends: [], printFriends() { this._friend.forEach((f) => console.log(_this.name + ' knows ' + f)) },}更多信息: MDN Arrow Functions
Classes 类
ES6 的类是基于原型继承的面向对象简易语法糖,这使得有一个更方便的类声明去使用,并鼓励互通性。类支持基于原型继承,super调用,实例化,静态方法以及 constructor构造。
class SkinnedMesh extends THREE.Mesh { constructor(geometry, materials) { super(geometry, materials)
this.idMatrix = SkinnedMesh.defaultMatrix() this.bones = [] this.boneMatrices = [] //... } update(camera) { //... super.update() } get boneCount() { return this.bones.length } set matrixType(matrixType) { this.idMatrix = SkinnedMesh[matrixType]() } static defaultMatrix() { return new THREE.Matrix4() }}更多信息: MDN Classes
Enhanced object literals 对象字面量
对象字面量被扩展至支持在构造时设置原型,简写了 foo: foo 的赋值,定义方法,super调用以及通过表达式计算属性名。同时,这些使对象字面值和类声明更加紧密地联系起来,让基于对象的设计从中受益更加便利。
var obj = { // __proto__ __proto__: theProtoObj, // 简写赋值:‘handler: handler’ handler, // 方法 toString() { // Super调用 return 'd ' + super.toString() }, // (动态)计算属性名称 ['prop_' + (() => 42)()]: 42,}更多信息: MDN Grammar and types: Enhanced Object literals
Template strings 字符串模版
字符串模版为构建字符串提供了语法糖,这有点像 Perl, Python 等字符串插值功能。可选的,允许添加标签来自定义字符串模版,避免注入攻击或者从字符串内容中构建更加高级别的数据结构。
// 创建基本字符串面量;`In JavaScript '\n' is a line-feed.` // 多行字符串`In JavaScript this is not legal.`
// 字符串插值var name = 'Bob', time = 'today';`Hello ${name}, how are you ${time}?`
// 构建一个HTTP请求前缀使用插值的替换和构造POST`http://foo.org/bar?a=${a}&b=${b} Content-Type: application/json X-Credentials: ${credentials} { "foo": ${foo}, "bar": ${bar}}`(myOnReadyStateChangeHandler)更多信息: MDN Template Strings
Destructuring 解构赋值
解构赋值允许使用模式匹配进行绑定,并支持匹配数组和对象。解构能够故障弱化,在查找类似于标准对象 foo["bar"],当未找到时会提供 undefined值。
// 匹配数组var [a, , b] = [1, 2, 3]
// 匹配对象var { op: a, lhs: { op: b }, rhs: c,} = getASTNode()
// 匹配对象作用域的'op', 'lhs', 'rhs'var { op, lhs, rhs } = getASTNode()
// 作为参数function g({ name: x }) { console.log(x)}g({ name: 5 })
// 解构故障弱化var [a] = []a === undefined
// 带默认值的的解构赋值故障弱化var [a = 1] = []a === 1更多信息:MDN Destructuring assignment
Default + rest + spread 默认值/rest 参数/扩展运算符
在函数调用中可以指定默认参数值,将数组转换成连续参数,绑定尾部参数到数组中。rest 替代了arguments的需求,并更直接地解决了问题。
function f(x, y = 12) { // 如果y没有传值或者被传入undefined,那么y的值为12 return x + y}f(3) == 15function f(x, ...y) { // y是一个数组 return x * y.length}f(3, 'hello', true) == 6function f(x, y, z) { return x + y + z}// 传递数组里的每一个元素作为参数f(...[1, 2, 3]) == 6更多信息:Default parameters, Rest parameters, Spread Operator
Let + Const
绑定并构造块作用域。let是一个新的var,const只能声明一次。静态限制,防止使用之前已经被声明。
function f() { { let x { // 正常,块作用域里的名称 const x = 'sneaky' // 错误,x是常量 x = 'foo' } // 错误,在块里面已经被声明 let x = 'inner' }}更多信息: let statement, const statment
Iterators + for..of 迭代器/for..of
迭代对象能够自定义迭代,像 CLR 的 IEnumerable 或 Java 的 Iterable。总结来说,for..of是for..in的基础上自定义的迭代。不需要实现数组,启用了像 LINQ 惰性设计模式。
let fibonacci = { [Symbol.iterator]() { let pre = 0, cur = 1 return { next() { ;[pre, cur] = [cur, pre + cur] return { done: false, value: cur } }, } },}
for (var n of fibonacci) { // 缩减数列到1000 if (n > 1000) break console.log(n)}迭代是基于鸭子类型接口(仅使用 TypeScript 语法作为展示)
interface IteratorResult { done: boolean value: any}interface Iterator { next(): IteratorResult}interface Iterable { [Symbol.iterator](): Iterator}更多信息: MDN for…of
Generators 生成器
生成器是一个使用了function*和yield简易化迭代器生成的。一个函数作为function*声明时会返回一个生成器实例。生成器是迭代器的一个子类并且包含了额外的next和throw。这使得值能够流回生成器,所以yield是一个返回值(或者抛出)的表达式。
注意:也可以用于启用’await’式的异步编程,请参考 ES7 await 提案。
var fibonacci = { [Symbol.iterator]: function* () { var pre = 0, cur = 1 for (;;) { var temp = pre pre = cur cur += temp yield cur } },}
for (var n of fibonacci) { // 缩减数列到1000 if (n > 1000) break console.log(n)}生成器的接口是(仅使用 TypeScript 语法作为展示):
interface Generator extends Iterator { next(value?: any): IteratorResult throw(exception: any)}更多信息: MDN iteration protocols
Unicode 统一码
非破坏性地增加支持统一码,包括了新的字符字面量统一码以及新的正则表达式u处理位码,以及新的 API 也可以在 21 位位码处理字符串。这些增加的功能支持在在 JavaScript 里面建立全局应用。
// 等同于ES5.1'𠮷'.length == 2
// 新的正则表达式, 内置‘u’'𠮷'.match(/./u)[0].length == 2
// 新形式;('\u{20BB7}' == '𠮷') == '\uD842\uDFB7'
// 新的字符串操作'𠮷'.codePointAt(0) == 0x20bb7
// for-of迭代位码for (var c of '𠮷') { console.log(c)}更多信息: MDN RegExp.prototype.unicode
Modules 模块
语言级别地支持模块化的组件定义。借鉴于流行的 JavaScript 模块加载器(AMD, CommonJS)。运行期间的行为定义由主机默认加载器定义。隐式异步模型——在被请求的模块加载处理且可用之前,不会执行代码。
export function sum(x, y) { return x + y}export var pi = 3.141593import * as math from 'lib/math'alert('2π = ' + math.sum(math.pi, math.pi))import { sum, pi } from 'lib/math'alert('2π = ' + sum(pi, pi))一些额外的新增功能,包括export default以及export *
export * from 'lib/math'export var e = 2.71828182846export default function (x) { return Math.log(x)}import ln, { pi, e } from 'lib/mathplusplus'alert('2π = ' + ln(e) * pi * 2)更多信息: import statement, export statement
Module loaders 模块加载器
模块加载器支持:
- 动态加载
- 状态隔离
- 全局命名空间隔离
- 编译钩子
- 内嵌虚拟化
默认的加载器能够被配置,同时在隔离或者上下文可以构造新加载器执行和加载代码。
// 动态加载 – ‘System’ 是默认的加载器System.import('lib/math').then(function (m) { alert('2π = ' + m.sum(m.pi, m.pi))})
// 创建一个执行沙盒 - 新的加载器var loader = new Loader({ global: fixup(window), // replace ‘console.log’})loader.eval("console.log('hello world!');")
// 直接操作模块缓存System.get('jquery')System.set('jquery', Module({ $: $ })) // 警告: 尚未最终确定Map + Set + Weakmap + Weakset
常见算法中的高效数据结构。WeakMap 提供了无泄漏的对象键值的侧表。
// Setsvar s = new Set()s.add('hello').add('goodbye').add('hello')s.size === 2s.has('hello') === true// Mapsvar m = new Map()m.set('hello', 42)m.set(s, 34)m.get(s) == 34// Weak Mapsvar wm = new WeakMap()wm.set(s, { extra: 42 })wm.size === undefined// Weak Setsvar ws = new WeakSet()ws.add({ data: 42 })// 因为增加的对象没有其他的参值,所以在set不会被持有更多信息:Map, Set, WeakMap, WeakSet
Proxies 代理
代理允许创建具有目标对象范围内所有行为的对象。可用于拦截,对象虚拟化,日志记录/分析等。
// 代理一个普通的对象var target = {}var handler = { get: function (receiver, name) { return `Hello, ${name}!` },}
var p = new Proxy(target, handler)p.world === 'Hello, world!'// 代理一个函数对象var target = function () { return 'I am the target'}var handler = { apply: function (receiver, ...args) { return 'I am the proxy' },}
var p = new Proxy(target, handler)p() === 'I am the proxy'这里是所有在运行级别的元操作的trap(trap: 提供属性访问的方法)
var handler ={ get:..., set:..., has:..., deleteProperty:..., apply:..., construct:..., getOwnPropertyDescriptor:..., defineProperty:..., getPrototypeOf:..., setPrototypeOf:..., enumerate:..., ownKeys:..., preventExtensions:..., isExtensible:...}更多信息: MDN Proxy
Symbols 符号
符号能够访问控制对象的状态,符号允许对象属性的键值是string或者symbol。符号是一个新的原始类型,可选的参数description可以用于调试—但这不是符号本身的一部分。符号是唯一的(像 gensym),但不是私有的,因为它们能够通过Object.getOwnPropertySymbols反射特性公开的
var MyClass = (function() {
// 模块作用域符号 var key = Symbol("key");
function MyClass(privateData) { this[key] = privateData; }
MyClass.prototype = { doStuff: function() { ... this[key] ... } };
return MyClass;})();
var c = new MyClass("hello")c["key"] === undefined更多信息: MDN Symbol
Subclassable built-ins 内置子类
在 ES6 里面,内置的Array,Date和 DOMElement对象能够创建子类。
对于命名为Ctor函数的对象构造,现使用两个阶段(都是虚拟派遣):
- 调用
Ctor[@@create]去申请对象,安装任意特殊行为 - 在新实例中调用构造器进行初始化
已知@@create符号可通过Symbol.create得到。内置对象现在明确地暴露它们的@@create
// 数组的伪代码class Array { constructor(...args) { /* ... */ } static [Symbol.create]() { // 设置指定的 [[DefineOwnProperty]] 来更新'length' }}
// 数组子类的用户代码class MyArray extends Array { constructor(...args) { super(...args) }}
// Two-phase 'new':// 1) 调用 @@create 申请对象// 2) 在新实例上调用构造函数var arr = new MyArray()arr[1] = 12arr.length == 2Promises
Promise 是一个异步编程的库,Promise 是将来可能提供的值的最先表示。Promise 现已经在许多库使用中。
function timeout(duration = 0) { return new Promise((resolve, reject) => { setTimeout(resolve, duration) })}
var p = timeout(1000) .then(() => { return timeout(2000) }) .then(() => { throw new Error('hmm') }) .catch((err) => { return Promise.all([timeout(100), timeout(200)]) })更多信息:MDN Promise
Math + Number + String + Array + Object APIs
许多新的库被增加,包括核心的数学库,数组转换,字符串和用来拷贝的Object.assign方法
Number.EPSILONNumber.isInteger(Infinity) // falseNumber.isNaN('NaN') // false
Math.acosh(3) // 1.762747174039086Math.hypot(3, 4) // 5Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2
'abcde'.includes('cd') // true'abc'.repeat(3) // "abcabcabc"
Array.from(document.querySelectorAll('*')) // 返回一个数组Array.of(1, 2, 3) // 类似new Array(...), 但不用指明第一个参数 [(0, 0, 0)].fill(7, 1) // [0,7,7] [(1, 2, 3)].find((x) => x == 3) // 3 [(1, 2, 3)].findIndex((x) => x == 2) // 1 [(1, 2, 3, 4, 5)].copyWithin(3, 0) // [1, 2, 3, 1, 2] [('a', 'b', 'c')].entries() // iterator [0, "a"], [1,"b"], [2,"c"] [('a', 'b', 'c')].keys() // iterator 0, 1, 2 [('a', 'b', 'c')].values() // iterator "a", "b", "c"
Object.assign(Point, { origin: new Point(0, 0) })更多信息: Number, Math, Array.from, Array.of, Array.prototype.copyWithin, Object.assign
Binary and octal literals 二进制和八进制字面量
两个新的数字字面量被增加的,二进制(b)和八进制(o)
0b111110111 === 503 // true0o767 === 503 // trueReflect Api
完整反射 API 暴露对象在运行级别上的元操作。这实际上是代理 API 反转,并且允许进行与代理 trap 相同的元操作调用。特别适合用于实现代理。
更多信息: MDN Reflect
Tail calls 尾调用
保证在尾部位置的调用不会无限制地增加堆栈。当输入是无界的情况下可以确保递归算法的安全。
function factorial(n, acc = 1) { 'use strict'; if (n <= 1) return acc; return factorial(n - 1, n * acc);}
// 在当今大部分堆栈会溢出,// 在ES6里,任意输入都是安全的factorial(100000)