学习 ES2015
本文档最初取自 Luke Hoban 出色的 es6features 存储库。去 GitHub 上给它一个星吧!
¥This document was originally taken from Luke Hoban's excellent es6features repo. Go give it a star on GitHub!
介绍
¥Introduction
ECMAScript 2015 是 2015 年 6 月批准的 ECMAScript 标准。
¥ECMAScript 2015 is an ECMAScript standard that was ratified in June 2015.
ES2015 是该语言的一次重大更新,也是自 2009 年 ES5 标准化以来对该语言的第一次重大更新。这些功能在主要 JavaScript 引擎中的实现是 现在正在进行中。
¥ES2015 is a significant update to the language, and the first major update to the language since ES5 was standardized in 2009. Implementation of these features in major JavaScript engines is underway now.
有关 ECMAScript 2015 语言的完整规范,请参阅 ES2015 标准。
¥See the ES2015 standard for full specification of the ECMAScript 2015 language.
ECMAScript 2015 特性
¥ECMAScript 2015 Features
箭头和词法 This
¥Arrows and Lexical This
箭头是使用 =>
语法的函数简写。它们在语法上类似于 C#、Java 8 和 CoffeeScript 中的相关功能。它们支持表达式和语句体。与函数不同,箭头与其周围的代码共享相同的词法 this
。如果箭头在另一个函数中,它共享其父函数的 "arguments" 变量。
¥Arrows are a function shorthand using the =>
syntax. They are syntactically
similar to the related feature in C#, Java 8 and CoffeeScript. They support
both expression and statement bodies. Unlike functions, arrows share the same
lexical this
as their surrounding code. If an arrow is inside another function,
it shares the "arguments" variable of its parent function.
// Expression bodies
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);
// Statement bodies
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
// Lexical this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
};
// Lexical arguments
function square() {
let example = () => {
let numbers = [];
for (let number of arguments) {
numbers.push(number * number);
}
return numbers;
};
return example();
}
square(2, 4, 7.5, 8, 11.5, 21); // returns: [4, 16, 56.25, 64, 132.25, 441]
类
¥Classes
ES2015 类是基于原型的面向对象模式的语法糖。拥有一个方便的声明形式使类模式更易于使用,并鼓励互操作性。类支持基于原型的继承、超级调用、实例和静态方法以及构造函数。
¥ES2015 classes are syntactic sugar over the prototype-based OO pattern. Having a single convenient declarative form makes class patterns easier to use, and encourages interoperability. Classes support prototype-based inheritance, super calls, instance and static methods and constructors.
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
super(geometry, materials);
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
static defaultMatrix() {
return new THREE.Matrix4();
}
}
增强的对象字面量
¥Enhanced Object Literals
对象字面量被扩展以支持在构造时设置原型、foo: foo
赋值的简写、定义方法和进行超级调用。同时,它们还使对象字面量和类声明更紧密地结合在一起,并让基于对象的设计受益于一些相同的便利。
¥Object literals are extended to support setting the prototype at construction,
shorthand for foo: foo
assignments, defining methods and making super calls.
Together, these also bring object literals and class declarations closer
together, and let object-based design benefit from some of the same
conveniences.
var obj = {
// Sets the prototype. "__proto__" or '__proto__' would also work.
__proto__: theProtoObj,
// Computed property name does not set prototype or trigger early error for
// duplicate __proto__ properties.
['__proto__']: somethingElse,
// Shorthand for ‘handler: handler’
handler,
// Methods
toString() {
// Super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
[ "prop_" + (() => 42)() ]: 42
};
proto
属性需要原生支持,并且在以前的 ECMAScript 版本中已弃用。现在大多数引擎都支持该属性,但 有些没有。另外,请注意,只有 网络浏览器 需要实现它,如 附件 B 中所示。它在 Node 中可用。
¥The proto
property requires native support, and was deprecated in previous ECMAScript versions. Most engines now support the property, but some do not. Also, note that only web browsers are required to implement it, as it's in Annex B. It is available in Node.
模板字符串
¥Template Strings
模板字符串为构造字符串提供了语法糖。这类似于 Perl、Python 等中的字符串插值功能。可选地,可以添加标签以允许自定义字符串构造,避免注入攻击或从字符串内容构造更高级别的数据结构。
¥Template strings provide syntactic sugar for constructing strings. This is similar to string interpolation features in Perl, Python and more. Optionally, a tag can be added to allow the string construction to be customized, avoiding injection attacks or constructing higher level data structures from string contents.
// Basic literal string creation
`This is a pretty little template string.`
// Multiline strings
`In ES5 this is
not legal.`
// Interpolate variable bindings
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// Unescaped template strings
String.raw`In ES5 "\n" is a line-feed.`
// Construct an HTTP request prefix is used to interpret the replacements and construction
GET`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
解构
¥Destructuring
解构允许使用模式匹配进行绑定,并支持匹配数组和对象。解构是软故障的,类似于标准对象查找 foo["bar"]
,在未找到时生成 undefined
值。
¥Destructuring allows binding using pattern matching, with support for matching
arrays and objects. Destructuring is fail-soft, similar to standard object
lookup foo["bar"]
, producing undefined
values when not found.
// list matching
var [a, ,b] = [1,2,3];
a === 1;
b === 3;
// object matching
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()
// object matching shorthand
// binds `op`, `lhs` and `rhs` in scope
var {op, lhs, rhs} = getASTNode()
// Can be used in parameter position
function g({name: x}) {
console.log(x);
}
g({name: 5})
// Fail-soft destructuring
var [a] = [];
a === undefined;
// Fail-soft destructuring with defaults
var [a = 1] = [];
a === 1;
// Destructuring + defaults arguments
function r({x, y, w = 10, h = 10}) {
return x + y + w + h;
}
r({x:1, y:2}) === 23
默认 + 剩余 + 展开
¥Default + Rest + Spread
被调用者评估的默认参数值。将数组转换为函数调用中的连续参数。将尾随参数绑定到数组。剩余取代了对 arguments
的需求,更直接地解决了常见情况。
¥Callee-evaluated default parameter values. Turn an array into consecutive
arguments in a function call. Bind trailing parameters to an array. Rest
replaces the need for arguments
and addresses common cases more directly.
function f(x, y=12) {
// y is 12 if not passed (or passed as undefined)
return x + y;
}
f(3) == 15
function f(x, ...y) {
// y is an Array
return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument
f(...[1,2,3]) == 6
Let + Const
块作用域的绑定构造。let
是新的 var
。const
是单次赋值。静态限制阻止在分配之前使用。
¥Block-scoped binding constructs. let
is the new var
. const
is
single-assignment. Static restrictions prevent use before assignment.
function f() {
{
let x;
{
// this is ok since it's a block scoped name
const x = "sneaky";
// error, was just defined with `const` above
x = "foo";
}
// this is ok since it was declared with `let`
x = "bar";
// error, already declared above in this block
let x = "inner";
}
}
迭代器 + For..Of
¥Iterators + For..Of
迭代器对象支持自定义迭代,如 CLR IEnumerable 或 Java Iterable。使用 for..of
将 for..in
推广到基于自定义迭代器的迭代。不需要实现数组,启用像 LINQ 这样的惰性设计模式。
¥Iterator objects enable custom iteration like CLR IEnumerable or Java
Iterable. Generalize for..in
to custom iterator-based iteration with
for..of
. Don’t require realizing an array, enabling lazy design patterns like
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) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
迭代基于这些动态接口(仅使用 TypeScript 类型语法进行说明):
¥Iteration is based on these duck-typed interfaces (using TypeScript type syntax for exposition only):
interface IteratorResult {
done: boolean;
value: any;
}
interface Iterator {
next(): IteratorResult;
}
interface Iterable {
[Symbol.iterator](): Iterator
}
生成器
¥Generators
生成器使用 function*
和 yield
简化了迭代器创作。声明为 function* 的函数返回一个 Generator 实例。生成器是迭代器的子类型,其中包括额外的 next
和 throw
。这些使值能够流回生成器,因此 yield
是一种返回值(或抛出)的表达式形式。
¥Generators simplify iterator-authoring using function*
and yield
. A function
declared as function* returns a Generator instance. Generators are subtypes of
iterators which include additional next
and throw
. These enable values to
flow back into the generator, so yield
is an expression form which returns a
value (or throws).
注意:也可用于启用类似“await”的异步编程,另见 ES7 await
提案。
¥Note: Can also be used to enable ‘await’-like async programming, see also ES7 await
proposal.
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) {
// truncate the sequence at 1000
if (n > 1000)
break;
console.log(n);
}
生成器接口是(仅使用 TypeScript 类型语法进行说明):
¥The generator interface is (using TypeScript type syntax for exposition only):
interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}
推导式
¥Comprehensions
在 Babel 6.0 中移除
¥Removed in Babel 6.0
Unicode
支持完整 Unicode 的非破坏性添加,包括字符串中的新 unicode 字面量形式和用于处理代码点的新 RegExp u
模式,以及用于在 21 位代码点级别处理字符串的新 API。这些新增功能支持在 JavaScript 中构建全局应用。
¥Non-breaking additions to support full Unicode, including new unicode literal
form in strings and new RegExp u
mode to handle code points, as well as new
APIs to process strings at the 21bit code points level. These additions support
building global apps in JavaScript.
// same as ES5.1
"𠮷".length == 2
// new RegExp behaviour, opt-in ‘u’
"𠮷".match(/./u)[0].length == 2
// new form
"\u{20BB7}" == "𠮷"
"𠮷" == "\uD842\uDFB7"
// new String ops
"𠮷".codePointAt(0) == 0x20BB7
// for-of iterates code points
for(var c of "𠮷") {
console.log(c);
}
模块
¥Modules
用于组件定义的模块的语言级支持。对流行的 JavaScript 模块加载器(AMD、CommonJS)的模式进行编码。由宿主定义的默认加载程序定义的运行时行为。隐式异步模型 - 在请求的模块可用并处理之前不会执行任何代码。
¥Language-level support for modules for component definition. Codifies patterns from popular JavaScript module loaders (AMD, CommonJS). Runtime behaviour defined by a host-defined default loader. Implicitly async model – no code executes until requested modules are available and processed.
// lib/math.js
export function sum(x, y) {
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
console.log("2π = " + sum(pi, pi));
一些附加功能包括 export default
和 export *
:
¥Some additional features include export default
and export *
:
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
return Math.exp(x);
}
// app.js
import exp, {pi, e} from "lib/mathplusplus";
console.log("e^π = " + exp(pi));
Babel 可以将 ES2015 模块转换为多种不同的格式,包括 Common.js、AMD、System 和 UMD。你甚至可以创建自己的。有关详细信息,请参阅 模块文档。
¥Babel can transpile ES2015 Modules to several different formats including Common.js, AMD, System, and UMD. You can even create your own. For more details see the modules docs.
模块加载器
¥Module Loaders
这在 ECMAScript 2015 规范中保留为实现定义。最终标准将在 WHATWG 的 加载器规范 中,但目前正在进行中。以下内容来自之前的 ES2015 草案。
¥This is left as implementation-defined within the ECMAScript 2015 specification. The eventual standard will be in WHATWG's Loader specification, but that is currently a work in progress. What is below is from a previous ES2015 draft.
模块加载器支持:
¥Module loaders support:
-
动态加载
¥Dynamic loading
-
状态隔离
¥State isolation
-
全局命名空间隔离
¥Global namespace isolation
-
编译钩子
¥Compilation hooks
-
嵌套虚拟化
¥Nested virtualization
可以配置默认的模块加载器,并且可以构造新的加载器来评估和加载隔离或受限上下文中的代码。
¥The default module loader can be configured, and new loaders can be constructed to evaluate and load code in isolated or constrained contexts.
// Dynamic loading – ‘System’ is default loader
System.import("lib/math").then(function(m) {
alert("2π = " + m.sum(m.pi, m.pi));
});
// Create execution sandboxes – new Loaders
var loader = new Loader({
global: fixup(window) // replace ‘console.log’
});
loader.eval("console.log(\"hello world!\");");
// Directly manipulate module cache
System.get("jquery");
System.set("jquery", Module({$: $})); // WARNING: not yet finalized
Map + Set + WeakMap + WeakSet
通用算法的高效数据结构。WeakMaps 提供无泄漏的对象键边表。
¥Efficient data structures for common algorithms. WeakMaps provides leak-free object-key’d side tables.
// 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 });
// Because the added object has no other references, it will not be held in the set
代理
¥Proxies
代理可以创建具有可用于宿主对象的全部行为的对象。可用于拦截、对象虚拟化、日志记录/分析等。
¥Proxies enable creation of objects with the full range of behaviors available to host objects. Can be used for interception, object virtualization, logging/profiling, etc.
// Proxying a normal object
var target = {};
var handler = {
get: function (receiver, name) {
return `Hello, ${name}!`;
}
};
var p = new Proxy(target, handler);
p.world === "Hello, world!";
// Proxying a function object
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";
所有运行时级元操作都有可用的捕获:
¥There are traps available for all of the runtime-level meta-operations:
var handler =
{
// target.prop
get: ...,
// target.prop = value
set: ...,
// 'prop' in target
has: ...,
// delete target.prop
deleteProperty: ...,
// target(...args)
apply: ...,
// new target(...args)
construct: ...,
// Object.getOwnPropertyDescriptor(target, 'prop')
getOwnPropertyDescriptor: ...,
// Object.defineProperty(target, 'prop', descriptor)
defineProperty: ...,
// Object.getPrototypeOf(target), Reflect.getPrototypeOf(target),
// target.__proto__, object.isPrototypeOf(target), object instanceof target
getPrototypeOf: ...,
// Object.setPrototypeOf(target), Reflect.setPrototypeOf(target)
setPrototypeOf: ...,
// Object.keys(target)
ownKeys: ...,
// Object.preventExtensions(target)
preventExtensions: ...,
// Object.isExtensible(target)
isExtensible :...
}
由于 ES5 的限制,代理不能被转译或填充。请参阅 各种 JavaScript 引擎 中的支持。
¥Due to the limitations of ES5, Proxies cannot be transpiled or polyfilled. See support in various JavaScript engines.
符号
¥Symbols
符号启用对象状态的访问控制。符号允许属性由 string
(如在 ES5 中)或 symbol
键入。符号是一种新的原始类型。调试时使用的可选 name
参数 - 但不是身份的一部分。符号是唯一的(如 gensym),但不是私有的,因为它们通过反射特性(如 Object.getOwnPropertySymbols
)暴露出来。
¥Symbols enable access control for object state. Symbols allow properties to be
keyed by either string
(as in ES5) or symbol
. Symbols are a new primitive
type. Optional name
parameter used in debugging - but is not part of identity.
Symbols are unique (like gensym), but not private since they are exposed via
reflection features like Object.getOwnPropertySymbols
.
(function() {
// module scoped symbol
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
... this[key] ...
}
};
// Limited support from Babel, full support requires native implementation.
typeof key === "symbol"
})();
var c = new MyClass("hello")
c["key"] === undefined
有限的支持需要 Babel polyfill。由于语言限制,某些功能无法转译或 polyfill。有关详细信息,请参阅 core.js 的 注意事项部分。
¥Limited support requires the Babel polyfill. Due to language limitations, some features can't be transpiled or polyfilled. See core.js's caveats section for more details.
可子类化的内置函数
¥Subclassable Built-ins
在 ES2015 中,可以对 Array
、Date
和 DOM Element
等内置函数进行子类化。
¥In ES2015, built-ins like Array
, Date
and DOM Element
s can be subclassed.
// User code of Array subclass
class MyArray extends Array {
constructor(...args) { super(...args); }
}
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
内置子类性应根据具体情况进行评估,因为诸如 HTMLElement
等类可以被子类化,而诸如 Date
、Array
和 Error
等许多类不能是由于 ES5 引擎的限制。
¥Built-in subclassability should be evaluated on a case-by-case basis as classes such as HTMLElement
can be subclassed while many such as Date
, Array
and Error
cannot be due to ES5 engine limitations.
数学 + 数字 + 字符串 + 对象 API
¥Math + Number + String + Object APIs
许多新的库添加,包括核心数学库、数组转换助手和用于复制的 Object.assign。
¥Many new library additions, including core Math libraries, Array conversion helpers, and Object.assign for copying.
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("*")) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1,2,3].findIndex(x => x == 2) // 1
["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) })
Babel polyfill 支持这些 API 中的大部分。然而,由于各种原因省略了某些功能(例如 String.prototype.normalize
需要大量额外代码来支持)。你可以在 此处 找到更多的 polyfill。
¥Most of these APIs are supported by the Babel polyfill. However, certain features are omitted for various reasons (e.g. String.prototype.normalize
needs a lot of additional code to support). You can find more polyfills here.
二进制和八进制字面量
¥Binary and Octal Literals
为二进制 (b
) 和八进制 (o
) 添加了两种新的数字字面量形式。
¥Two new numeric literal forms are added for binary (b
) and octal (o
).
0b111110111 === 503 // true
0o767 === 503 // true
Babel 只能改造 0o767
,不能改造 Number("0o767")
。
¥Babel is only able to transform 0o767
and not Number("0o767")
.
Promise
Promises 是一个用于异步编程的库。Promise 是未来可能可用的值的第一类表示。许多现有的 JavaScript 库中都使用了 Promise。
¥Promises are a library for asynchronous programming. Promises are a first class representation of a value that may be made available in the future. Promises are used in many existing JavaScript libraries.
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)]);
})
反射 API
¥Reflect API
完整的反射 API 公开对象的运行时级元操作。这实际上是代理 API 的逆向,并允许调用对应于与代理捕获相同的元操作。对于实现代理特别有用。
¥Full reflection API exposing the runtime-level meta-operations on objects. This is effectively the inverse of the Proxy API, and allows making calls corresponding to the same meta-operations as the proxy traps. Especially useful for implementing proxies.
var O = {a: 1};
Object.defineProperty(O, 'b', {value: 2});
O[Symbol('c')] = 3;
Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]
function C(a, b){
this.c = a + b;
}
var instance = Reflect.construct(C, [20, 22]);
instance.c; // 42
尾调用
¥Tail Calls
保证尾部位置的调用不会无限制地增长堆栈。使递归算法在面对无限输入时安全。
¥Calls in tail-position are guaranteed to not grow the stack unboundedly. Makes recursive algorithms safe in the face of unbounded inputs.
function factorial(n, acc = 1) {
"use strict";
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
// Stack overflow in most implementations today,
// but safe on arbitrary inputs in ES2015
factorial(100000)
由于全局支持尾调用的复杂性和性能影响,仅支持显式自引用尾递归。由于其他错误已删除,并将重新实现。
¥Only explicit self referencing tail recursion was supported due to the complexity and performance impact of supporting tail calls globally. Removed due to other bugs and will be re-implemented.