scriptDevs

ECMAScript 6 (ES6) 新特性介绍 一

2017-12-12  本文已影响30人  乌龟怕铁锤

原文地址 ECMAScript 6 — New Features: Overview & Comparison
本文介绍 ECMAScript 6中的新特性并通过具体代码演示与ECMAScript 5的差别。译者额外增加了一些例子和说明 .

ECMAScript 6 是2015年推出的第6版本的ECMAScript标准(即Javascript标准),同时也被成为ECMAScript 2015.

ECMAScript 6 中定了了许多新的Javascript特性,包括新的类和模块,类似python的generators和generators表达式, 箭头函数, 二进制数据, 类型数组, 集合类型(maps, set & 弱maps), promises, reflection, proxy等。它也被称作 ES6 Harmony. (和谐ES6)

本文是该系列文章的第一篇

作用域

变量的作用域

现在在代码块中以let声明的变量和常量不会被提升到顶部声明。

ECMAScript 6的实现

for (let i = 0; i < a.length; i++) {
    let x = a[i]  // 代码块中定义新的x变量
    …
}
for (let i = 0; i < b.length; i++) {
    let y = b[i] //  代码块中定义新的y变量
    …
}

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;

ECMAScript 5中错误的写法


var callbacks = [];
for (var i = 0; i <= 2; i++) {
    callbacks[i] = function() { return i * 2; }; // 这里的i会被提升到顶部声明,导致三个callback中的i都是3
}
callbacks[0]() === 6;
callbacks[1]() === 6;
callbacks[2]() === 6;

函数的作用域

函数在代码块中定义的方式

ECMAScript 6的实现

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

对比ECMAScript 5的实现

(function () {  // 必须通过模拟代码块环境来实现
    var foo = function () { return 1; }
    foo() === 1;
    (function () {
        var foo = function () { return 2; }
        foo() === 2;
    })();
    foo() === 1;
})();

箭头函数

表达体函数

更简单的表达函数包符号

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 (v) { return v + 1; });
pairs = evens.map(function (v) { return { even: v, odd: v + 1 }; });
nums  = evens.map(function (v, i) { return 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的指代

this更加直观的指代当前上下文

ECMAScript 6的实现

this.nums.forEach((v) => {
    if (v % 5 === 0)
        this.fives.push(v) // 这里的this 指的是运行for Each的上下文this.
})

对比ECMAScript 5的实现

//  variant 1
var self = this;
this.nums.forEach(function (v) {
    if (v % 5 === 0)
        self.fives.push(v);  // 需要额外指定self来获取外部this
});

//  variant 2
this.nums.forEach(function (v) {
    if (v % 5 === 0)
        this.fives.push(v); // 需要将外部this当作参数传入
}, this);

//  variant 3 (since ECMAScript 5.1 only)
this.nums.forEach(function (v) {
    if (v % 5 === 0)
        this.fives.push(v); // 需要绑定外部this
}.bind(this)); 

扩展的函数参数处理

默认参数

简单而且直观的默认参数方式

ECMAScript 6的实现

function f (x, y = 7, z = 42) {
    return x + y + z
}
f(1) === 50 // y取默认参数7, z取默认参数 42

对比ECMAScript 5的实现

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

可变参数个数

对剩余参数可变个数的单个参数聚合

ECMAScript 6的实现

function f (x, y, ...a) {
    return (x + y) * a.length // 获取剩余参数a的参数个数 a.length
}
f(1, 2, "hello", true, 7) === 9

对比ECMAScript 5的实现

function f (x, y) {
    var a = Array.prototype.slice.call(arguments, 2); // 通过协议获取参数个数并切割
    return (x + y) * a.length;
};
f(1, 2, "hello", true, 7) === 9;

扩展操作符

针对可以遍历的对象集合(例如数组,字符串),将其拆解成单个个体并填入其他参数/遍历对象。

ECMAScript 6的实现

var params = [ "hello", true, 7 ]
var other = [ 1, 2, ...params ] // [ 1, 2, "hello", true, 7 ] // 将数组params填入数组other

function f (x, y, ...a) {
    return (x + y) * a.length
}
f(1, 2, ...params) === 9 // 将params填入可变参数

var str = "foo"
var chars = [ ...str ] // [ "f", "o", "o" ] // 将str拆解为单个字符并填入数组

对比ECMAScript 5的实现

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

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

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

模版语法

字符串的插入修改

对单行和多行的字符串进行方便直观的插入修改

ECMAScript 6的实现

var customer = { name: "Foo" }
var card = { amount: 7, product: "Bar", unitprice: 42 }
// 可以看到 使用 ` ` 包含字符串模版,通过${} 插入内容
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 = { amount: 7, product: "Bar", unitprice: 42 };
// 需要使用 + + 来转换连接内容
var message = "Hello " + customer.name + ",\n" +
"want to buy " + card.amount + " " + card.product + " for\n" +
"a total of " + (card.amount * card.unitprice) + " bucks?";

自定义添加修改

弹性的表达式针对各种方法都可以执行例如:

ECMAScript 6的实现

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

对比ECMAScript 5的实现

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

原始字符串的操作

String现在可以操作原始字符串了 (不支持反斜杠)

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的实现

//无

文字扩展

对二进制和八进制的扩展

现在可以直接操作二进制和八进制的字符了

ECMAScript 6的实现

0b111110111 === 503 // 二进制实现
0o767 === 503 // 八进制实现

对比ECMAScript 5的实现

parseInt("111110111", 2) === 503;
parseInt("767", 8) === 503;
0767 === 503; // 只在 non-strict, 向后兼容模式

对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";
//  ES5中无
//  ES5中无
//  ES5中无

正则表达式增强

正则表达式的粘性匹配

通过保持匹配位置来更有效率的在不同的长字符串中匹配不同的正则表达式。

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+)/y, action: (match) => report(match) },
    { pattern: /^Bar\s+(\d+)/y, action: (match) => report(match) },
    { pattern: /^Baz\s+(\d+)/y, action: (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+)/, action: function (match) { report(match); } },
    { pattern: /^Bar\s+(\d+)/, action: function (match) { report(match); } },
    { pattern: /^Baz\s+(\d+)/, action: function (match) { report(match); } },
    { pattern: /^\s*/,         action: function (match) {}                 }
]);
上一篇下一篇

猜你喜欢

热点阅读