原文地址 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) {} }
]);
网友评论