前言
在 ES6中的 Class(類別)語法,並不是向其他語言真的是以類別為基礎 ( class-based ) 的物件導向,在骨子裡仍然是以原型為基礎 ( prototype-based ) 的物件導向,它只是個語法糖 ( syntactical sugar )。加入 Class(類別)語法的目的,並不是要建立另一套物件導向的繼承模型,而是為了提供更簡潔的語法來作物件建立與繼承,當然,一部份的原因是,讓已經熟悉以類別為基礎的物件導向程式語言的開發者使用,以此提供另一種在物件導向語法上的選擇。
語法糖(syntactical sugar):指的是在程式語言中添加的某些語法,這些語法對語言本身的功能並沒有影響,但是能更方便使用,可以讓程式碼更加簡潔,有更高可讀性。
類別宣告 class declaration
如何使用class
1 | class Person{ |
先設定一個 class類別 Person
,裏頭開分為兩塊來看,constructor可以想像成之前所學的函數建構子,若要新增方法 getFullName
寫在class內即可。
用函數建構子的方式,寫一次相同的程式,會更好理解。
1 | function Person(firstname, lastname) { |
在其他的語言,類別是創建物件的藍圖 blue print,但在ES6,類別本身就是被建立出來的物件,上例 class Person
是一個實際被建立的物件,開發者是從物件class Person建立新物件 john出來的,也就是說,ES6 的 Class,本質仍然是原型繼承,而非變成古典繼承。
class的命名規範和先前的函數建構子一樣首字建議大寫。
繼承 Extends
由剛剛的例子做延伸
1 | class Person{ |
結果是
john
的原型指向(繼承)了InformalPeron
,而InformalPeron
也繼承了 Person
的屬性及方法,形成一個原型鏈 prototypal chain。
呼叫父類別 Super
用
extends
關鍵字可以作類別的繼承,而在建構式中會多呼叫一個super()
方法,用於執行上層父母類別的建構式
之用。super
也可以用於指向上層父母類別,呼叫其中的方法
或存取屬性
。
繼承時還有有幾個注意的事項:
- 繼承的子類別中的建構式,
super()
需要放在建構式第一行,這是標準的呼叫方式。如果有需要傳入參數可以傳入。 - 繼承的子類別中的屬性與方法,都會覆蓋掉原有的在父母類別中的同名稱屬性或方法,要區為不同的屬性或方法要用super關鍵字來存取父母類別中的屬性或方法,例如
super.toString()
1 | class Point { |
輸出結果
創建 static 函數
關鍵字 static
定義了一個類別的靜態方法,靜態方法不需要實體化它所屬類別的實例就可以被呼叫,它也無法被已實體化的類別物件呼叫。靜態方法經常被用來建立給應用程式使用的工具函數,可以有效避免全域的污染。
1 | class Point { |
全域 namespace 呼叫
1 | Point.staticFn // output: 'This is static function' |
創建 setter、getter 函數
getter 及 setter 函數都是 callback function, callback 函數有三個的特點
- 由你定義的
- 你並沒有調用
- 但最終他執行了
1 | class Square { |
getter 在當你讀取此屬性值時,自動調用 (就像 Vue 框架的計算屬性 computed)
1 | square1.area // output: 10 |
setter 調用於 getter 參數被修改時,並且接受一個 param,值為 setter 被賦予的新值
1 | square1.area = 10 |
在執行 square1.area = 10
後, setter 已經被調用了,現在試著印出 setter 函式內所更改和新增的屬性
1 | console.log(newData) // output: 'Hello!' |
ES6 class 原型繼承完整 DEMO
小結
類別的語法目前雖然內容少而簡單,但它仍然有不少的好處。如果你把它當作是一種自訂物件類型的簡寫語法來看,它隱藏了很多JS中物件在作初始化與繼承的複雜語法,提供了較為簡單、閱讀性高、較容易維護的語法。並不是說直接使用原型語法來寫達不到,只是原型物件導向的特性需要理解到一定的程度,才有辦法寫出像類別這麼簡短的語法能達到的事情。