JavaScript Mixin模式

2018-08-02 16:25 更新

Mixin 模式

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

子類劃分

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

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

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

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

var Person =  function( firstName , lastName ){

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

};

接下來(lái),我們將制定一個(gè)新的類(對(duì)象),它是一個(gè)現(xiàn)有的Person對(duì)象的子類.讓我們想象我們想要加入一個(gè)不同屬性用來(lái)分辨一個(gè)Person和一個(gè)繼承了Person"超類"屬性的Superhero.由于超級(jí)英雄分享了一般人類許多共有的特征(例如: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)建了一個(gè)自Peroson下降的對(duì)象。這種類型的對(duì)象擁有鏈中位于它之上的對(duì)象的屬性,而且如果我們?cè)赑erson對(duì)象中設(shè)置了默認(rèn)的值,Superhero能夠使用特定于它的對(duì)象的值覆蓋任何繼承的值。

Mixin(織入目標(biāo)類)

在Javascript中,我們會(huì)將從Mixin繼承看作是通過(guò)擴(kuò)展收集功能的一種途徑.我們定義的每一個(gè)新的對(duì)象都有一個(gè)原型,從其中它可以繼承更多的屬性.原型可以從其他對(duì)象繼承而來(lái),但是更重要的是,能夠?yàn)槿我鈹?shù)量的對(duì)象定義屬性.我們可以利用這一事實(shí)來(lái)促進(jìn)功能重用。

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

它們可以被看做是其屬性和方法可以很容易的在其它大量對(duì)象原型共享的對(duì)象.想象一下我們定義了一個(gè)在一個(gè)標(biāo)準(zhǔn)對(duì)象字面量中含有實(shí)用功能的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!" );
  }

};

然后我們可以方便的擴(kuò)展現(xiàn)有構(gòu)造器功能的原型,使其包含這種使用一個(gè) 如下面的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!

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

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

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

// 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)點(diǎn)&缺點(diǎn)

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

也就是說(shuō),對(duì)Mixin的副作用是值得商榷的.一些開發(fā)者感覺(jué)將功能注入到對(duì)象的原型中是一個(gè)壞點(diǎn)子,因?yàn)樗鼤?huì)同時(shí)導(dǎo)致原型污染和一定程度上的對(duì)我們?cè)泄δ艿牟淮_定性.在大型的系統(tǒng)中,很可能是有這種情況的。

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

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)