JavaScript中Object的總結(jié)

基于原型繼承庶溶,動(dòng)態(tài)對(duì)象擴(kuò)展秘症,閉包匪凉,JavaScript已經(jīng)成為當(dāng)今世界上最靈活和富有表現(xiàn)力的編程語(yǔ)言之一捌肴。

這里有一個(gè)很重要的概念需要特別指出:在JavaScript中,包括所有的函數(shù)藕咏,數(shù)組状知,鍵值對(duì)和數(shù)據(jù)結(jié)構(gòu)都是對(duì)象。 舉個(gè)簡(jiǎn)單的例子:

var testFunc = function testFunc() {

};
testFunc.customP = "James";
console.log(testFunc.customP);

上邊的代碼中孽查,testFunc可以添加customP這個(gè)屬性饥悴,說(shuō)明testFunc本身就是一個(gè)對(duì)象。在JavaScript中盲再,函數(shù)名是一個(gè)指向函數(shù)對(duì)象的指針西设,我們看下邊的代碼:

var testFunc = function testFunc() {
    console.log("Hello world");
};
var anotherTestFunc = testFunc;
testFunc = null;
anotherTestFunc();

即使把testFunc置為空,上邊的程序仍然打印了Hello world答朋。通過(guò)上邊的例子贷揽,我們演示了函數(shù)作為對(duì)象的證據(jù),然而JavaScript中的基本數(shù)據(jù)類型绿映,在特定的情況下也會(huì)體現(xiàn)出對(duì)象的特性:

'tonya@example.com'.split('@')[1]; // => example.com

當(dāng)我們可以使用屬性獲取符.來(lái)操作基本類型的時(shí)候擒滑,它就表現(xiàn)的很像對(duì)象腐晾,但我們不能給它賦值叉弦。原因是:基本類型會(huì)被臨時(shí)包裝成object,之后會(huì)立刻拋棄這個(gè)包裝藻糖,表面上看像是賦值成功了淹冰,但下次是無(wú)法訪問(wèn)之前的賦值的。

接下來(lái)巨柒,我們會(huì)探討JavaScript中Object的一些問(wèn)題樱拴,這和其他面向?qū)ο蟮恼Z(yǔ)言有很大不同,我們會(huì)解釋為什么JavaScripts不是面向?qū)ο箢愋偷恼Z(yǔ)言洋满,而是原型語(yǔ)言晶乔。

類型繼承是否應(yīng)該被淘汰

Design Patterns: Elements of Reusable Object Oriented Software這本書(shū)中有兩個(gè)關(guān)于面向?qū)ο笤O(shè)計(jì)程序的原則:

  • Program to an interface, not an implementation 面向接口編程
  • Favor object composition over class inheritance 優(yōu)先使用組合,而非繼承

在某種意義上牺勾,上邊的第二個(gè)原則同樣遵循了第一個(gè)原則正罢,因?yàn)槔^承把父類暴露給了子類,這樣的話驻民,子類就被設(shè)計(jì)成了一個(gè)實(shí)現(xiàn)翻具,而不是接口了。因此我們得出一個(gè)結(jié)論回还,類型繼承破壞了封裝原則裆泳,并且把子類同它的祖先緊密聯(lián)系在一起。

舉一個(gè)更生動(dòng)的例子柠硕,我們可以把類型繼承比作是家具工禾,我們用很多設(shè)計(jì)好的零部件來(lái)拼裝一個(gè)家具,如果這些零部件都符合設(shè)計(jì),那么我們有很高的機(jī)會(huì)組裝成功闻葵,如果有一些不符合要求糙捺,那么就會(huì)組裝失敗。

在程序設(shè)計(jì)中笙隙,組合就像樂(lè)高積木洪灯,大部分的零件被設(shè)計(jì)成能夠和其他零件拼接到一起,這樣竟痰,我們就能夠很靈活的進(jìn)行組裝了签钩。

如何按照組件化的思想來(lái)設(shè)計(jì)程序,不是本篇文章的內(nèi)容坏快,現(xiàn)在我們來(lái)看看反對(duì)繼承的理由是什么:

  • 高耦合 繼承在面向?qū)ο蟮脑O(shè)計(jì)中的耦合性最高铅檩,子類與其祖先類緊密相連
  • 層次劃分不靈活 在真實(shí)的開(kāi)發(fā)中,往往不會(huì)出現(xiàn)單層的繼承莽鸿,如果使用了多層次的繼承昧旨,那么很可能有很多繼承過(guò)來(lái)的屬性是不需要的,這樣就造成了代碼的過(guò)度重復(fù)
  • 多繼承難以理解 有時(shí)候很有必要會(huì)繼承多個(gè)父類祥得,對(duì)于這種情況的處理跟單一繼承是不一樣的兔沃,會(huì)更復(fù)雜,需要處理沖突和不一致的情況级及,并且代碼也變得難以閱讀和理解
  • 脆弱的架構(gòu) 高耦合的程序乒疏,很難對(duì)某個(gè)類進(jìn)行代碼重構(gòu),這就體現(xiàn)了架構(gòu)的脆弱性
  • 大猩猩/香蕉問(wèn)題 父類中的某些屬性可能不是我們需要的饮焦,子類可以重寫(xiě)父類的屬性怕吴,但不能選擇繼承那些屬性,就像县踢,我只需要香蕉转绷,繼承卻給了我一個(gè)拿著香蕉的大猩猩和整片叢林

JavaScript的繼承和其他面向?qū)ο笳Z(yǔ)言的繼承有很大不同,只有我們了解了繼承的缺點(diǎn)硼啤,才能更好的使用好這些特性议经。

Prototypes

prototype是JavaScript中很重要的一個(gè)概念,他可以說(shuō)是對(duì)其他對(duì)象的一個(gè)模仿丙曙。又很像是一個(gè)類爸业,你可以用他來(lái)構(gòu)建很多實(shí)例對(duì)象。但他的本質(zhì)就是一個(gè)對(duì)象亏镰。 我們可以通過(guò)prototype做兩件事情:

  • 訪問(wèn)一個(gè)共享的原型對(duì)象扯旷,也叫代理原型
  • 克隆一個(gè)原型

代理原型

我們先把原型編程這一概念弄明白,在大多數(shù)的面向?qū)ο蟮恼Z(yǔ)言中索抓,對(duì)象就跟模具一樣钧忽,我們根據(jù)模具來(lái)制造對(duì)象毯炮,在JavaScript中,不是這樣的耸黑,通過(guò)prototype給object賦值一個(gè)原型或?qū)ο筇壹澹缓笤谛庐a(chǎn)生的對(duì)象身上做修改,也就是說(shuō)新對(duì)象獲取了原型的數(shù)據(jù)大刊。這就是原型編程思想为迈。

在JavaScript中,對(duì)象內(nèi)部都有一個(gè)原型對(duì)象缺菌,當(dāng)一個(gè)對(duì)象查詢某個(gè)屬性或方法的時(shí)候葫辐,JavaScript引擎首先會(huì)搜索對(duì)象本身是否答案,如果沒(méi)有伴郁,就會(huì)去它的原型對(duì)象中繼續(xù)搜索耿战,如果沒(méi)有,再去它的原型的原型中去找焊傅,這就形成了一個(gè)原型鏈剂陡。直到Object的prototype為止。

我們看一段代碼:

if (!Object.create) { 
    Object.create = function (o) {
        if (arguments.length > 1) {
            throw new Error('Object.create implementation' + ' only accepts the first parameter.');
        }
        function F() {} 
        F.prototype = o; 
        return new F();
    }; 
}

Object.create可用于創(chuàng)建一個(gè)對(duì)象狐胎,它的函數(shù)原型中接受兩個(gè)參數(shù)鸭栖,第一個(gè)參數(shù)是原型對(duì)象,表示新建的對(duì)象的原型顽爹,必填纤泵,第二個(gè)參數(shù)是屬性數(shù)組,表示新建對(duì)象的屬性镜粤。它在ES5中被引入,因此上邊的代碼是考慮到兼容問(wèn)題的玻褪。

通過(guò)上邊的代碼可以看出來(lái)肉渴,其內(nèi)部創(chuàng)建了一個(gè)F構(gòu)造器,然后把原型參數(shù)通過(guò)F.prototype = o進(jìn)行賦值带射,最后使用構(gòu)造器生成一個(gè)對(duì)象同规。因此我們得出下邊幾個(gè)結(jié)論:

  • 使用F.prototype = o這樣的方法給對(duì)象的原型賦值

  • new F()是怎么的過(guò)程?

      var a = {};
      a.__proto__ = F.prototype;
      F.call(a);
    

有一個(gè)很容易讓人迷惑的地方窟社,我們先看代碼:

var a = new Object();
console.log(a.prototype); // => undefined
console.log(a.__proto__); // => {}

在上邊我們不是解釋過(guò)了嗎券勺?一個(gè)對(duì)象內(nèi)部默認(rèn)會(huì)指向一個(gè)原型,但是為什么上邊第二行代碼打印的結(jié)果是undefined呢灿里?

這就引出了__proto__的概念吞杭,我覺(jué)得這篇文章寫(xiě)的不錯(cuò)告材。上邊的代碼輸出為undefined,但是__proto__卻有值,說(shuō)明每個(gè)對(duì)象內(nèi)部真正創(chuàng)建的原型是__proto__,而prototype只起到了輔助的作用,完全可以把他當(dāng)做一個(gè)普通的屬性來(lái)看待。當(dāng)使用Object.create方法創(chuàng)建對(duì)象的時(shí)候,參數(shù)中的原型會(huì)賦值給新對(duì)象的__proto__而不是而prototype。

再來(lái)看段代碼:

var switchProto = {
    isOn: function isOn() {
        return this.state;
    },
    toggle: function toggle() {
        this.state = !this.state;
        return this;
    },
    meta: {
        name: "James"
    },
    state: false
},
    switch1 = Object.create(switchProto),
    switch2 = Object.create(switchProto);

var state1 = switch1.toggle().isOn();
console.log(state1);

var state2 = switch2.isOn();
console.log(state2);

console.log(switchProto.isOn());

/*當(dāng)改變一個(gè)對(duì)象或者數(shù)組中的屬性時(shí),會(huì)有影響桶错,這說(shuō)明了這兩個(gè)賦值的方式不是copy。*/
switch1.meta.name = "Bond";
console.log(switch2.meta.name);

switch2.meta = { name: "zhangsan"};
console.log(switch1.meta.name);

上邊代碼中的switch1和switch2都指向了同一個(gè)原型,當(dāng)執(zhí)行完var state1 = switch1.toggle().isOn();后见转,state1的結(jié)果為true,而state2為false蒜哀,這也正好驗(yàn)證了上邊解釋的JavaScript尋找屬性或方法的原理斩箫。查找會(huì)使用原型鏈,賦值則不一樣撵儿,如果沒(méi)有該屬性校焦,直接在對(duì)象內(nèi)部創(chuàng)建該屬性,這時(shí)候跟原型沒(méi)關(guān)系统倒。

如果修改原型中的對(duì)象或數(shù)組時(shí)寨典,需要特別注意,會(huì)對(duì)原型產(chǎn)生副作用房匆,但是對(duì)對(duì)象或數(shù)組直接賦值不會(huì)產(chǎn)生影響耸成。因此在原型中使用對(duì)象或數(shù)組時(shí),要十分小心浴鸿。

原型克隆

原型編程也是有缺點(diǎn)的井氢,如果兩個(gè)對(duì)象共享了同一原型,那么更改原型內(nèi)容的話會(huì)影響到其他的對(duì)象岳链,我們看一個(gè)例子:

var testPrototype = {
    name: "James",
    obj: {
        objName: "objName"
    }
};
var b = Object.create(testPrototype);
var c = Object.create(testPrototype);
b.obj.objName = "Test";

console.log(c.obj.objName); // => Test

上邊的代碼演示了共享數(shù)據(jù)造成的問(wèn)題花竞,只有理解了如何觸發(fā)這些問(wèn)題,才能更好的避免錯(cuò)誤的發(fā)生掸哑。我們解決上邊出現(xiàn)的問(wèn)題的思路就是采用值拷貝约急,復(fù)制一份數(shù)據(jù)。

. extend()方法在jQuery和Underscore中都存在苗分,它的作用就是實(shí)現(xiàn)原型的拷貝厌蔽。我們看看Underscore內(nèi)部的實(shí)現(xiàn)方法:

_.extend = function(obj) { 
    each(slice.call(arguments, 1), function(source) {
    
        for (var prop in source) { 
            obj[prop] = source[prop];
        } 
    });
    return obj; 
};

each函數(shù)會(huì)取出對(duì)象中的每一個(gè)屬性,然后賦值給source摔癣,最終把所有的屬性賦值給了obj奴饮。我們就不做其他演示了,這種通過(guò)遍歷屬性择浊,然后賦值的方法原理是直接屬性賦值戴卜,因此我們說(shuō)這種方式?jīng)]有使用原型繼承。

這種deepCopy的思想在不同語(yǔ)言中還是比較重要的一個(gè)思想琢岩,在JavaScript中投剥,我們應(yīng)該使用代理原型來(lái)共享那些公共的屬性,使用原型拷貝來(lái)操縱獨(dú)享的數(shù)據(jù)粘捎,這是一條基本的編程原則薇缅。

The Flyweight Pattern(享元模式)

假如有一組屬性和方法是被很多實(shí)例對(duì)象共享的危彩,把他們?cè)O(shè)計(jì)成可復(fù)用的模式就是享元模式,相對(duì)于給每一個(gè)實(shí)例對(duì)象大量相同的屬性和方法泳桦,享元模式大大提高了內(nèi)存性能汤徽。

而JavaScript的原型非常完美的契合享元模式。假如我們要開(kāi)發(fā)一款游戲灸撰,游戲中的每一個(gè)敵人都有一些共有的屬性谒府,比如名字,位置浮毯,血量值完疫,還有一些共有的方法,比如攻擊债蓝,防御等等壳鹤。如果我們每創(chuàng)建出一個(gè)敵人對(duì)象都要把這些屬性進(jìn)行賦值,無(wú)疑會(huì)造成大量的性能問(wèn)題饰迹。我們看看下邊這段程序:

var enemyPrototype = {
    name: "James",
    position: {
        x: 0,
        y: 0
    },
    setPosition: function setPosition(x, y) {
        this.position = {
            x: x,
            y: y
        };
        return this;
    },
    health: 20,
    bite: function bite() {

    },
    evade: function evade() {

    }
},
    spawnEnemy = function () {
        return Object.create(enemyPrototype);
    };

var james1 = spawnEnemy();
var james2 = spawnEnemy();

james1.health = 5;
console.log(james2.__proto__);
console.log(james2.health);
console.log(james1.__proto__.health);

james1.setPosition(10, 10);
console.log(james1.position);
console.log(james1.__proto__.position);
console.log(james2.position);

james1和james2這兩個(gè)敵人共享了一個(gè)enemyPrototype芳誓,這也算是一個(gè)默認(rèn)配置。修改了一個(gè)的屬性啊鸭,不會(huì)影響其他對(duì)象的屬性锹淌。

值得注意的一點(diǎn)是,在上邊我們也提到了修改原型中的Object或數(shù)組一定要小心赠制,那么在這個(gè)例子中赂摆,我們通過(guò)setPosition這個(gè)函數(shù)解決了這個(gè)問(wèn)題,核心就是這個(gè)函數(shù)中的this關(guān)鍵字钟些。

Object Creation(創(chuàng)建對(duì)象)

關(guān)于JavaScript中對(duì)象的創(chuàng)建烟号,我在這里談兩點(diǎn):一種是使用構(gòu)造器,另一種是使用字面量厘唾。

我們之前也提到過(guò)褥符,使用構(gòu)造器初始化對(duì)象是很面向?qū)ο?/code>的編程思想,這在JavaScript中并不推薦抚垃,原因是它不能很好的利用原型這一利器。我們看個(gè)例子:

function Car(color, direction, mph) {
    var isParkingBrakeOn = false;
    this.color = color || "black";
    this.direction = direction || 0;
    this.mph = mph || 0;

    this.gas = function gas(amount) {
        amount = amount || 10;
        this.mph += amount;
        return this;
    };

    this.brake = function brake(amount) {
        amount = amount || 10;
        this.mph = (this.mph - amount) < 0 ? 0 : this.mph - amount;
        return this;
    };

    this.toggleParkingBrake = function toggleParkingBrake() {
        isParkingBrakeOn = !isParkingBrakeOn;
        return this;
    };

    this.isParked = function isParked() {
        return isParkingBrakeOn;
    }
}

var car  = new Car();
console.log(car.color); // => black
console.log(car.gas(30).mph); // => 30
console.log(car.brake(20).mph); // => 10
console.log(car.toggleParkingBrake().isParked()); // => true

仔細(xì)觀察上邊的代碼趟大,可以總結(jié)出下邊幾點(diǎn):

  • 在設(shè)計(jì)構(gòu)造器函數(shù)的時(shí)候鹤树,函數(shù)名第一個(gè)字母要大寫(xiě),創(chuàng)建對(duì)象時(shí)要使用new關(guān)鍵字
  • 函數(shù)內(nèi)部對(duì)外暴露的屬性逊朽,方法使用this關(guān)鍵字
  • 函數(shù)內(nèi)部可以添加私有變量

最重要的是理解new Object()這一過(guò)程的原理罕伯,再次強(qiáng)調(diào)一下:

var a = {};
a.__proto__ = F.prototype;
F.call(a);

另一種創(chuàng)建方式是使用字面量:

var myCar =  {
    isParkingBrakeOn: false,
    color: "black",
    direction: 0,
    mph: 0,

    gas: function gas(amount) {
        amount = amount || 10;
        this.mph += amount;
        return this;
    },

    brake: function brake(amount) {
        amount = amount || 10;
        this.mph = (this.mph - amount) < 0 ? 0 : this.mph - amount;
        return this;
    },

    toggleParkingBrake: function toggleParkingBrake() {
        this.isParkingBrakeOn = !this.isParkingBrakeOn;
        return this;
    },

    isParked: function isParked() {
        return this.isParkingBrakeOn;
    }
}

console.log(myCar.color); // => black
console.log(myCar.gas(30).mph); // => 30
console.log(myCar.brake(20).mph); // => 10
console.log(myCar.toggleParkingBrake().isParked()); // => true

代碼稍微做了改變,實(shí)現(xiàn)的效果一模一樣叽讳。有一個(gè)缺點(diǎn)是追他,不能使用私有變量坟募,如果我要生成多個(gè)Car對(duì)象,需要反復(fù)的寫(xiě)上邊的代碼邑狸。那么我們應(yīng)該如何批量的生產(chǎn)對(duì)象呢懈糯?答案就在下一個(gè)小結(jié)。

Factories (工廠方法)

我們本篇討論的主要內(nèi)容就是Object单雾,上邊我們已經(jīng)提到了使用字面量的方式創(chuàng)建對(duì)象有一個(gè)最大的缺點(diǎn)就是無(wú)法使用私有變量赚哗,我們可以使用工廠方法完美解決這個(gè)問(wèn)題。

工廠方法本質(zhì)上就是一個(gè)函數(shù)硅堆,函數(shù)的返回值就是我們想要?jiǎng)?chuàng)建的對(duì)象屿储。這個(gè)函數(shù)就像工廠一個(gè)樣能夠批量生產(chǎn)出規(guī)格一樣的的產(chǎn)品。

var car = function car(color, direction, mph) {
    var  isParkingBrakeOn = false;
    return {
        color: "black",
        direction: 0,
        mph: 0,

        gas: function gas(amount) {
            amount = amount || 10;
            this.mph += amount;
            return this;
        },

        brake: function brake(amount) {
            amount = amount || 10;
            this.mph = (this.mph - amount) < 0 ? 0 : this.mph - amount;
            return this;
        },

        toggleParkingBrake: function toggleParkingBrake() {
            this.isParkingBrakeOn = !this.isParkingBrakeOn;
            return this;
        },

        isParked: function isParked() {
            return this.isParkingBrakeOn;
        }
    }

}

var myCar = car();

console.log(myCar.color); // => black
console.log(myCar.gas(30).mph); // => 30
console.log(myCar.brake(20).mph); // => 10
console.log(myCar.toggleParkingBrake().isParked()); // => true

我們把之前的代碼稍作修改渐逃,就成了一個(gè)工廠方法够掠,每當(dāng)調(diào)用car()就會(huì)產(chǎn)生一個(gè)對(duì)象,這就是工廠方法茄菊,他相比于構(gòu)造器的優(yōu)勢(shì)就在于不需要使用new關(guān)鍵字疯潭。

到目前為止,我們已經(jīng)可以使用3中方式創(chuàng)建對(duì)象了:

  • 構(gòu)造器
  • 字面量
  • 工廠方法

還有一個(gè)好玩的事情就是在工廠方法中使用原型买羞,一定要記住的一點(diǎn)是袁勺,新建的對(duì)象會(huì)繼承原型中的所有屬性和方法。

var carPrototype = {
        gas: function gas(amount) {
            amount = amount || 10; this.mph += amount; return this;
        },
        brake: function brake(amount) {
            amount = amount || 10;
            this.mph = ((this.mph - amount) < 0)? 0
                : this.mph - amount; return this;
        },
        color: 'pink',
        direction: 0,
        mph: 0
    },
    car = function car(options) {
        return extend(Object.create(carPrototype), options);
    },
    myCar = car({
        color: 'red'
    });

console.log(myCar.color);

上邊這種方式最大的優(yōu)點(diǎn)就是在創(chuàng)建對(duì)象時(shí)畜普,可以為新建對(duì)象自由擴(kuò)展屬性和方法期丰,這主要得益于extend函數(shù)的作用。

JavaScript是一門(mén)動(dòng)態(tài)語(yǔ)言吃挑,我們可以使用下邊的方法給carPrototype動(dòng)態(tài)的擴(kuò)展屬性和方法:

extend(carPrototype, {
    name: 'Porsche',
    color: 'black',
    mph: 220
});

Stamps

Stamps是一個(gè)JavaScript庫(kù)钝荡,他模仿了其他面向?qū)ο笳Z(yǔ)言中的類。通常我們定義了一個(gè)類舶衬,類里邊有屬性埠通,方法,初始化方法逛犹,有時(shí)候某個(gè)屬性還可能是另一個(gè)類端辱。我們看看Stamps的示例代碼:

const MyStamp = stampit()       // create new empty stamp
.props({                        // add properties to your future objects
  myProp: 'default value'
})
.methods({                      // add methods to your future objects
  getMyProp() {
    return this.myProp;
  }
})
.init(function ({value}) {      // add initializers to your future objects
  this.myProp = value || this.myProp;
})
.compose(AnotherStamp);         // add other stamp behaviours to your objects

console.log(typeof MyStamp);                            // 'function'
console.log(MyStamp());                                 // { myProp: 'default value' }

console.log(typeof MyStamp().getMyProp);                // 'function'
console.log(MyStamp().getMyProp());                     // default value

console.log(MyStamp({value: 'new value'}));             // { myProp: 'new value' }
console.log(MyStamp({value: 'new value'}).getMyProp()); // new value

MyStamp的風(fēng)格跟面向?qū)ο蟮念愂窒嗨疲?strong>謹(jǐn)記一點(diǎn),MyStamp是一個(gè)函數(shù)虽画,

// Some privileged methods with some private data.
const Availability = stampit().init(function() {
  var isOpen = false; // private

  this.open = function open() {
    isOpen = true;
    return this;
  };
  this.close = function close() {
    isOpen = false;
    return this;
  };
  this.isOpen = function isOpenMethod() {
    return isOpen;
  }
});

上邊的代碼創(chuàng)建了一個(gè)Availability對(duì)象舞蔽,在設(shè)計(jì)上,我們給這個(gè)對(duì)象提供了開(kāi)和關(guān)這兩個(gè)方法码撰,并且提供了一個(gè)獲取當(dāng)前狀態(tài)的函數(shù)isOpen渗柿,var isOpen是一個(gè)私有變量,用于保存狀態(tài)信息脖岛。

// Here's a stamp with public methods, and some state:
const Membership = stampit({
  methods: {
    add(member) {
      this.members[member.name] = member;
      return this;
    },
    getMember(name) {
      return this.members[name];
    }
  },
  properties: {
    members: {}
  }
});

這段代碼中朵栖,我們?cè)O(shè)計(jì)了一個(gè)會(huì)員管理類颊亮,提供了兩個(gè)公共的方法add getMember和一個(gè)公共的屬性members。這些東西在外部都是可以訪問(wèn)的陨溅。

// Let's set some defaults:
const Defaults = stampit({
  init({name, specials}) {
    this.name = name || this.name;
    this.specials = specials || this.specials;
  },
  properties: {
    name: 'The Saloon',
    specials: 'Whisky, Gin, Tequila'
  }
});

這段代碼為了演示給對(duì)像一個(gè)默認(rèn)值终惑,init({name, specials})這行代碼我不太理解,{name, specials}這是什么意思声登?不會(huì)報(bào)錯(cuò)狠鸳?

// Classical inheritance has nothing on this.
// No parent/child coupling. No deep inheritance hierarchies.
// Just good, clean code reusability.
const Bar = stampit(Defaults, Availability, Membership);

// Create an object instance
const myBar = Bar({name: 'Moe\'s'});

// Silly, but proves that everything is as it should be.
myBar.add({name: 'Homer'}).open().getMember('Homer');

重要的事情說(shuō)三遍、重要的事情說(shuō)三遍悯嗓、重要的事情說(shuō)三遍件舵,使用stampit最大的優(yōu)點(diǎn)就是??????沒(méi)有繼承,完全是組合的思想??????脯厨。

源碼等以后學(xué)的差不多了再研究一下铅祸。

總結(jié)

學(xué)html,css合武,js有段時(shí)間了临梗,看的基本都是國(guó)外的書(shū)籍。我發(fā)現(xiàn)如果只是讀一讀稼跳,不把內(nèi)容寫(xiě)成文章的話盟庞,很快就忘了。在學(xué)習(xí)新知識(shí)的同時(shí)也得到了很多靈感汤善,這對(duì)目前的主業(yè)也很有幫助什猖,

但是我有一個(gè)問(wèn)題,哪里有那種整個(gè)web的源碼呢红淡?暫時(shí)定的目標(biāo)是模仿一個(gè)網(wǎng)站不狮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市在旱,隨后出現(xiàn)的幾起案子摇零,更是在濱河造成了極大的恐慌,老刑警劉巖桶蝎,帶你破解...
    沈念sama閱讀 211,265評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件驻仅,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡登渣,警方通過(guò)查閱死者的電腦和手機(jī)雾家,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)绍豁,“玉大人,你說(shuō)我怎么就攤上這事牙捉≈褡幔” “怎么了敬飒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,852評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)芬位。 經(jīng)常有香客問(wèn)我无拗,道長(zhǎng),這世上最難降的妖魔是什么昧碉? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,408評(píng)論 1 283
  • 正文 為了忘掉前任英染,我火速辦了婚禮,結(jié)果婚禮上被饿,老公的妹妹穿的比我還像新娘四康。我一直安慰自己,他們只是感情好狭握,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布闪金。 她就那樣靜靜地躺著,像睡著了一般论颅。 火紅的嫁衣襯著肌膚如雪哎垦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,772評(píng)論 1 290
  • 那天恃疯,我揣著相機(jī)與錄音漏设,去河邊找鬼。 笑死今妄,一個(gè)胖子當(dāng)著我的面吹牛郑口,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛙奖,決...
    沈念sama閱讀 38,921評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼潘酗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了雁仲?” 一聲冷哼從身側(cè)響起仔夺,我...
    開(kāi)封第一講書(shū)人閱讀 37,688評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎攒砖,沒(méi)想到半個(gè)月后缸兔,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,130評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吹艇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評(píng)論 2 325
  • 正文 我和宋清朗相戀三年惰蜜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片受神。...
    茶點(diǎn)故事閱讀 38,617評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抛猖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情财著,我是刑警寧澤联四,帶...
    沈念sama閱讀 34,276評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站撑教,受9級(jí)特大地震影響朝墩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜伟姐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評(píng)論 3 312
  • 文/蒙蒙 一收苏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧愤兵,春花似錦鹿霸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,740評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至矫夷,卻和暖如春葛闷,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背双藕。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,967評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工淑趾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人忧陪。 一個(gè)月前我還...
    沈念sama閱讀 46,315評(píng)論 2 360
  • 正文 我出身青樓扣泊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親嘶摊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子延蟹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評(píng)論 2 348

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