天天看點

《JavaScript設計模式》——9.11 Mixin模式

本節書摘來自異步社群《javascript設計模式》一書中的第9章,第9.11節, 作者: 【美】addy osmani 譯者: 徐濤 更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

在c++和lisp等傳統程式設計語言中,mixin是可以輕松被一個子類或一組子類繼承功能的類,目的是函數複用。

9.11.1 子類化

對于不熟悉子類化的開發人員來說,在深入研究mixin和decorator之前,将閱讀初學者内容。

子類化這個術語是指針對一個新對象,從一個基礎或超類對象中繼承相關的屬性。在傳統的面向對象程式設計中,類b是從另外一個類a擴充得來。這裡我們認為a是一個超類,b是a的一個子類。是以,b的所有執行個體從a處繼承了相關方法。但是b仍然能夠定義自己的方法,包括那些a最初所定義方法的重寫。

a中的一個方法,在b裡已經被重寫了,那麼b還需要調用a中的這個方法嗎,我們稱此為方法鍊。b需要調用構造函數a(超類)嗎,我們稱此為構造函數鍊。

為了示範子類化,首先需要一個可以建立自己新執行個體的基本對象。讓我們圍繞一個人的概念來模拟子類化。

下一步,指定一個新類(對象),它是現有person對象的一個子類。想象一下,在繼承person超類上的屬性同時,我們需要在superhero上添加另外不同的屬性。由于超級英雄與平常人具有很多共同的特征(如姓名、性别),希望這應該能夠充分說明子類化是如何工作的。

// 輸出person屬性和powers

superhero構造函數建立一個源于person的對象。這種類型的對象擁有在鍊中比它們靠上對象的屬性,如果我們已經在person對象中設定預設值,superhero就能夠重寫所有繼承的值,并且其對象本身也可以擁有特定的值。

9.11.2 mixin(混入)

在javascript中,我們可以将繼承mixin看作為一種通過擴充收集功能的方式。我們定義的每個新對象都有一個原型,可以從中繼承更多屬性。原型可以繼承于其他對象的原型,但更重要的是,它可以為任意數量的對象執行個體定義屬性。可以利用這一點來促進函數複用(見圖9-10)。

《JavaScript設計模式》——9.11 Mixin模式

mixin允許對象通過較低的複雜性借用(或繼承)功能。由于該模式非常适用于javascript的對象原型,它為我們提供了一種相當靈活的方式,從不隻一個mixin中分享功能,但實際上很多功能是通過多重繼承獲得的。

它們可以被視為具有可以在很多其他對象原型中輕松共享屬性和方法的對象。想象一下,我們在标準對象字面量中定義一個包含實用函數的mixin,如下所示:

然後我們可以使用underscore.js的_.extend()方法等輔助器輕松地擴充現有構造器函數的原型,以将上述行為包含進來:

正如我們所看到的,這允許我們以通用方式輕松“混入”對象構造函數。

在下一個示例中,我們有兩個構造函數:car和mixin。我們要做的是擴充(擴充的另一種說法)car,以便它可以繼承mixin中定義的特定方法,即driveforward()和drivebackward()。這次,我們不會使用underscore.js。

本示例将示範如何擴充構造函數,不需要對我們可能擁有的每個構造函數都重複這個過程而将功能包含進來。

// 給car構造函數增加"driveforward"和"drivebackward"兩個方法

// 測試確定新增方法可用

// 輸出:

// 也可以通過不聲明特定方法名的形式,将mixin的所有方法都添加到car裡

優點和缺點

mixin有助于減少系統中的重複功能及增加函數複用。當一個應用程式可能需要在各種對象執行個體中共享行為時,我們可以通過在mixin中維持這種共享功能并專注于僅實作系統中真正不同的功能,來輕松避免任何重複。

也就是說,有關mixin的缺點是稍有争議的。有些開發人員認為将功能注入對象原型中是一種很糟糕的想法,因為它會導緻原型污染和函數起源方面的不确定性。在大型系統中,可能就會有這種情況。

我認為,強大的文檔有助于将與混入函數來源有關的困惑減至最低,但對于每一種模式,如果在實作期間多加注意,一切應該會很順利。

繼續閱讀