ECMAScript 6,也称为 ECMAScript 2015,是 ECMAScript 最新版本(注: 已经不是最新版本)。ES6 对于这门语言来说是一个很重要的更新,而且这是 2009 年后的 ES5 标准之后的第一次更新。现在主流的 JavaScript 引擎都已经实现这些功能——查看详情

By lukehoban

原文地址:lukehoban/es6features

这里你可以查看ES6 规范的全部标准。

ES6 包含以下新功能:

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);
});

// 词法this
var 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) == 15;
function f(x, ...y) {
  // y是一个数组
  return x * y.length;
}
f(3, 'hello', true) == 6;
function 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..offor..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*声明时会返回一个生成器实例。生成器是迭代器的一个子类并且包含了额外的nextthrow。这使得值能够流回生成器,所以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)。运行期间的行为定义由主机默认加载器定义。隐式异步模型——在被请求的模块加载处理且可用之前,不会执行代码。

// lib/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from 'lib/math';
alert('2π = ' + math.sum(math.pi, math.pi));
// otherApp.js
import { sum, pi } from 'lib/math';
alert('2π = ' + sum(pi, pi));

一些额外的新增功能,包括export default以及export *

// lib/mathplusplus.js
export * from 'lib/math';
export var e = 2.71828182846;
export default function (x) {
  return Math.log(x);
}
// app.js
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 提供了无泄漏的对象键值的侧表。

// Sets
var s = new Set();
s.add('hello').add('goodbye').add('hello');
s.size === 2;
s.has('hello') === true;
// Maps
var m = new Map();
m.set('hello', 42);
m.set(s, 34);
m.get(s) == 34;
// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined;
// Weak Sets
var 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 里面,内置的ArrayDate和 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] = 12;
arr.length == 2;

Promises

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.EPSILON;
Number.isInteger(Infinity); // false
Number.isNaN('NaN'); // false

Math.acosh(3); // 1.762747174039086
Math.hypot(3, 4); // 5
Math.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; // true
0o767 === 503; // true

Reflect 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)