ES6 你可能不知道的事 – 進階篇

發表於2016-11-06

tb1xlp2oxxxxxaxxvxxxxxxxxxx-900-500

前言

這篇文章主要會針對上篇未涉及到的進階特性展開;而與前一篇文章相同,本文主要介紹這些特性的一些容易忽略的部分,希望能對大家正確認識和使用 ES6 有幫助。

還是那句話,時間和能力有限,針對文章中的問題或不同意見,歡迎隨時拍磚、指正!

正文

Module

模組化是個進行了很久的話題,發展歷程中出現過很多模式,例如 AMD, CommonJS 等等。

Module 是 ES6 的新特性,是語言層面對模組化的支援。

與之前模組載入機制不同,Module 是動態的載入,匯入的是變數的 只讀引用 ,而不是拷貝

Symbol

symbol 是 ES6 的一個新特性,他有如下特點:

  • symbol 是一個 “新” 的 基礎資料型別 ;從 ES6 起,JavaScript 的 基礎資料型別 變為 6 個:string, number, boolean, null, undefined, symbol
  • symbol 可以用作 Object 的 key
  • symbol 存在全域性作用域,利用 Symbol.for(key) 方法,可以建立(全域性作用域無指定鍵)或獲取全域性作用域內的 symbol ;利用 Symbol.keyFor(sym) 方法,可以獲取指定symbol 的鍵
  • JavaScript 內部使用了很多內建 symbol ,作為特殊的鍵,來實現一些內部功能;例如Symbol.iterator 用於標示物件的迭代器

“新” 僅僅是針對前端開發人員來說的,其實 Symbol 概念本身已經在 JavaScript 語言內部長時間使用了

Iterator + For..Of

ES6 中除了新特性外,還有一個新的規範,那就是關於迭代的規範,他包括兩部分分別是 “可迭代規範(iterable protocol)” 和 “迭代器規範(iterator protocol)”。任何實現了前者的物件,都可以進行 for…of 迴圈。

String, Array, Map, Set等是原生可迭代物件,因為他們都在原型(prototype)物件中實現了 Symbol.iterator 鍵對應的方法

for…of 是物件迭代器的遍歷,而 for…in 是物件中 可列舉 值的遍歷

下面用程式碼來解釋一下兩個規範:

關於集合

原來我們使用集合,多數情況下會直接用 Object 代替,ES6新增了兩個特性,MapSet,他們是對 JavaScript 關於集合概念的補充。

Map

剛剛看到這個概念的同學會有幾個常見的疑問,為什麼我們需要 Map 這種資料結構?直接用 Object 不好麼? 是不是 Map 可以完全取代 Object 用於資料存取?

MapObject 的區別

  • MapObject 都可以存取資料,Map 適用於儲存需要 常需要變化(增減鍵值對)或遍歷 的資料集,而 Object 適用於儲存 靜態 (例如配置資訊)資料集
  • Object 的 key 必須是 StringSymbol 型別的,而 Map 無此限制,可以是任何值
  • Map 可以很方便的取到鍵值對數量,而 Object 需要用額外途徑

Set

Set 作為最簡單的集合,有著如下幾個特點:

  • Set 可以儲存任何型別的值,遍歷順序與 插入順序相同
  • Set 內無重複的值

WeakMap + WeakSet

WeakMapWeakSet 作為一個比較新穎的概念,其主要特點在於弱引用。

相比於 MapSet 的強引用,弱引用可以令物件在 “適當” 情況下正確被 GC 回收,減少記憶體資源浪費。

但由於不是強引用,所以無法進行遍歷或取得值數量,只能用於值的存取(WeakMap)或是否存在值得判斷(WeakSet)

在弱引用的情況下,GC 回收時,不會把其視作一個引用;如果沒有其他強引用存在,那這個物件將被回收

非同步程式設計

在 ES6 之前,JavaScript 的非同步程式設計都跳不出回撥函式這個方式。回撥函式方式使用非常簡單,在簡單非同步任務呼叫時候沒有任何問題,但如果出現複雜的非同步任務場景時,就顯得力不從心了,最主要的問題就是多層回撥函式的巢狀會導致程式碼的橫向發展,難以維護;ES6 帶來了兩個新特性來解決非同步程式設計的難題。

Promise

Promise 是 ES6 的一個新特性,同為非同步程式設計方式,它主要有如下幾個特點:

  • 本質還是回撥函式
  • 區分成功和失敗的回撥,省去巢狀在內層的判斷邏輯
  • 可以很輕鬆的完成回撥函式模式到 Promise 模式的轉化
  • 程式碼由回撥函式巢狀的橫向擴充套件,變為鏈式呼叫的縱向擴充套件,易於理解和維護

Promise 雖然優勢頗多,但是程式碼結構仍與同步程式碼區別較大

Generator

Generator 作為 ES6 的新特性,是一個語言層面的升級。它有以下幾個特點:

  • 可以通過 yield 關鍵字,終止執行並返回(內到外)
  • 可以通過 next(val) 方法呼叫重新喚醒,繼續執行(外回內)
  • 執行時(包括掛起態),共享區域性變數
  • Generator 執行會返回一個結果物件,結果物件本身既是迭代器,同時也是可迭代物件(同時滿足兩個迭代規範),所以 Generator 可以直接用於 自定義物件迭代器

由於具備以上特點(第四點除外),Generator 也是 JavaScript 對 協程(coroutine)的實現,協程可以理解為 “可由開發人員控制排程的多執行緒”

協程按照排程機制來區分,可以分為對稱式和非對稱式

非對稱式:被呼叫者(協程)掛起時,必須將控制權返還呼叫者(協程)

對稱式:被呼叫者(協程)掛起時,可將控制權轉給 “任意” 其他協程

JavaScript 實現的是 非對稱式協程(semi-coroutine);非對稱式協程相比於對稱式協程,程式碼邏輯更清晰,易於理解和維護

協程給 JavaScript 提供了一個新的方式去完成非同步程式設計;由於 Generator 的執行會返回一個迭代器,需要手動去遍歷,所以如果要達到自動執行的目的,除了本身語法外,還需要實現一個執行器,例如 TJ 大神的 co 框架。

Generator 是一個 ES6 最佳的非同步程式設計選擇麼?顯然不是,因為除了基本語法外,我們還要額外去實現執行器來達到執行的目的,但是它整體的程式碼結構是優於回撥函式巢狀和Promise 模式的。

Async-Await

這並不是一個 ES6 新特性,而是 ES7 的語法,放在這裡是因為它將是 JavaScript 目前支援非同步程式設計最好的方式

超程式設計

超程式設計是指的是開發人員對 “語言本身進行程式設計”。一般是程式語言暴露了一些 API,供開發人員來操作語言本身的某些特性。ES6 兩個新特性 ProxyReflect 是 JavaScript 關於物件超程式設計能力的擴充套件。

Proxy

Proxy 是 ES6 加入的一個新特性,它可以 “代理” 物件的原生行為,替換為執行自定義行為。

這樣的超程式設計能力使得我們可以更輕鬆的擴充套件出一些特殊物件。

  • 任何物件都可以被 “代理”
  • 利用 Proxy.revocable(target, handler) 可以建立出一個可逆的 “被代理” 物件

Reflect

ES6 中引入的 Reflect 是另一個超程式設計的特性,它使得我們可以直接操縱物件的原生行為。Reflect 可操縱的行為與 Proxy 可代理的行為是一一對應的,這使得可以在 Proxy 的自定義方法中方便的使用 Reflect 調起原生行為。

進階閱讀

篇幅有限,無法面面俱到,還想再最後推薦給大家一些想進階瞭解 ES6 的必看內容

結語

基礎篇+進階篇基本介紹完了 ES6 的主要特性,但 ES6 僅僅是現在時,後續如果大家覺得這個系列有意思,可以再寫一寫 ES 2016+ 的相關內容,來擁抱一下更新的變化。

最後,希望文章中的部分內容可以對大家理解和使用 ES6 有所幫助,感謝支援~

參考資料

相關文章