前记
根据计划,来岁年中,ECMAScript 6(ES6)就要正式宣布了。
近来抽闲看了Dr. Axel Rauschmayer的几篇文章和演讲PPT,对新特征有了些相识。
趁没忘,抓紧记录下,混合自身的感觉。
计分别三部份:
- 新语法
- 面临对象和模块化
- 规范库扩大
参考了以下文章/PPT:
- Use ECMAScript 6 today
- Ecmascript 6 Whats next for Javascript
- es6 features
- ECMAScript 6: arrow functions and method definitions
- Callable entities in ECMAScript 6
- Iterators and generators in ECMAScript 6
其他文章:
整体印象
的确是「design by champions」。种种为了代码誊写效力举行的优化,自创了最近几年种种「新」言语的优异特征,灵活性大大提拔,浏览难度也提拔了……
不过,熟习Ruby的看了这些会宁神不少吧。
新语法
1.块级作用域 关键字let
, const
function order(x, y) {
if (x > y) {
let tmp = x;
x = y;
y = tmp;
}
console.log(tmp === x); // 援用毛病:tmp此时未定义
return [x,y];
}
JS终究有了块级作用域变量。虽然在代码构造层面没有太大的作用(之前没偶然也活得很好么,虽然不怎么惬意),但会让代码越发正确,更易于浏览。
今年夏天宣布的Swift中也增加了let
关键字,虽然有些许区分,但目标应当是差不多——提拔代码可读性。
2.对象字面量的属性赋值简写 property value shorthand
let first = 'Bob';
let last = 'Dylan';
let singer = { first, last };
console.log(singer.first + " " + singer.last); // Bob Dylan
这关于常常运用对象作为设置属性参数的苦主来讲,算个小小的劝慰了。预计反复增加统一属性会报错吧,没有考证。
3.要领定义 Method definitions
let obj = {
myMethod(arg0, arg1) {
...
}
};
防止了在对象定义中涌现function
关键字,越发清楚明确地星散出函数的三种用处。
4.赋值解构 Destructuring
let singer = { first: "Bob", last: "Dylan" };
let { first: f, last: l } = singer; // 相当于 f = "Bob", l = "Dylan"
依旧是为了轻易。今后代码头部的「变量定义地区」不会有太多行了。
数组也是能够的,下面这个例子迥殊棒:
let [all, year, month, day] =
/^(\d\d\d\d)-(\d\d)-(\d\d)$/.exec("2014-08-31");
let [x, y] = [1, 2, 3]; // x = 1, y = 2
固然也能够如许,但有些……:
function f([x]) {...} // 参数定义
f(['Blonde on Blonde']);
下面是几种毛病用法(Refutable):
let { a: x, b: y } = {a: 3}; // TypeError
let [x, y] = ['a']; // TypeError
更重要的是,支撑默许值,在情势不婚配或目标值undefined时有用:
let { a: x, b: y=5 } = {a: 3, b: undefined }; // x = 3, y = 5
let [x, y='b'] = ['a']; // x = 'a', y = 'b'
5.函数的多项返回值 Multiple return values
function findSong(songs, songTitle) {
for (let trackNumber = 0; trackNumber < songs.length; trackNumber++) {
let song = songs[trackNumber];
if(songTitle ===song.title) {
return {song, trackNumber};
}
}
return {song: undefined, trackNumber: -1}
}
let songList = ["Tombstone blues", "Don't think twice", "North country girl"];
let {song, trackNumber} = findSong(songList, "North country girl"); // song = "North country girl", trackNumber = 2;
由于赋值解构,所以也能够如许:
let {song} = findSong(...);
let {trackNumber} = findSong(...);
let {trackNumber, song} = findSong(...); // 变量递次不重要
实在就是返回个对象。
但也有个题目,变量名肯定要与函数返回对象的属性名雷同,这能够会是一个别扭点。
6.函数参数 – 默许值
function findArtist(name='', genre='') {
...
}
没什么好说的,今后不必再写var option = option || {}
了。
7.函数参数 – 参数打包 Rest parameters
function createArtistProfile(name, ...details) {
.. // details是个数组
}
所以,今后也不须要arguments
了。不过,看例子只是「1,rest」,不知可不能够「1,2,3,rest」。
8.函数参数 – 数组睁开 Spread parameters
Math.max(...[1,11,111]); // 111
算是参数打包的逆操纵,今后不必写[1,2,3].apply(Math.max)
这类代码了。
9.函数参数 – 指名参数 Named parameters
function func(arg0, {opt1, opt2}) {
return [opt1, opt2];
}
func(0, {opt1: 'a', opt2: 'b'}) // ['a', 'b']
一样是经由过程对象带来的变化。有个复杂点的例子:
class Entries {
// ...
selectEntries({ from = 0, to = this.length } = {}) {
// Long: { from: from=0, to: to=this.length }
// Use `from` and `to`
}
}
let entries = new Entries();
entries.selectEntries({ from: 5, to: 15 });
entries.selectEntries({ from: 5 });
entries.selectEntries({ to: 15 });
指名参数+赋值解构+默许参数,看着反而有点杂沓了……自由度大自然带来浏览难度的上升,这又是一个衡量点。
10.胖箭头函数 Arrow functions
let bob = {
name: "Bob Dylan",
holdConcert: function (songList) {
songList.forEach(song => {
console.log(this.name + " sang " + song)
});
}
}
这里情势上自创了CoffeeScript里「fat arrow」(ES6对实行和内存上有优化)。Arrow functions重要做了两件事:
- 简化了代码情势,默许
return
表达式效果。 - 自动绑定语义this,即定义函数时的this。如上面例子中,
forEach
的匿名函数参数中用到的this
。
来看几个例子:
let squares = [ 1, 2, 3 ].map(x => x * x);
x => x + this.y
// 相当于
function(x) { return x + this.y }.bind(this)
// 但胖箭头在实行效力上会更高
胖箭头函数与一般函数的区分:
- 胖箭头在建立时即绑定this(lexical this);一般函数的this是在实行时动态传入的(dynamic this)。
- 胖箭头没有内部要领
[[Construct]]
和属性原型,所以new (() => {})
是会报错的。 - 胖箭头没有
arguments
变量。
如许,今后在定义要领/函数时,就有了清楚的挑选:
- 定义子程序(subroutine),用胖箭头,自动取得语义this。
- 定义要领(method),用一般函数,动态this。而且能够用要领定义特征简写代码,防止
function
关键字涌现。
11.字符串模板 Template strings
templateHandler`Hello ${first} ${last}!`
${first}
如许的构造在Ruby的字符串处置惩罚很罕见,first
是动态替代的部份。templateHandler
是替代后的处置惩罚函数。
固然也能够不要handler,那就仅仅是模板替代了:
if(x > MAX) {
throw new Error(`At most ${MAX} allowed: $(x)!`);
}
Template strings支撑多行,其间的文本也不会被转码:
var str = String.raw`This is a text
with multiple lines.
Escapes are not interpreted,
\n is not a newline.`;
连系差别的handler,用法多样,比方正则:
let str = "Bob Dylan - 2009 - Together Through Life";
let albumInfo = str.match(XRegExp.rx`
^(?<artist>[^/]+ ) - (?<year>\d{4}) - (?<albumTitle>[^/]+)$
`);
console.log(albumInfo.year); // 2009
12.迭代器 Iterators
轻微熟习函数式编程(Python,Ruby也能够)的朋侪对着这个观点应当都不生疏。ES6参考了Python的设想,迭代器有个next
要领,挪用会返回:
- 返回迭代对象的一个元素:
{ done: false, value: elem }
- 假如已到迭代对象的末尾:
{done: true[, value: retVal] }
上面第二种状况中的前提返回部份是为了递归挪用生成器而设想的(迭代器实际上是生成器的运用之一),详细申明拜见这篇文章的对应部份。
下例完成了一个数组的迭代器:
function createArrayIterator(arr) {
let index = 0;
return {
next() {
if (index < arr.length) {
return { done: false, value: arr[index++] };
else {
return { done: true }
}
}
}
}
let arr = [1,2,3];
let iter = createArrayIterator(arr);
console.log(iter.next()); // 1
console.log(iter.next()); // 2
在ES6中,可迭代数据构造(比方数组)都必须完成一个名为Symbol.iterator
的要领,该要领返回一个该构造元素的迭代器。注重,Symbol.iterator
是一个Symbol,Symbol是ES6新到场的原始值范例。
针对可迭代的数据构造,ES6还引入了一个新的遍历要领 for-of。再举个例子,革新下上例中的createArrayIterator
:
function createArrayIterator(arr) {
let index = 0;
return {
[Symbol.iterator]() {
return this; // 由于自身就是个迭代器
},
next() {
...
}
}
}
let arr = [1, 2, 3];
for(x of createArrayIterator(arr)) { // 注重看
console.log(x);
}
固然,ES6中的数组自身就是可迭代的,上例仅仅是为了展现罢了。
13.生成器 Generators
ES6的生成器一样自创了Python,经由过程操纵符yield
来挂起、继承。
生成器的写法比较奇异,运用了关键字function*
:
function* generatorFunction() {
yield 1;
yield 2;
}
生成器返回一个对象,用来掌握生成器实行,这个对象是可迭代的:
let genObj = generatorFunction();
genObj.next(); // { done: false, value: 1 }
genObj.next(); // { done: false, value: 2 }
genObj.next(); // { done: true }
下面这个例子演示了可递归挪用的生成器,用到了操纵符yield*
:
function* iterTree(tree) {
if (Array.isArray(tree)) {
for (let i = 0; i < tree.length; i++) {
yield* iterTree(tree[i]); // (*)
}
} else {
yield tree;
}
}
yield*
会交出(yield)悉数迭代对象,而不仅仅是一个元素值。原话是「yield* in line (*) yields everything that is yielded by the iterable that is its operand. 」
yield*
还能够通报返回值。如:
let result1 = yield* step(); // step也是个generator
这个例子不太好,或者说,ES6的这部份完成有点烦琐,须要更多示例才明白这个特征。