Ch.11 行為委派


複習第 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)。請注意其他的一些特性:

  1. 資料特性、狀態 (state) 直接位於委派者 (XYZ) 上。
  2. 在類別設計模式中,會使用同名的 mehod,以進行多型。但在行為委派中,盡可能地避免在 [[Prototype]] 串鏈的不同層級上為東西取相同名稱。使用描述性方法名稱,說明每個物件專屬的行為。
  3. 使用一個 XYZ 的方法卻找不到時,會依循連結前往 Task 尋找,此時雖然方法是 Task 的,但隱含的 this 繫結會是 XYZ。
  4. 相互委派是不允許的,在設定時期就會被駁回。

行為委派所代表的意義是,當某些物件在自身 (XYZ) 找不到特性或方法時,能夠進行委派 (給 Task)。

比較心智模型

請直接參考書本 P.201~205 (或是這裡),看一遍不懂,可以多看幾遍!

類別 vs. 物件

此節中,以製作一個 UI Widget 為實例介紹類別風格、ES6、委派三者實作的差異。ES6 的 class 只是語法糖,依然是運作於 [[Prototype]] 機制之上。在 OLOO 風格中:

  • Widget 只是一個物件,是一個工具的集合。
  • 與類別不同,OLOO 建議採用不同且較具描述性的名稱。
  • 語法上沒有出現任何 constructor.prototypenew
  • 創建 (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 的強大之處。


發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *