複習第 10 章,[[Prototype]] 機制是存在一個物件上,參考了另一個物件的一種內部連結。這種連結會在對物件進行特性或方法的參考時,特性不存在的情況下發揮作用。當發生這種情況時,[[Prototype]] 會告訴引擎循著連結查找,一直循序尋找下去。一連串的連結就是「原型鏈 (prototype chain)」。
採用委派導向的設計
以直截了當的方式採用 [[Prototype]],必須從類別 / 繼承設計模式改為行為委派 (behavior delegation) 設計模式。
先進行一些理論上的練習,考量當我們有多個類似任務 (例如 XYZ、ABC 等) 的情境:
類別理論
- 抽取相似任務來定義共通行為,作為父類別 Task。
- 定義子類別 XYZ 與 ABC,並繼承自 Task。
- 新增子類別特化的行為,覆寫 (overriding)、多型 (polymorphism)
- 實體化出子類別 XYZ 的拷貝 (copies),並使用實體 (instance) 執行任務。實體具有 Task 定義的行為,也具有 XYZ 定義的行為。ABC 也會有專屬自己的行為。
- 實體化後只會與實體互動,不會與類別互動。
委派理論
- 定義一個 Task 物件。
- 對特定任務 (XYZ、ABC) 定義物件來持有該任務專屬的行為。
- 特定任務會連結到 Task 上,將 XYZ 委派給 Task。
作者稱這種風格為 OLOO (objects linked to other objects)。請注意其他的一些特性:
- 資料特性、狀態 (state) 直接位於委派者 (XYZ) 上。
- 在類別設計模式中,會使用同名的 mehod,以進行多型。但在行為委派中,盡可能地避免在 [[Prototype]] 串鏈的不同層級上為東西取相同名稱。使用描述性方法名稱,說明每個物件專屬的行為。
- 使用一個 XYZ 的方法卻找不到時,會依循連結前往 Task 尋找,此時雖然方法是 Task 的,但隱含的 this 繫結會是 XYZ。
- 相互委派是不允許的,在設定時期就會被駁回。
行為委派所代表的意義是,當某些物件在自身 (XYZ) 找不到特性或方法時,能夠進行委派 (給 Task)。
比較心智模型
請直接參考書本 P.201~205 (或是這裡),看一遍不懂,可以多看幾遍!
類別 vs. 物件
此節中,以製作一個 UI Widget 為實例介紹類別風格、ES6、委派三者實作的差異。ES6 的 class 只是語法糖,依然是運作於 [[Prototype]] 機制之上。在 OLOO 風格中:
- Widget 只是一個物件,是一個工具的集合。
- 與類別不同,OLOO 建議採用不同且較具描述性的名稱。
- 語法上沒有出現任何
constructor
、.prototype
或new
。 - 創建 (construction) 與初始化 (initialization) 能夠分開進行,更完善支援關注點分離 (separation of concerns) 的原則。
較為簡單的設計
此節再提供另一個範例。說明行為委派模式的優點:
- 物件是彼此的水平同儕。
- 減少實體的數量。
- 不需要進行合成。
- 避開多型的陷阱。
更好的語法
透過 ES6 可以使用簡潔的語法宣告,省略 function
。並使用 Object.setPrototypeOf()
來進行委派。
簡潔方法會根據上下文設定該函式物件內部的 name 特性。然而它們不會有語彙識別字來進行自我參考。
內省 (Introspection)
型別內省:檢視一個實體以判斷它是何種物件,依據它被創建的方式來推理物件的結構或能力。
instanceof
只是實體與建構函式的 .prototype
是否有關連。要使用必須要有一個函式,並且函式有對物件的參考。
duck typing (鴨子辨型)
:如果看起來像鴨子,叫聲也像鴨子,那必定是隻鴨子。因此可以判斷該物件有其方法,則有能力使用該方法。然而,可能會有一些陷阱(ex. ES6 then
)。
if (a1.something) { a1.something(); }
OLOO 風格下,可以使用以下方法來進行型別內省,取代 instanceof
。
// relating `Foo` and `Bar` to each other Foo.isPrototypeOf( Bar ); // true Object.getPrototypeOf( Bar ) === Foo; // true // relating `b1` to both `Foo` and `Bar` Foo.isPrototypeOf( b1 ); // true Bar.isPrototypeOf( b1 ); // true Object.getPrototypeOf( b1 ) === Bar; // true
複習
- 行為委派模式中,物件是彼此對等的同儕,之間會有委派關係,而非父子類別關係。
- 僅使用物件來設計程式碼,不只簡化了所使用的語法,實際上也能產生較簡單的架構設計。
附錄 D – ES6 類別
ES6 Class 語法糖優點:
- 沒有對
.prototype
的參考。 - 使用
extends
。 super
使得在串鏈某一層級都能夠相對往上參考同名的方法。class
預設不包括指定特性,防呆機制。
陷阱:
- 使人誤以為真的有類別機制。實際上仍是使用委派,因此修改了父類別的方法時,子類別也會受影響。
- 當需要宣告特性時,仍得回歸到使用
.prototype
,使得實作細節暴露。 - 存在特性遮蔽問題。
super
是在宣告時期以算是「靜態」的方式繫結的。
雖然 ES6 class 語法看似漂亮,但實際上會使得概念更模糊不清,令人以為是靜態的,然而在 JS 中,任何物件的定義都是一種流動可變的東西,而這也是 JS 的強大之處。