按照規劃,明年年中,ECMAScript 6(ES6)就要正式發佈了。javascript
最近抽空看了Dr. Axel Rauschmayer的幾篇文章和演講PPT,對新特性有了些瞭解。html
趁沒忘,抓緊記錄下,夾雜本身的感覺。java
計劃分三部分:git
參考瞭如下文章/PPT:es6
其餘文章:github
的確是「design by champions」。各類爲了代碼書寫效率進行的優化,借鑑了近年各類「新」語言的優秀特性,靈活性大大提高,閱讀難度也提高了……編程
不過,熟悉Ruby的看了這些會放心很多吧。segmentfault
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
表達式結果。forEach
的匿名函數參數中用到的this
。來看幾個例子:
let squares = [ 1, 2, 3 ].map(x => x * x); x => x + this.y // 至關於 function(x) { return x + this.y }.bind(this) // 但胖箭頭在執行效率上會更高
胖箭頭函數與正常函數的區別:
[[Construct]]
和屬性原型,因此new (() => {})
是會報錯的。arguments
變量。這樣,之後在定義方法/函數時,就有了清晰的選擇:
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的這部分實現有點繁瑣,須要更多示例才能理解這個特性。