學習 ECMAScript/JavaScript 6 - Class
本文將介紹 ECMAScript/JavaScript 6 新式的 Class (類別) 語法。但直到本文發布時,實作支持此語法的瀏覽器還不多,多數還在測試階段。想要練習本文內容,你需要使用 JavaScript compiler ,例如 Babel,將包含 ES6 語法的 JavaScript 程式碼,轉譯為現在的瀏覽器認得的語法。
ECMAScript/JavaScript 之前雖然沒有提供 class 語法,但它仍然是一種個體導向程式語言(OOPL),只不過它屬於 prototype-based 這支比較冷門的派別。 prototype-based 這支的世界觀比較接近現實世界,因為在現實世界中是先有獨特的個體出現,我們才以觀察者的身份依共同特徵將複數個體分類,這才有類別。ECMAScript/JavaScript 仍然支援繼承 (inheritance) 、封裝 (encapsulation) 、動態連結 (dynamic binding) 這三種特性。
為何增加 class 語法
儘管 ECMAScript/JavaScript 的封裝繼承規範一樣不缺,但目前程式語言的主要派別是 class-based ,先定義類別,才有實例的個體。很多編程人員一開始學的就是 class-based 派的程式語言。當他們開始接觸 JavaScript 時,通常腦子轉不過來,因 JavaScript 沒有 class, extends, constructor 這些關鍵字而困惑,抱怨 JavaScript 的語法不完善。所以 ES6 決定從眾,增加了 class 語法。
Class 語法是 ECMAScript/JavaScript 原有個體封裝與繼承模式的另一種包裝糖衣。這個糖衣雖然少了一些設計彈性,但好處是語意結構比較嚴謹易讀。對於老練的 JavaScript 編程人員來說,學不學無所謂。但有助於降低使用者從其他程式語言轉換過來的門檻。
我們先複習一次 ECMAScript/JavaScript 3 就已經存在的「傳統」JavaScript 封裝與繼承用法,這部份請看我以前寫的兩篇文章:
Property 定義
轉眼間,這兩篇文章已經過十年了。在這段時間中, C# 的普及讓人們認識了 property 的使用彈性。所以未正式推廣的 ECMAScript/JavaScript 5 規範增加了 defineProperty() 方法,讓編程人員可以自定個體屬性的存取方法 (setter/getter)。請看下列示範。
上例的 defineProperty() 可能有些較舊的瀏覽器並未實作。但隨著 ES6 的落實,以後支持 ES6 的瀏覽器也會提供 defineProperty() 。
Class 語法
ECMAScript/JavaScript 6 新增的 Class 語法,基本上就和其他程式語言的用法差不多。相關的關鍵字有 class, extends, constructor, super, static, get, set 。但未提供 public, private 等存取權限飾詞。
將上例的 Square 以新的 Class 語法改寫如下:
在 Class 中定義方法內容時,不需要加上 function 關鍵字。建構方法 constructor() 則是類別實體化時調用的方法,負責設置實體的初始內容。至於 Property 的定義方式也很簡單,只要在方法前面加上 get 或 set 飾詞即可。
若要衍生新的類別,使用 extends 指示新的類別繼承的基底類別。而 super 則是在衍生類別中用於指示基底類別的關鍵字;super.???
表示基底類別成員,super()
表示調用基底類別的建構方法。在方法名稱前加上 static 飾詞則是定義類別方法。這些用法都和其他 class-based 的程式語言差不多。
下列是衍生類別的範例:
ECMAScript/JavaScript 6 特有內容
除了和其他程式語言相同之處, ECMAScript/JavaScript 6 還是有一些特有的內容。
Computed property names
在 ECMAScript/JavaScript 6 中,你可以在成員名稱的位置用 [ ]
包住一段敘述,將敘述的計算結果作為成員名稱。敘述可以是變數、函數或符號。這種用法稱為 “Computed property names”。這用法不限於配合新的 Class 語法,其他地方也可以用。
範例:
Generator
在方法名稱前加上 * 號,就是將此方法定義為 generator 。
new.target
在衍生類別中,你可以透過 super 操作基底類別成員。那麼反過來呢? ECMAScript/JavaScript 6 就增加了 new.target 讓你可以在基底類別的方法中,得知衍生類別為何。
一個類別可以衍生好幾個類別,所以 new.target 的實際指涉對象會改變。它是看 new 操作符的對象決定。每一個函數內部都會隱含 new.target 變數 (就像 arguments);若這個函數不是透過 new 調用的話,其值將為 undefined。
注意,在良好的繼承樹中,基底類別不必認知衍生類別的內容,所以要小心,別誤用了 new.target 。
相關文章
- ES6 In Depth: Classes
- MDN: Classes
- 石頭閒語: 掌握 JavaScript 的「封裝」特性, part 1
- 石頭閒語: 掌握 JavaScript 的「封裝」特性, part 2
- 石頭閒語: ECMAScript/JavaScript 6 - Template strings
- 石頭閒語: ECMAScript/JavaScript 6 - Symbol
- 石頭閒語: ECMAScript/JavaScript 6 - for-of 與 iterator。
- 石頭閒語: ECMAScript/JavaScript 6 - Generator
- 石頭閒語: ECMAScript/JavaScript 6 - 新函數語法 - Arrow functions, Rest and Spread parameters, Default value
- 石頭閒語: ECMAScript/JavaScript 6 - Destructuring
- 石頭閒語: ECMAScript/JavaScript 6 - var, let 和 const
- 石頭閒語: ECMAScript/JavaScript 6 - Proxy 和 Reflect
- 石頭閒語: ECMAScript/JavaScript 6 - Class
- 石頭閒語: ECMAScript/JavaScript 6 - 語法補遺
- 石頭閒語: ECMAScript/JavaScript 6 - Promise