ECMAScript 6 git.io/es6features
ECMAScript 6,又称 ECMAScript 2015,是 ECMA 标准的最新版。ES6 重新定义了这门语言,而上一版则要追溯到 09 年发布的 ES5。这里可以看到一份正在兼容此标准的 JavaScript 引擎详单 进行中。
查阅 ES6 standard 了解 ECMAScript 6 语言的完整规范。
ES6 包含以下特性:
- 箭头函数arrows
- 类classes
- 增强对象字面量enhanced object literals
- 模板字符串template strings
- 解构destructuring
- 参数默认值 + 可变形参 + 拓展实参default + rest + spread
- 新关键字let + const
- 迭代器 + 新遍历方式iterators + for..of
- 生成器generators
- 通用字符集支持unicode
- 模块支持modules
- 模块加载器module loaders
- 新数据结构map + set + weakmap + weakset
- 代理proxies
- 唯一符symbols
- <不懂>subclassable built-ins
- 异步处理promises
- 内置对象新增APImath + number + string + array + object APIs
- 2 & 8 进制字面量binary and octal literals
- 反射 APIreflect api
- 尾调用tail calls
箭头函数是旧式函数写法的语法糖,使用 =>
语法。
它的语法跟 C#、Java 8 和 CoffeeScript 的语法类似。
箭头函数的函数体可以是一个语句块,也可以是一个表达式,表达式的结果将作为函数的返回值。
与普通函数不同,箭头函数内部的 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._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
}
ES6 中的类是“基于原型的面向对象模式”的语法糖(注:并不是面向对象)。 以前用 ES5 模拟面向对象的形式太多了, 现在规范了更清晰的语法,提高代码的通用性和语法统一。 类支持原型继承,super 调用,创建实例,静态方法和构造函数。
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, materials) {
// 调用父类的构造函数
super(geometry, materials);
// 增加类的属性成员
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
// 增加类的方法成员
update(camera) {
// 调用父类方法
super.update();
}
// getter 函数
get boneCount() {
return this.bones.length;
}
// setter 函数
set matrixType(matrixType) {
this.idMatrix = SkinnedMesh[matrixType]();
}
// 静态函数
static defaultMatrix() {
return new THREE.Matrix4();
}
}
ES6 增强了对象字面量以便支持以下特性:
- 构造期间指定对象的原型
foo: foo
赋值的简便语法- 方法定义
- super 调用
- 计算属性
综上,对象字面量和类定义在形式上更加接近了, 对于 ECMAScript 这种基于对象的语言大有裨益。
var obj = {
// __proto__
__proto__: theProtoObj,
// Shorthand for ‘handler: handler’
handler,
// Methods
toString() {
// Super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
[ 'prop_' + (() => 42)() ]: 42
};
字符串模板为构造字符串提供语法糖, 这与 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);
解构允许通过模式匹配进行值绑定,支持数组和对象的解构。
解构具有良好的容错性,这一点跟检索对象的未定义属性时类似,会得到 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;
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 未被赋值或为 undefined,则取 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
提供块级作用域,let
是 var
的升级版。
const
定义常量,未赋值不可使用。
function f() {
{
let x;
{
// okay, 块级变量名
const x = "sneaky";
// error, const 值不可修改
x = "foo";
}
// error, 重复定义
let x = "inner";
}
}
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) {
// 1000 时截断序列
if (n > 1000)
break;
console.log(n);
}
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 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).
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);
}
The generator interface is (using TypeScript type syntax for exposition only):
interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}
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);
}
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";
alert("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
alert("2π = " + sum(pi, pi));
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.log(x);
}
// app.js
import ln, {pi, e} from "lib/mathplusplus";
alert("2π = " + ln(e)*pi*2);
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
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 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 =
{
get:...,
set:...,
has:...,
deleteProperty:...,
apply:...,
construct:...,
getOwnPropertyDescriptor:...,
defineProperty:...,
getPrototypeOf:...,
setPrototypeOf:...,
enumerate:...,
ownKeys:...,
preventExtensions:...,
isExtensible:...
}
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 description
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
.
var MyClass = (function() {
// module scoped symbol
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
In ES6, built-ins like Array
, Date
and DOM Element
s can be subclassed.
Object construction for a function named Ctor
now uses two-phases (both virtually dispatched):
- Call
Ctor[@@create]
to allocate the object, installing any special behavior - Invoke constructor on new instance to initialize
The known @@create
symbol is available via Symbol.create
. Built-ins now expose their @@create
explicitly.
// Pseudo-code of Array
class Array {
constructor(...args) { /* ... */ }
static [Symbol.create]() {
// Install special [[DefineOwnProperty]]
// to magically update 'length'
}
}
// User code of Array subclass
class MyArray extends Array {
constructor(...args) { super(...args); }
}
// Two-phase 'new':
// 1) Call @@create to allocate object
// 2) Invoke constructor on new instance
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
Many new library additions, including core Math libraries, Array conversion helpers, String 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].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) })
Two new numeric literal forms are added for binary (b
) and octal (o
).
0b111110111 === 503 // true
0o767 === 503 // true
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)]);
})
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.
// No sample yet
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 ES6
factorial(100000)