十一、JavaScript Mixin模式

Mixin 模式

在諸如C++或者List著這樣的傳統(tǒng)語言中,織入模式就是一些提供能夠被一個或者一組子類簡單繼承功能的類,意在重用其功能。

子類劃分

對于不熟悉子類劃分的開發(fā)者,在深入織入模式和裝飾器模式之前,我們將對他們進行一個簡短的初學者入門指引。

子類劃分是一個參考了為一個新對象繼承來自一個基類或者超類對象的屬性的術(shù)語.在傳統(tǒng)的面向?qū)ο缶幊讨?類B能夠從另外一個類A處擴展.這里我們將A看做是超類,而將B看做是A的子類.如此,所有B的實體都從A處繼承了其A的方法.然而B仍然能夠定義它自己的方法,包括那些重載的原本在A中的定義的方法毫蚓。

B是否應(yīng)該調(diào)用已經(jīng)被重載的A中的方法,我們將這個引述為方法鏈.B是否應(yīng)該調(diào)用A(超類)的構(gòu)造器,我們將這稱為構(gòu)造器鏈。

為了演示子類劃分,首先我們需要一個能夠創(chuàng)建自身新實體的基對象昔善。

var Person =  function( firstName , lastName ){

  this.firstName = firstName;
  this.lastName =  lastName;
  this.gender = "male";

};

接下來,我們將制定一個新的類(對象),它是一個現(xiàn)有的Person對象的子類.讓我們想象我們想要加入一個不同屬性用來分辨一個Person和一個繼承了Person"超類"屬性的Superhero.由于超級英雄分享了一般人類許多共有的特征(例如:name,gender),因此這應(yīng)該很有希望充分展示出子類劃分是如何工作的元潘。

// a new instance of Person can then easily be created as follows:
var clark = new Person( "Clark" , "Kent" );

// Define a subclass constructor for for "Superhero":
var Superhero = function( firstName, lastName , powers ){

    // Invoke the superclass constructor on the new object
    // then use .call() to invoke the constructor as a method of
    // the object to be initialized.

    Person.call( this, firstName, lastName );

    // Finally, store their powers, a new array of traits not found in a normal "Person"
    this.powers = powers;
};

SuperHero.prototype = Object.create( Person.prototype );
var superman = new Superhero( "Clark" ,"Kent" , ["flight","heat-vision"] );
console.log( superman );

// Outputs Person attributes as well as powers

Superhero構(gòu)造器創(chuàng)建了一個自Peroson下降的對象。這種類型的對象擁有鏈中位于它之上的對象的屬性,而且如果我們在Person對象中設(shè)置了默認的值,Superhero能夠使用特定于它的對象的值覆蓋任何繼承的值君仆。

Mixin(織入目標類)

在Javascript中,我們會將從Mixin繼承看作是通過擴展收集功能的一種途徑.我們定義的每一個新的對象都有一個原型,從其中它可以繼承更多的屬性.原型可以從其他對象繼承而來,但是更重要的是,能夠為任意數(shù)量的對象定義屬性.我們可以利用這一事實來促進功能重用翩概。

Mix允許對象以最小量的復(fù)雜性從它們那里借用(或者說繼承)功能.作為一種利用Javascript對象原型工作得很好的模式,它為我們提供了從不止一個Mix處分享功能的相當靈活,但比多繼承有效得多得多的方式牲距。

它們可以被看做是其屬性和方法可以很容易的在其它大量對象原型共享的對象.想象一下我們定義了一個在一個標準對象字面量中含有實用功能的Mixin,如下所示:

var myMixins = {

  moveUp: function(){
    console.log( "move up" );
  },

  moveDown: function(){
    console.log( "move down" );
  },

  stop: function(){
    console.log( "stop! in the name of love!" );
  }

};

然后我們可以方便的擴展現(xiàn)有構(gòu)造器功能的原型,使其包含這種使用一個 如下面的score.js_.extends()方法輔助器的行為:

// A skeleton carAnimator constructor
function carAnimator(){
  this.moveLeft = function(){
    console.log( "move left" );
  };
}

// A skeleton personAnimator constructor
function personAnimator(){
  this.moveRandomly = function(){ /*..*/ };
}

// Extend both constructors with our Mixin
_.extend( carAnimator.prototype, myMixins );
_.extend( personAnimator.prototype, myMixins );

// Create a new instance of carAnimator
var myAnimator = new carAnimator();
myAnimator.moveLeft();
myAnimator.moveDown();
myAnimator.stop();

// Outputs:
// move left
// move down
// stop! in the name of love!

如我們所見,這允許我們將通用的行為輕易的"混"入相當普通對象構(gòu)造器中。

在接下來的示例中,我們有兩個構(gòu)造器:一個Car和一個Mixin.我們將要做的是靜Car參數(shù)化(另外一種說法是擴展),以便它能夠繼承Mixin中的特定方法,名叫driveForwar()和driveBackward().這一次我們不會使用Underscore.js钥庇。

取而代之牍鞠,這個示例將演示如何將一個構(gòu)造器參數(shù)化,以便在無需重復(fù)每一個構(gòu)造器函數(shù)過程的前提下包含其功能上沐。

// Define a simple Car constructor
var Car = function ( settings ) {

        this.model = settings.model || "no model provided";
        this.color = settings.color || "no colour provided";

    };

// Mixin
var Mixin = function () {};

Mixin.prototype = {

    driveForward: function () {
        console.log( "drive forward" );
    },

    driveBackward: function () {
        console.log( "drive backward" );
    },

    driveSideways: function () {
        console.log( "drive sideways" );
    }

};

// Extend an existing object with a method from another
function augment( receivingClass, givingClass ) {

    // only provide certain methods
    if ( arguments[2] ) {
        for ( var i = 2, len = arguments.length; i < len; i++ ) {
            receivingClass.prototype[arguments[i]] = givingClass.prototype[arguments[i]];
        }
    }
    // provide all methods
    else {
        for ( var methodName in givingClass.prototype ) {

            // check to make sure the receiving class doesn't
            // have a method of the same name as the one currently
            // being processed
            if ( !Object.hasOwnProperty(receivingClass.prototype, methodName) ) {
                receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            }

            // Alternatively:
            // if ( !receivingClass.prototype[methodName] ) {
            //  receivingClass.prototype[methodName] = givingClass.prototype[methodName];
            // }
        }
    }
}

// Augment the Car constructor to include "driveForward" and "driveBackward"
augment( Car, Mixin, "driveForward", "driveBackward" );

// Create a new Car
var myCar = new Car({
    model: "Ford Escort",
    color: "blue"
});

// Test to make sure we now have access to the methods
myCar.driveForward();
myCar.driveBackward();

// Outputs:
// drive forward
// drive backward

// We can also augment Car to include all functions from our mixin
// by not explicitly listing a selection of them
augment( Car, Mixin );

var mySportsCar = new Car({
    model: "Porsche",
    color: "red"
});

mySportsCar.driveSideways();

// Outputs:
// drive sideways

優(yōu)點&缺點

Mixin支持在一個系統(tǒng)中降解功能的重復(fù)性,增加功能的重用性.在一些應(yīng)用程序也許需要在所有的對象實體共享行為的地方,我們能夠通過在一個Mixin中維護這個共享的功能,來很容易的避免任何重復(fù),而因此專注于只實現(xiàn)我們系統(tǒng)中真正彼此不同的功能。

也就是說,對Mixin的副作用是值得商榷的.一些開發(fā)者感覺將功能注入到對象的原型中是一個壞點子,因為它會同時導(dǎo)致原型污染和一定程度上的對我們原有功能的不確定性.在大型的系統(tǒng)中,很可能是有這種情況的楞艾。

我認為,強大的文檔對最大限度的減少對待功能中的混入源的迷惑是有幫助的,而且對于每一種模式而言,如果在實現(xiàn)過程中小心行事,我們應(yīng)該是沒多大問題的参咙。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市硫眯,隨后出現(xiàn)的幾起案子蕴侧,更是在濱河造成了極大的恐慌,老刑警劉巖两入,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件净宵,死亡現(xiàn)場離奇詭異,居然都是意外死亡裹纳,警方通過查閱死者的電腦和手機择葡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剃氧,“玉大人敏储,你說我怎么就攤上這事∨蟀埃” “怎么了已添?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長滥酥。 經(jīng)常有香客問我更舞,道長,這世上最難降的妖魔是什么坎吻? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任缆蝉,我火速辦了婚禮,結(jié)果婚禮上瘦真,老公的妹妹穿的比我還像新娘返奉。我一直安慰自己,他們只是感情好吗氏,可當我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布芽偏。 她就那樣靜靜地躺著,像睡著了一般弦讽。 火紅的嫁衣襯著肌膚如雪污尉。 梳的紋絲不亂的頭發(fā)上膀哲,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天,我揣著相機與錄音被碗,去河邊找鬼某宪。 笑死,一個胖子當著我的面吹牛锐朴,可吹牛的內(nèi)容都是我干的兴喂。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼焚志,長吁一口氣:“原來是場噩夢啊……” “哼衣迷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起酱酬,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤壶谒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后膳沽,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體汗菜,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年挑社,在試婚紗的時候發(fā)現(xiàn)自己被綠了陨界。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡痛阻,死狀恐怖普碎,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情录平,我是刑警寧澤麻车,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站斗这,受9級特大地震影響动猬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜表箭,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一赁咙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧免钻,春花似錦彼水、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拆魏,卻和暖如春盯桦,著一層夾襖步出監(jiān)牢的瞬間慈俯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工拥峦, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留贴膘,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓略号,卻偏偏與公主長得像刑峡,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子玄柠,可洞房花燭夜當晚...
    茶點故事閱讀 45,086評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 第3章 基本概念 3.1 語法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,128評論 0 21
  • ??面向?qū)ο螅∣bject-Oriented随闪,OO)的語言有一個標志阳似,那就是它們都有類的概念骚勘,而通過類可以創(chuàng)建任意...
    霜天曉閱讀 2,109評論 0 6
  • 這是16年5月份編輯的一份比較雜亂適合自己觀看的學習記錄文檔铐伴,今天18年5月份再次想寫文章,發(fā)現(xiàn)簡書還為我保存起的...
    Jenaral閱讀 2,762評論 2 9
  • 下標腳本 下標腳本 可以定義在類俏讹、結(jié)構(gòu)體和枚舉這些目標中当宴,可以認為是訪問集合(collection),列表(li...
    cht005288閱讀 449評論 0 0
  • class的基本用法 概述 JavaScript語言的傳統(tǒng)方法是通過構(gòu)造函數(shù)泽疆,定義并生成新對象户矢。下面是一個例子: ...
    呼呼哥閱讀 4,100評論 3 11