ECMAScript 6 与 ECMAScript 5 哪里不一样?

早过忘川 提交于 2020-10-28 13:45:10

内容译自 es6-features,在介绍 ES6 新特性的同时,列举了与 ES5 的语法差异,原文很多词语都属于专业术语,避免误导,保留原文相关内容。

常量(Constants)

const 声明会阻止对于变量绑定与变量自身值的修改,这意味着 const 声明并不会阻止对成员的修改。

ECMAScript 6:

const PI| = 3.141593;
PI > 3.0;

ECMAScript 5:

 //  only in ES5 through the help of object properties
 //  and only in global context and not in a block scope
 Object.|defineProperty|(typeof global === "object" ? global : window"|PI|", {
     value:        3.141593,
     enumerable:   true,
     |writable:     false|,
     |configurable: false|
 })
 PI > 3.0

作用域(Scoping)

块级变量(Block-Scoped Variables)

let 声明会将作用域限制在当前代码块中,let 声明和 const 声明不进行提升。

ECMAScript 6:

for (let i = 0; i < a.length; i++) {
  let x = a[i];
  ...
}
for (let i = 0; i < b.length; i++) {
  let y = b[i];
  ...
}

let callbacks = [];
for (|let| i = 0; i <= 2; i++) {
  callbacks[i] = function (return i * 2; };
}
callbacks[0]() === 0;
callbacks[1]() === 2;
callbacks[2]() === 4;

ECMAScript 5:

var i, x, y
for (i = 0; i < a.length; i++) {
    x = a[i]
    …
}
for (i = 0; i < b.length; i++) {
    y = b[i]
    …
}

var callbacks = []
for (var i = 0; i <= 2; i++) {
  (function (i{
      callbacks[i] = function(return i * 2 }
  })(i);
}
callbacks[0]() === 0
callbacks[1]() === 2
callbacks[2]() === 4

块级函数

块级作用域的函数定义。

ESMAScript 6:

{
    function foo (return 1; }
    foo() === 1;
    {
        function foo (return 2; }
        foo() === 2;
    }
    foo() === 1;
}

ESMAScript 5:

//  only in ES5 with the help of block-scope emulating
//  function scopes and function expressions
(function ({
    var foo = function (return 1 }
    foo() === 1
    (function ({
        var foo = function (return 2 }
        foo() === 2
    })();
    foo() === 1
})()

箭头函数(Arrow Functions)

表达式(Expression Bodies)

更具表现力的闭包语法。

ECMAScript 6 语法:

odds  = evens.map(v => v + 1);
pairs = evens.map(v => ({ even: v, odd: v + 1 }));
nums  = evens.map((v, i) => v + i);

ECMAScript 5 语法:

odds  = evens.map(function (vreturn v + 1 })
pairs = evens.map(function (vreturn { even: v, odd: v + 1 } })
nums  = evens.map(function (v, ireturn v + i })

变体

函数只有单个参数时,可以直接书写,不需要额外的语法。

ECMAScript 6 语法:

nums.forEach(v => {
   if (v % 5 === 0)
       fives.push(v);
})

ECMAScript 5 语法:

nums.forEach(function (v{
   if (v % 5 === 0)
       fives.push(v)
})

词法 this

更直观地处理当前对象上下文。

ECMAScript 6 语法:

this.nums.forEach((v) => {
    if (v % 5 === 0)
        this.fives.push(v);
});

ECMAScript 5 语法:

//  variant 1
var self = this
this.nums.forEach(function (v{
    if (v % 5 === 0)
        self.fives.push(v)
})

//  variant 2
this.nums.forEach(function (v{
    if (v % 5 === 0)
        this.fives.push(v)
}, this)

参数处理扩展

默认参数

简单直观的函数参数默认值。

ECMAScript 6 语法:

function f (x, y = 7, z = 42{
    return x + y + z;
}
f(1) === 50;

ECMAScript 5 语法:

function f (x, y, z{
    if (y === undefined)
        y = 7
    if (z === undefined)
        z = 42
    return x + y + z
}
f(1) === 50

剩余参数(Rest Parameter)

将剩余参数聚合为变长参数函数(variadic-functions)的单个参数。

ECMAScript 6 语法:

function f (x, y, ...a{
    return (x + y) * a.length;
}
f(12"hello"true7) === 9;

ECMAScript 5 语法:

function f (x, y{
    var a = Array.prototype.slice.call(arguments2)
    return (x + y) * a.length
}
f(12"hello"true7) === 9

展开运算符(Spread Operator)

将可迭代集合的元素(如数组、字符串)展开为字面意义的元素或函数的单个参数。

ECMAScript 6:

var params = [ "hello"true7 ];
var other = [ 12, ...params ]; // [ 1, 2, "hello", true, 7 ]

function f (x, y, ...a{
    return (x + y) * a.length;
}
f(12, ...params) === 9;

var str = "foo";
var chars = [ ...str ]; // [ "f", "o", "o" ]

ECMAScript 5:

var params = [ "hello"true7 ]
var other = [ 12 ].concat(params) // [ 1, 2, "hello", true, 7 ]

function f (x, y{
    var a = Array.prototype.slice.call(arguments2)
    return (x + y) * a.length
}
f.apply(undefined, [ 12 ].concat(params)) === 9

var str = "foo"
var chars = str.split(""// [ "f", "o", "o" ]

模版字符串(Template Literals)

字符串插值

单行和多行字符串的插值表达式。

ECMAScript 6:

var customer = { name"Foo" };
var card = { amount7product"Bar"unitprice42 };
var message = `Hello ${customer.name},
want to buy ${card.amount} ${card.product} for
a total of ${card.amount * card.unitprice} bucks?`
;

ECMAScript 5:

var customer = { name"Foo" }
var card = { amount7product"Bar"unitprice42 }
var message = "Hello " + customer.name + ",\n" +
"want to buy " + card.amount + " " + card.product + " for\n" +
"a total of " + (card.amount * card.unitprice) + " bucks?"

自定义插值(Custom Interpolation)

任意方法的灵活插值表达式。

ECMAScript 6 :

get`http://example.com/foo?bar=${bar + baz}&quux=${quux}`;

ECMAScript 5 :

get([ "http://example.com/foo?bar=", "&quux=", "" ],bar + baz, quux)

访问原始字符串(Raw String Access)

访问原始模板字符串内容(不解析反斜杠)。

ECMAScript 6:

function quux (strings, ...values{
    strings[0] === "foo\n";
    strings[1] === "bar";
    strings.raw[0] === "foo\\n";
    strings.raw[1] === "bar";
    values[0] === 42;
}
quux`foo\n${ 42 }bar`

String.raw`foo\n${ 42 }bar` === "foo\\n42bar";

ECMAScript 5:

//  no equivalent in ES5

字面量的扩展(Extended Literals)

二进制和八进制

直接支持安全的二进制和八进制。

ECMAScript 6:

0b111110111 === 503;
0o767 === 503;

ECMAScript 5:

parseInt("111110111"2) === 503
parseInt("767"8) === 503
0767 === 503 // only in non-strict, backward compatibility mode

unicode 字符和正则

支持在字符串和正则表达式中使用 unicode

ECMAScript 6:

"𠮷".length === 2;
"𠮷".match(/./u)[0].length === 2;
"𠮷" === "\uD842\uDFB7";
"𠮷" === "\u{20BB7}";
"𠮷".codePointAt(0) == 0x20BB7;
for (let codepoint of "𠮷"console.log(codepoint);

ECMAScript 5:

"𠮷".length === 2
"𠮷".match(/(?:[\0-\t\x0B\f\x0E-\u2027\u202A-\uD7FF\uE000-\uFFFF][\uD800-\uDBFF][\uDC00-\uDFFF][\uD800-\uDBFF](?![\uDC00-\uDFFF])(?:[^\uD800-\uDBFF]^)[\uDC00-\uDFFF])/)[0].length === 2
"𠮷" === "\uD842\uDFB7"
//  no equivalent in ES5
//  no equivalent in ES5
//  no equivalent in ES5

增强的正则表达式(Enhanced Regular Expression)

正则表达式粘性匹配

Keep the matching position sticky between matches and this way support efficient parsing of arbitrary long input strings, even with an arbitrary number of distinct regular expressions.

ECMAScript 6:

let parser = (input, match) => {
    for (let pos = 0, lastPos = input.length; pos < lastPos; ) {
        for (let i = 0; i < match.length; i++) {
            match[i].pattern.lastIndex = pos;
            let found;
            if ((found = match[i].pattern.exec(input)) !== null) {
                match[i].action(found);
                pos = match[i].pattern.lastIndex;
                break;
            }
        }
    }
}

let report = (match) => {
    console.log(JSON.stringify(match));
};
parser("Foo 1 Bar 7 Baz 42", [
    { pattern/Foo\s+(\d+)/yaction(match) => report(match) },
    { pattern/Bar\s+(\d+)/yaction(match) => report(match) },
    { pattern/Baz\s+(\d+)/yaction(match) => report(match) },
    { pattern/\s*/y,         action(match) => {}            }
]);

ECMAScript 5:

var parser = function (input, match{
    for (var i, found, inputTmp = input; inputTmp !== ""; ) {
        for (i = 0; i < match.length; i++) {
            if ((found = match[i].pattern.exec(inputTmp)) !== null) {
                match[i].action(found)
                inputTmp = inputTmp.substr(found[0].length)
                break
            }
        }
    }
}

var report = function (match{
    console.log(JSON.stringify(match))
}
parser("Foo 1 Bar 7 Baz 42", [
    { pattern/^Foo\s+(\d+)/actionfunction (match{ report(match) } },
    { pattern/^Bar\s+(\d+)/actionfunction (match{ report(match) } },
    { pattern/^Baz\s+(\d+)/actionfunction (match{ report(match) } },
    { pattern/^\s*/,         actionfunction (match{}                 }
])

增强的对象(Enhanced Object Properties)

属性速记

对象属性定义的简写语法。

ECMAScript 6:

var x = 0, y = 0;
obj = { x, y };

ECMAScript 5:

var x = 0, y = 0
obj = { x: x, y: y }

计算属性名

支持对象属性定义中的计算名称。

ECMAScript 6:

let obj = {
    foo"bar",
    [ "baz" + quux() ]: 42
};

ECMAScript 5:

var obj = {
    foo"bar"
}
obj[ "baz" + quux() ] = 42

方法简写

省略 : 与 function 关键字。

ECMAScript 6:

obj = {
    foo (a, b) {
        …
    },
    bar (x, y) {
        …
    },
    *quux (x, y) {
        …
    }
};

ECMAScript 5:

obj = {
    foofunction (a, b{
        …
    },
    barfunction (x, y{
        …
    },
    //  quux: no equivalent in ES5
    …
}

更方便的数据访问

数组解构(Array Matching)

在赋值过程中,直观灵活地将数组分解为单个变量。

ECMAScript 6:

var list = [ 123 ];
var [ a, , b ] = list;
[ b, a ] = [ a, b ];

ECMAScript 5:

var list = [ 123 ]
var a = list[0], b = list[2]
var tmp = a; a = b; b = tmp

对象解构

在赋值过程中直观灵活地将对象分解为单个变量。

ECMAScript 6:

var { op, lhs, rhs } = getASTNode();

ECMAScript 5:

var tmp = getASTNode()
var op  = tmp.op
var lhs = tmp.lhs
var rhs = tmp.rhs

嵌套对象解构

在赋值过程中直观灵活地将对象分解为单个变量。

ECMAScript 6:

var { op: a, lhs: { op: b }, rhs: c } = getASTNode();

ECMAScript 5:

var tmp = getASTNode()
var a = tmp.op
var b = tmp.lhs.op
var c = tmp.rhs

对象/数组解构默认值

简单直观的对象和数组解构默认值。

ECMAScript 6:

var obj = { a1 };
var list = [ 1 ];
var { a, b = 2 } = obj;
var [ x, y = 2 ] = list;

ECMAScript 5:

var obj = { a1 }
var list = [ 1 ]
var a = obj.a
var b = obj.b === undefined ? 2 : obj.b
var x = list[0]
var y = list[1] === undefined ? 2 : list[1]

参数解构(Parameter Context Matching)

在函数调用期间,将数组和对象直观而灵活地分解为单个参数。

ECMAScript 6:

function f ([ name, val ]{
    console.log(name, val);
}
function g ({ name: n, val: v }{
    console.log(n, v);
}
function h ({ name, val }{
    console.log(name, val);
}
f([ "bar"42 ]);
g({ name"foo"val:  7 });
h({ name"bar"val42 });

ECMAScript 5:

function f (arg{
    var name = arg[0]
    var val  = arg[1]
    console.log(name, val)
}
function g (arg{
    var n = arg.name
    var v = arg.val
    console.log(n, v)
}
function h (arg{
    var name = arg.name
    var val  = arg.val
    console.log(name, val)
}
f([ "bar"42 ])
g({ name"foo"val:  7 })
h({ name"bar"val42 })

Fail-Soft 解构

Fail-soft 解构,可选择使用默认值。

ECMAScript 6:

var list = [ 742 ];
var [ a = 1, b = 2, c = 3, d ] = list;
a === 7;
b === 42;
c === 3;
d === undefined;

ECMAScript 5:

var list = [ 742 ]
var a = typeof list[0] !== "undefined" ? list[0] : 1
var b = typeof list[1] !== "undefined" ? list[1] : 2
var c = typeof list[2] !== "undefined" ? list[2] : 3
var d = typeof list[3] !== "undefined" ? list[3] : undefined
a === 7
b === 42
c === 3
d === undefined

模块(Modules)

值的导入导出

支持从模块导入导出 值,不会污染全局命名空间。

ECMAScript 6:

//  lib/math.js
export function sum (x, yreturn x + y };
export var pi = 3.141593;

//  someApp.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));

ECMAScript 5:

//  lib/math.js
LibMath = {}
LibMath.sum = function (x, yreturn x + y }
LibMath.pi = 3.141593

//  someApp.js
var math = LibMath
console.log("2π = " + math.sum(math.pi, math.pi))

//  otherApp.js
var sum = LibMath.sum, pi = LibMath.pi
console.log("2π = " + sum(pi, pi))

default & 通配符 *

默认导出和全部导出。

ECMAScript 6:

//  lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default (x) => Math.exp(x);

//  someApp.js
import exp, { pi, e } from "lib/mathplusplus";
console.log("e^{π} = " + exp(pi));

ECMAScript 5:

//  lib/mathplusplus.js
LibMathPP = {}
for (symbol in LibMath)
    if (LibMath.hasOwnProperty(symbol))
        LibMathPP[symbol] = LibMath[symbol]
LibMathPP.e = 2.71828182846
LibMathPP.exp = function (xreturn Math.exp(x) }

//  someApp.js
var exp = LibMathPP.exp, pi = LibMathPP.pi, e = LibMathPP.e
console.log("e^{π} = " + exp(pi))

类 (Classes)

类的声明(Class Definition)

类声明以 class 关键字开始,其后是类的名称,剩余部分的语法看起来就像对象字面量中方法的简写,并且在方法直接不需要使用逗号。

ECMAScript 6:

class Shape {
    constructor (id, x, y) {
        this.id = id;
        this.move(x, y);
    }
    move (x, y) {
        this.x = x;
        this.y = y;
    }
}

ECMAScript 5:

var Shape = function (id, x, y{
    this.id = id
    this.move(x, y)
}
Shape.prototype.move = function (x, y{
    this.x = x
    this.y = y
}

类的继承

使用 extends 关键字来指定当前类所需继承的类。

ECMAScript 6:

class Rectangle extends Shape {
    constructor (id, x, y, width, height) {
        super(id, x, y);
        this.width  = width;
        this.height = height;
    }
}
class Circle extends Shape {
    constructor (id, x, y, radius) {
        super(id, x, y);
        this.radius = radius;
    }
}

ECMAScript 5:

var Rectangle = function (id, x, y, width, height{
    Shape.call(this, id, x, y)
    this.width  = width
    this.height = height
}
Rectangle.prototype = Object.create(Shape.prototype)
Rectangle.prototype.constructor = Rectangle
var Circle = function (id, x, y, radius{
    Shape.call(this, id, x, y)
    this.radius = radius
}
Circle.prototype = Object.create(Shape.prototype)
Circle.prototype.constructor = Circle

从表达式中继承

ECMAScript 6 中派生类的最强大能力,或许就是能够从表达式中派生类。只要一个表达式能够返回一个具有 [[Construct]] 属性以及原型的函数,你就可以对其使用 extends

ECMAScript 6:

var aggregation = (baseClass, ...mixins) => {
    let base = class _Combined extends baseClass {
        constructor (...args) {
            super(...args);
            mixins.forEach((mixin) => {
                mixin.prototype.initializer.call(this);
            });
        }
    };
    let copyProps = (target, source) => {
        Object.getOwnPropertyNames(source)
            .concat(Object.getOwnPropertySymbols(source))
            .forEach((prop) => {
            if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/))
                return
            Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
        })
    }
    mixins.forEach((mixin) => {
        copyProps(base.prototype, mixin.prototype);
        copyProps(base, mixin);
    });
    return base;
};

class Colored {
    initializer ()     { this._color = "white"; }
    get color ()       { return this._color; }
    set color (v)      { this._color = v; }
}

class ZCoord {
    initializer ()     { this._z = 0; }
    get z ()           { return this._z; }
    set z (v)          { this._z = v; }
}

class Shape {
    constructor (x, y) { this._x = x; this._y = y; }
    get x ()           { return this._x; }
    set x (v)          { this._x = v; }
    get y ()           { return this._y; }
    set y (v)          { this._y = v; }
}

class Rectangle extends aggregation(ShapeColoredZCoord{}

var rect = new Rectangle(742);
rect.z     = 1000;
rect.color = "red";
console.log(rect.x, rect.y, rect.z, rect.color);

ECMAScript 5:

var aggregation = function (baseClass, mixins{
    var base = function ({
        baseClass.apply(thisarguments)
        mixins.forEach(function (mixin{
            mixin.prototype.initializer.call(this)
        }.bind(this))
    }
    base.prototype = Object.create(baseClass.prototype)
    base.prototype.constructor = base
    var copyProps = function (target, source{
        Object.getOwnPropertyNames(source).forEach(function (prop{
            if (prop.match(/^(?:constructor|prototype|arguments|caller|name|bind|call|apply|toString|length)$/))
                return
            Object.defineProperty(target, prop, Object.getOwnPropertyDescriptor(source, prop))
        })
    }
    mixins.forEach(function (mixin{
        copyProps(base.prototype, mixin.prototype)
        copyProps(base, mixin)
    })
    return base
}

var Colored = function ({}
Colored.prototype = {
    initializerfunction ()  this._color = "white" },
    getColor:    function ()  return this._color },
    setColor:    function (vthis._color = v }
}

var ZCoord = function ({}
ZCoord.prototype = {
    initializerfunction ()  this._z = 0 },
    getZ:        function ()  return this._z },
    setZ:        function (vthis._z = v }
}

var Shape = function (x, y{
    this._x = x; this._y = y
}
Shape.prototype = {
    getXfunction ()  return this._x },
    setXfunction (vthis._x = v },
    getYfunction ()  return this._y },
    setYfunction (vthis._y = v }
}

var _Combined = aggregation(Shape, [ Colored, ZCoord ])
var Rectangle = function (x, y{
    _Combined.call(this, x, y)
}
Rectangle.prototype = Object.create(_Combined.prototype)
Rectangle.prototype.constructor = Rectangle

var rect = new Rectangle(742)
rect.setZ(1000)
rect.setColor("red")
console.log(rect.getX(), rect.getY(), rect.getZ(), rect.getColor())

基类的访问

使用 super 关键字对基类构造函数和方法的直观访问。

ECMAScript 6:

class Shape {
    …
    toString () {
        return `Shape(${this.id})`
    }
}
class Rectangle extends Shape {
    constructor (id, x, y, width, height) {
        super(id, x, y);
        …
    }
    toString () {
        return "Rectangle > " + super.toString();
    }
}
class Circle extends Shape {
    constructor (id, x, y, radius) {
        super(id, x, y);
        …
    }
    toString () {
        return "Circle > " + super.toString();
    }
}

ECMAScript 5:

var Shape = function (id, x, y{
    …
}
Shape.prototype.toString = function (x, y{
    return "Shape(" + this.id + ")"
}
var Rectangle = function (id, x, y, width, height{
    Shape.call(this, id, x, y)
    …
}
Rectangle.prototype.toString = function ({
    return "Rectangle > " + Shape.prototype.toString.call(this)
}
var Circle = function (id, x, y, radius{
    Shape.call(this, id, x, y)
    …
}
Circle.prototype.toString = function ({
    return "Circle > " + Shape.prototype.toString.call(this)
}

静态成员

支持使用 static 关键字定义静态成员。

ECMAScript 6:

class Rectangle extends Shape {
    …
    static defaultRectangle () {
        return new Rectangle("default"00100100);
    }
}
class Circle extends Shape {
    …
    static defaultCircle () {
        return new Circle("default"00100);
    }
}
var defRectangle = Rectangle.defaultRectangle();
var defCircle    = Circle.defaultCircle();

ECMAScript 5:

var Rectangle = function (id, x, y, width, height{
    …
}
Rectangle.defaultRectangle = function ({
    return new Rectangle("default"00100100)
}
var Circle = function (id, x, y, width, height{
    …
}
Circle.defaultCircle = function ({
    return new Circle("default"00100)
}
var defRectangle = Rectangle.defaultRectangle()
var defCircle    = Circle.defaultCircle()

访问器属性 getter/setter

自有属性需要在类的构造器中创建,而类还允许你在原型上定义访问器属性。为了创建一个 getter 要使用 get 关键字,创建 setter 使用 set 关键字。

ECMAScript 6:

class Rectangle {
    constructor (width, height) {
        this._width  = width;
        this._height = height;
    }
    set width  (width)  { this._width = width;               }
    get width  ()       { return this._width;                }
    set height (height) { this._height = height;             }
    get height ()       { return this._height;               }
    get area   ()       { return this._width * this._height; }
};
var r = new Rectangle(5020);
r.area === 1000;

ECMAScript 5:

var Rectangle = function (width, height{
    this._width  = width
    this._height = height
}
Rectangle.prototype = {
    set width  (width)  { this._width = width               },
    get width  ()       { return this._width                },
    set height (height) { this._height = height             },
    get height ()       { return this._height               },
    get area   ()       { return this._width * this._height }
}
var r = new Rectangle(5020)
r.area === 1000

符号(Symbol)

符号的类型

用作对象属性标识符的唯一且不可变的数据类型。符号可以有可选的描述,但仅用于调试目的。

ECMAScript 6:

Symbol("foo") !== Symbol("foo");
const foo = Symbol();
const bar = Symbol();
typeof foo === "symbol";
typeof bar === "symbol";
let obj = {};
obj[foo] = "foo";
obj[bar] = "bar";
JSON.stringify(obj); // {}
Object.keys(obj); // []
Object.getOwnPropertyNames(obj); // []
Object.getOwnPropertySymbols(obj); // [ foo, bar ]

ECMAScript 5:

// no equivalent in ES5

全局符号

全局符号,通过唯一 key 索引。

ECMAScript 6:

Symbol.for("app.foo") === Symbol.for("app.foo")
const foo = Symbol.for("app.foo");
const bar = Symbol.for("app.bar");
Symbol.keyFor(foo) === "app.foo";
Symbol.keyFor(bar) === "app.bar";
typeof foo === "symbol";
typeof bar === "symbol";
let obj = {};
obj[foo] = "foo";
obj[bar] = "bar";
JSON.stringify(obj); // {}
Object.keys(obj); // []
Object.getOwnPropertyNames(obj); // []
Object.getOwnPropertySymbols(obj); // [ foo, bar ]

ECMAScript 5:

// no equivalent in ES5

迭代器(Iterators)

迭代器和 for-of

支持 iterable 协议,允许对象自定义迭代行为。另外,支持 iterable 协议来生成 值序列(有限或无限)。最后,提供方便的 of 运算符迭代可迭代对象的所有值。

ECMAScript 6:

let fibonacci = {
    [Symbol.iterator]() {
        let pre = 0, cur = 1;
        return {
           next () {
               [ pre, cur ] = [ cur, pre + cur ];
               return { donefalsevalue: cur };
           }
        };
    }
}

for (let n of fibonacci) {
    if (n > 1000)
        break;
    console.log(n);
}

ECMAScript 5:

var fibonacci = {
    next: (function ({
        var pre = 0, cur = 1
        return function ({
            tmp = pre
            pre = cur
            cur += tmp
            return cur
        }
    })()
}

var n
for (;;) {
    n = fibonacci.next()
    if (n > 1000)
        break
    console.log(n)
}

生成器(Generators)

生成器函数 - 迭代器协议(Iterator Protocol)

生成器是能返回一个迭代器的函数,其中可以暂停和恢复控制流,以便生成值序列(有限或无限)。

ECMAScript 6:

let fibonacci = {
    *[Symbol.iterator]() {
        let pre = 0, cur = 1;
        for (;;) {
            [ pre, cur ] = [ cur, pre + cur ];
            yield cur;
        }
    }
}

for (let n of fibonacci) {
    if (n > 1000)
        break;
    console.log(n);
}

ECMAScript 5:

var fibonacci = {
    next: (function ({
        var pre = 0, cur = 1
        return function ({
            tmp = pre
            pre = cur
            cur += tmp
            return cur
        }
    })()
}

var n
for (;;) {
    n = fibonacci.next()
    if (n > 1000)
        break
    console.log(n)
}

生成器函数 - 直接调用(Direct Use)

对生成器函数的支持,是一种特殊的函数变体,可以暂停和恢复控制流,以生成值序列(有限或无限)。

ECMAScript 6:

functionrange (start, end, step{
    while (start < end) {
        yield start;
        start += step;
    }
}

for (let i of range(0102)) {
    console.log(i); // 0, 2, 4, 6, 8
}

ECMAScript 5:

function range (start, end, step{
    var list = []
    while (start < end) {
        list.push(start)
        start += step
    }
    return list
}

var r = range(0102)
for (var i = 0; i < r.length; i++) {
    console.log(r[i]) // 0, 2, 4, 6, 8
}

生成器 - 匹配(Matching)

支持生成器函数,即可以暂停和恢复控制流的函数,以便生成和扩展值序列(有限或无限)。

ECMAScript 6:

let fibonacci = function* (numbers{
    let pre = 0, cur = 1;
    while (numbers-- > 0) {
        [ pre, cur ] = [ cur, pre + cur ];
        yield cur;
    }
};

for (let n of fibonacci(1000))
    console.log(n);

let numbers = [ ...fibonacci(1000) ];

let [ n1, n2, n3, ...others ] = fibonacci(1000);

ECMAScript 5:

//  no equivalent in ES5

生成器 - 控制流(Control-Flow)

可以暂停和恢复流,以协程(co-routines)的方式结合 Promise 解决异步编程。

ECMAScript 6:

//  generic asynchronous control-flow driver
function async (proc, ...params{
    let iterator = proc(...params);
    return new Promise((resolve, reject) => {
        let loop = (value) => {
            let result;
            try {
                result = iterator.next(value);
            }
            catch (err) {
                reject(err);
            }
            if (result.done)
                resolve(result.value);
            else if (   typeof result.value      === "object"
                     && typeof result.value.then === "function")
                result.value.then((value) => {
                    loop(value);
                }, (err) => {
                    reject(err);
                });
            else
                loop(result.value);
        }
        loop();
    });
}

//  application-specific asynchronous builder
function makeAsync (text, after{
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(text), after);
    });
}

//  application-specific asynchronous procedure
async(function* (greeting{
    let foo = yield makeAsync("foo"300)
    let bar = yield makeAsync("bar"200)
    let baz = yield makeAsync("baz"100)
    return `${greeting} ${foo} ${bar} ${baz}`
}, "Hello").then((msg) => {
    console.log("RESULT:", msg); // "Hello foo bar baz"
})

ECMAScript 5:

//  no equivalent in ES5

生成器 - 方法

由于生成器就是函数,因此也可以被添加到类和对象中。

ECMAScript 6:

class Clz {
    * bar () {
        …
    }
};
let Obj = {
    * foo () {
        …
    }
};

ECMAScript 5:

//  no equivalent in ES5

Map/Set & WeakMap/WeakSet

Set 数据结构

基于集合的常用算法的清洁数据结构。

ECMAScript 6:

let s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;
for (let key of s.values()) // insertion order
    console.log(key);

ECMAScript 5:

var s = {}
s["hello"] = true; s["goodbye"] = true; s["hello"] = true
Object.keys(s).length === 2
s["hello"] === true
for (var key in s) // arbitrary order
    if (s.hasOwnProperty(key))
        console.log(s[key])

Map 数据结构

基于映射的常用算法的更清晰的数据结构。

ECMAScript 6:

let m = new Map();
let s = Symbol();
m.set("hello"42);
m.set(s, 34);
m.get(s) === 34;
m.size === 2;
for (let [ key, val ] of m.entries())
    console.log(key + " = " + val);

ECMAScript 5:

var m = {}
// no equivalent in ES5
m["hello"] = 42
// no equivalent in ES5
// no equivalent in ES5
Object.keys(m).length === 2
for (key in m) {
    if (m.hasOwnProperty(key)) {
        var val = m[key]
        console.log(key + " = " + val)
    }
}

weakSet/weakMap 数据结构

无内存泄漏,Object-key’d 并排数据结构。

ECMAScript 6:

let isMarked     = new WeakSet();
let attachedData = new WeakMap();

export class Node {
    constructor (id)   { this.id = id;                  }
    mark        ()     { isMarked.add(this);            }
    unmark      ()     { isMarked.delete(this);         }
    marked      ()     { return isMarked.has(this);     }
    set data    (data) { attachedData.set(this, data);  }
    get data    ()     { return attachedData.get(this); }
}

let foo = new Node("foo");

JSON.stringify(foo) === '{"id":"foo"}';
foo.mark();
foo.data = "bar";
foo.data === "bar";
JSON.stringify(foo) === '{"id":"foo"}';

isMarked.has(foo)     === true
attachedData.has(foo) === true
foo = null  /* remove only reference to foo */
attachedData.has(foo) === false
isMarked.has(foo)     === false

ECMAScript 5:

// no equivalent in ES5

类型化数组(Typed Arrays)

类型化数组

支持基于任意字节的数据结构,实现网络协议、加密算法、文件格式操作等。

ECMAScript 6:

//  ES6 class equivalent to the following C structure:
//  struct Example { unsigned long id; char username[16]; float amountDue; };
class Example {
    constructor (buffer = new ArrayBuffer(24)) {
        this.buffer = buffer;
    }
    set buffer (buffer) {
        this._buffer    = buffer;
        this._id        = new Uint32Array (this._buffer,  0,  1);
        this._username  = new Uint8Array  (this._buffer,  416);
        this._amountDue = new Float32Array(this._buffer, 20,  1);
    }
    get buffer ()     { return this._buffer;       }
    set id (v)        { this._id[0] = v;           }
    get id ()         { return this._id[0];        }
    set username (v)  { this._username[0] = v;     }
    get username ()   { return this._username[0];  }
    set amountDue (v) { this._amountDue[0] = v;    }
    get amountDue ()  { return this._amountDue[0]; }
}

let example = new Example()
example.id = 7
example.username = "John Doe"
example.amountDue = 42.0

ECMAScript 5:

//  no equivalent in ES5
//  (only an equivalent in HTML5)

新的内置方法(New Built-In Methods)

对象属性分配

Object.assign() 分配一个或多个对象的可枚举属性到目标对象上。

ECMAScript 6:

var dest = { quux0 };
var src1 = { foo1bar2 };
var src2 = { foo3baz4 };
Object.assign(dest, src1, src2);
dest.quux === 0;
dest.foo  === 3;
dest.bar  === 2;
dest.baz  === 4;

ECMAScript 5:

var dest = { quux0 }
var src1 = { foo1bar2 }
var src2 = { foo3baz4 }
Object.keys(src1).forEach(function(k{
    dest[k] = src1[k]
})
Object.keys(src2).forEach(function(k{
    dest[k] = src2[k]
})
dest.quux === 0
dest.foo  === 3
dest.bar  === 2
dest.baz  === 4

数组元素查找

查找数组元素的新方法 Array.find() 、Array.findIndex()

ECMAScript 6:

1342 ].find(x => x > 3); // 4
1342 ].findIndex(x => x > 3); // 2

ECMAScript 5:

1342 ].filter(function (xreturn x > 3 })[0// 4
// no equivalent in ES5

字符串复制

String.repeat() 接受一个参数作为字符串的重复次数,返回一个将初始字符串重复指定次数的新字符串。

ECMAScript 6:

" ".repeat(4 * depth);
"foo".repeat(3);

ECMAScript 5:

Array((4 * depth) + 1).join(" ")
Array(3 + 1).join("foo")

字符串检测

  • String.startsWith() 检测字符串是否以指定的前缀开始。
  • String.endsWith() 检测字符串是否以指定的前缀结束。
  • String.includes() 检测字符串收费包含指定字符串。

ECMAScript 6:

"hello".startsWith("ello"1); // true
"hello".endsWith("hell"4);   // true
"hello".includes("ell");       // true
"hello".includes("ell"1);    // true
"hello".includes("ell"2);    // false

ECMAScript 5:

"hello".indexOf("ello") === 1    // true
"hello".indexOf("hell") === (4 - "hell".length) // true
"hello".indexOf("ell") !== -1    // true
"hello".indexOf("ell"1) !== -1 // true
"hello".indexOf("ell"2) !== -1 // false

数字类型检测

  • Number.isNaN() 方法确定传递的值是否为 NaN,并且检查其类型是否为 Number。它是原来的全局 isNaN() 的更稳妥的版本。

  • Number.isFinite() 方法用来检测传入的参数是否是一个有穷数(finite number)。

ECMAScript 6:

Number.isNaN(42) === false;
Number.isNaN(NaN) === true;

Number.isFinite(Infinity) === false;
Number.isFinite(-Infinity) === false;
Number.isFinite(NaN) === false;
Number.isFinite(123) === true;

ECMAScript 5:

var isNaN = function (n{
    return n !== n
}
var isFinite = function (v{
    return (typeof v === "number" && !isNaN(v) && v !== Infinity && v !== -Infinity)
}
isNaN(42) === false
isNaN(NaN) === true

isFinite(Infinity) === false
isFinite(-Infinity) === false
isFinite(NaN) === false
isFinite(123) === true

数字安全性检测

Number.isSafeInteger() 检查数字是否在安全范围内(-(253 - 1) 到 253 - 1 之间的整数,包含 -(253 - 1) 和 253 - 1)。

ECMAScript 6:

Number.isSafeInteger(42) === true;
Number.isSafeInteger(9007199254740992) === false;

ECMAScript 5:

function isSafeInteger (n{
    return (
           typeof n === 'number'
        && Math.round(n) === n
        && -(Math.pow(253) - 1) <= n
        && n <= (Math.pow(253) - 1)
    )
}
isSafeInteger(42) === true
isSafeInteger(9007199254740992) === false

数字比较

标准 Epsilon 值的可用性,用于更精确地比较浮点数。

ECMAScript 6:

console.log(0.1 + 0.2 === 0.3); // false
console.log(Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON); // true

ECMAScript 5:

console.log(0.1 + 0.2 === 0.3// false
console.log(Math.abs((0.1 + 0.2) - 0.3) < 2.220446049250313e-16// true

数字截断

将浮点数截断为其整数部分,完全去掉小数部分。

ECMAScript 6:

console.log(Math.trunc(42.7)) // 42
console.log(Math.trunc( 0.1)) // 0
console.log(Math.trunc(-0.1)) // -0

ECMAScript 5:

function mathTrunc (x{
    return (x < 0 ? Math.ceil(x) : Math.floor(x))
}
console.log(mathTrunc(42.7)) // 42
console.log(mathTrunc( 0.1)) // 0
console.log(mathTrunc(-0.1)) // -0

数字符号的确认

Math.sign() 函数返回一个数字的符号, 指示数字是正数、负数还是 0。 ECMAScript 6:

console.log(Math.sign(7))   // 1
console.log(Math.sign(0))   // 0
console.log(Math.sign(-0))  // -0
console.log(Math.sign(-7))  // -1
console.log(Math.sign(NaN)) // NaN

ECMAScript 5:

function mathSign (x{
    return ((x === 0 || isNaN(x)) ? x : (x > 0 ? 1 : -1))
}
console.log(mathSign(7))   // 1
console.log(mathSign(0))   // 0
console.log(mathSign(-0))  // -0
console.log(mathSign(-7))  // -1
console.log(mathSign(NaN)) // NaN

Promise

Promise 用法

一个值的 First-Class (头等函数)表示,该值可以异步生成并在将来可用。

ECMAScript 6:

function msgAfterTimeout (msg, who, timeout{
    return new Promise((resolve, reject) => {
        setTimeout(() => resolve(`${msg} Hello ${who}!`), timeout);
    });
}
msgAfterTimeout("""Foo"100).then((msg) =>
    msgAfterTimeout(msg, "Bar"200)
).then((msg) => {
    console.log(`done after 300ms:${msg}`);
});

ECMAScript 5:

function msgAfterTimeout (msg, who, timeout, onDone{
    setTimeout(function ({
        onDone(msg + " Hello " + who + "!")
    }, timeout)
}
msgAfterTimeout("""Foo"100function (msg{
    msgAfterTimeout(msg, "Bar"200function (msg{
        console.log("done after 300ms:" + msg)
    })
})

承诺组合 - Promise.all()

将一个或多个 Promise 组合成新的承诺,而不必亲自处理底层异步操作的排序。

ECMAScript 6:

function fetchAsync (url, timeout, onData, onError{
    …
}
let fetchPromised = (url, timeout) => {
    return new Promise((resolve, reject) => {
        fetchAsync(url, timeout, resolve, reject);
    });
}
Promise.all([
    fetchPromised("http://backend/foo.txt"500),
    fetchPromised("http://backend/bar.txt"500),
    fetchPromised("http://backend/baz.txt"500)
]).then((data) => {
    let [ foo, bar, baz ] = data;
    console.log(`success: foo=${foo} bar=${bar} baz=${baz}`);
}, (err) => {
    console.log(`error: ${err}`);
});

ECMAScript 5:

function fetchAsync (url, timeout, onData, onError{
    …
}
function fetchAll (request, onData, onError{
    var result = [], results = 0
    for (var i = 0; i < request.length; i++) {
        result[i] = null
        (function (i{
            fetchAsync(request[i].url, request[i].timeout, function (data{
                result[i] = data
                if (++results === request.length)
                    onData(result)
            }, onError)
        })(i)
    }
}
fetchAll([
    { url"http://backend/foo.txt"timeout500 },
    { url"http://backend/bar.txt"timeout500 },
    { url"http://backend/baz.txt"timeout500 }
], function (data{
    var foo = data[0], bar = data[1], baz = data[2]
    console.log("success: foo=" + foo + " bar=" + bar + " baz=" + baz)
}, function (err{
    console.log("error: " + err)
})

元编程

代理 - Proxy

挂接到运行时级对象的元操作。

ECMAScript 6:

let target = {
    foo"Welcome, foo"
};
let proxy = new Proxy(target, {
    get (receiver, name) {
        return name in receiver ? receiver[name] : `Hello, ${name}`;
    }
});
proxy.foo   === "Welcome, foo";
proxy.world === "Hello, world";

ECMAScript 5:

// no equivalent in ES5

反射 - Reflect

根据对象元操作进行调用。

ECMAScript 6:

let obj = { a1 };
Object.defineProperty(obj, "b", { value2 });
obj[Symbol("c")] = 3;
Reflect.ownKeys(obj); // [ "a", "b", Symbol(c) ]

ECMAScript 5:

var obj = { a1 }
Object.defineProperty(obj, "b", { value2 })
// no equivalent in ES5
Object.getOwnPropertyNames(obj) // [ "a", "b" ]

国际化与本地化

Collation

Sorting a set of strings and searching within a set of strings. Collation is parameterized by locale and aware of Unicode.

ECMAScript 6:

// in German,  "ä" sorts with "a"
// in Swedish, "ä" sorts after "z"
var list = [ "ä""a""z" ];
var l10nDE = new Intl.Collator("de");
var l10nSV = new Intl.Collator("sv");
l10nDE.compare("ä""z") === -1;
l10nSV.compare("ä""z") === +1;
console.log(list.sort(l10nDE.compare)); // [ "a", "ä", "z" ]
console.log(list.sort(l10nSV.compare)); // [ "a", "z", "ä" ]

ECMAScript 5:

// no equivalent in ES5

Number Formatting

Format numbers with digit grouping and localized separators.

ECMAScript 6:

var l10nEN = new Intl.NumberFormat("en-US");
var l10nDE = new Intl.NumberFormat("de-DE");
l10nEN.format(1234567.89) === "1,234,567.89";
l10nDE.format(1234567.89) === "1.234.567,89";

ECMAScript 5:

// no equivalent in ES5

Currency Formatting

Format numbers with digit grouping, localized separators and attached currency symbol.

ECMAScript 6:

var l10nUSD = new Intl.NumberFormat("en-US", { style"currency"currency"USD" });
var l10nGBP = new Intl.NumberFormat("en-GB", { style"currency"currency"GBP" });
var l10nEUR = new Intl.NumberFormat("de-DE", { style"currency"currency"EUR" });
l10nUSD.format(100200300.40) === "$100,200,300.40";
l10nGBP.format(100200300.40) === "£100,200,300.40";
l10nEUR.format(100200300.40) === "100.200.300,40 €";

ECMAScript 5:

// no equivalent in ES5

日期/时间格式化(Date/Time Formatting)

Format date/time with localized ordering and separators.

ECMAScript 6:

var l10nEN = new Intl.DateTimeFormat("en-US");
var l10nDE = new Intl.DateTimeFormat("de-DE");
l10nEN.format(new Date("2015-01-02")) === "1/2/2015";
l10nDE.format(new Date("2015-01-02")) === "2.1.2015";

ECMAScript 5:

// no equivalent in ES5


本文分享自微信公众号 - 像素摇摆(pxDance)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!