這篇想說的是 2015 年的那個 ECMAScript 6(後面簡稱 ES6),也就是之前 ECMAScript Harmony 計畫的主要成果,那版 ES6 其實是這幾年來改動最多的一版,新增了很多的新功能和語法,而這一堆新功能很多是環環相扣的,我以前曾經在 Facebook 上提過,不過那邊的東西容易就消失在網路上,所以還是另開一篇文章來記錄,剛好也可以做為下一篇文章的參考資料。
首先要從Map/Set這兩個新的資料型態說起,ECMAScript 一直以來都只有少少的資料型態,直到 ES6 才加了些新的,其中比較容易注意到的就是 Map 和 Set 了,其實這兩種資料型態以前就是直接用 object 來 array 來做,兩邊蠻接近的,最主要的差異則是 Map 的 key 可以是任意型態, 而以前 object 的 key 只能是字串,Set 是 unique 的,array 則否,另外就是在適合的情境下,現在 Map/Set 的效能不一定會比較差。
???? Jest reduced their test runner time by 20% by switching from plain JavaScript objects to Maps where it made sense.https://t.co/ZrtgeJIwOU
-- V8 (@v8js)2018年9月13日
In general, don't be afraid to use modern features! Focus on writing idiomatic code, and let JS engines worry about making it fast.
Map、Set 其實都算是 collection 的資料,所以會需要有個方法可以遍歷所有元素,像是 array 的forEach或是 for...in 語法,不過 for...in 先拿到的東西是 key,還需要拿 key 去取元素實際的值:
for (let k in arr) {
let v = arr[k];
}
一直以來,其實開發者社群都希望有個語法能直接取 collection 內的元素,所以像是 CoffeeScript 就是把for...in
換成直接拿到 value,於是 ES6 就有了個for...of
語法,可以遍歷 collection 類型的資料並直接取得值:
for (let v of arr) {
}
ES6 的這個語法,其實底層是透過iteration protocols這些內部協定來運作的,包括了 iterable protocol 和 iterator protocol,for...of
其實就是透過 iterable protocol 去拿物件的 iterator,利用 iterator 來遍歷元素,所以自己寫的物件也可以實做 iterable protocol,然後就可以讓該物件支援for...of
語法了;除此之外,iterator 是不能重複使用的,所以其實每次for..of
,都是拿一個該物件的新的 iterator,而為了可以簡單產生這個 iterator,又有了generator function,generator function 每次執行都會回傳一個新的 iterator(精確一點說是 generator object,同時是 iterator 也是 iterable),正好適合這個情境。
Iterable protocol 的定義其實很簡單,就是定義怎樣把 generator function 放在物件裡的方式,實際上是利用 ES6 另一個新的資料型態:Symbol來達成的,為什麼不直接定個屬性名稱給它呢?最主要就是不要讓這些內部 protocol 的東西在for...in
操作的時候被遍歷到,所以定義了新的 Symbol 型別,利用它的特性來把內部 protocol 做了一定程度的保護,iterable protocol 就是把 generator function 用一個預先定義好的 Symbol 來儲存,這個 Symbol 又稱為Symbol.iterator
,這種預先定義好的 Symbol 則統稱為 Well-Known Symbols,ES6 其實定義了好幾個,不是只有 iterable 用的到,透過定義這些 Well-Known Symobls,可以介入改變一些 JavaScript 比較基礎的運作。
Well-Known Symobls 可以做到的事情,其實有點像是改變程式語言的運作,而這種類型的機制又稱為meta programming,除了 Well-Known Symobls 之外,ES6 其實還提供了Proxy和Reflect,這兩個東西應該比較多人知道 Proxy 是幹嘛的,對 Reflect 比較陌生,其實 Reflect 有點像是為了 JavaScript 一些設計不好的地方,想了解詳細一點推薦可以看主要的 polyfill [harmony-reflect][] 的 Wiki 頁:Why should I use this library?
最後總結一下,這串從 Map/Set 開始,接著連到for...of
語法、Iterator、Generator、Symbol、Well-Known Symobls 最後到 meta programming 的 Proxy 和 Reflect,其實也差不多佔了 1/4 的 ES6 新功能。