你不知道的JavaScript(五)|this和對(duì)象原型

[[Prototype]]
JavaScript中的對(duì)象有一個(gè)特殊的[[Prototype]]內(nèi)置屬性桨武,其實(shí)就是對(duì)于其他對(duì)象的引用。幾乎所有的對(duì)象在創(chuàng)建時(shí)[[Prototype]]屬性都會(huì)被賦予一個(gè)非空的值锄贼。

var myObject = {
    a: 2
};
myObject.a; // 2

[[Prototype]]引用由什么用呢?當(dāng)你試圖引用對(duì)象的屬性時(shí)會(huì)觸發(fā)[[Get]]操作雷猪,比如myObject.a票从。對(duì)于默認(rèn)的[[Get]]操作來說,第一步是檢查對(duì)象本身是都有這個(gè)屬性罩抗,如果有的話就使用它拉庵。但是如果a不在myObject中,就需要使用對(duì)象的[[Prototype]]鏈了套蒂。
對(duì)于默認(rèn)的[[Get]]操作來說钞支,如果無法在對(duì)象本身找到需要的屬性,就會(huì)繼續(xù)訪問對(duì)象的[[Prototype]]鏈:

var anotherObject = {
    a: 2
};
// 創(chuàng)建一個(gè)關(guān)聯(lián)到anotherObject 的對(duì)象
var myObject = Object.create(anotherObject);
myObject.a; // 2

現(xiàn)在myObject對(duì)象的[[Prototype]]關(guān)聯(lián)到了anotherObject操刀。顯然myObject.a并不存在烁挟,但是盡管如此,屬性訪問仍然成功地(在anotherObject中)找到了值2骨坑。但是撼嗓,如果anotherObject中也找不到a并且[[Prototype]]鏈不為空的話,就會(huì)繼續(xù)查找下去。這個(gè)過程會(huì)持續(xù)到找到匹配的屬性名或者查找完整條[[Prototype]]鏈静稻。如果是后者的話警没,[[Get]]操作的返回值是undefined。

Object.prototype
所有普通的[[Prototype]]鏈最終都會(huì)指向內(nèi)置的Object.prototype振湾。由于所有的“普通”對(duì)象都“源于”(或者說把[[Prototype]]鏈的頂端設(shè)置為)這個(gè)Object.prototype對(duì)象杀迹,所有它包含JavaScript中許多通用的功能。

屬性設(shè)置和屏蔽
給一個(gè)對(duì)象設(shè)置屬性并不僅僅是添加一個(gè)新屬性或者修改已有的屬性值押搪。

myObject.foo = "bar";

如果myObject對(duì)象中包含名為foo的普通數(shù)據(jù)訪問屬性树酪,這條賦值語句只會(huì)修改已有的屬性值。
如果foo不是直接存在于myObject中大州,[[Prototype]]鏈就會(huì)被遍歷续语,類似[[Get]]操作。如果原型鏈上找不到foo厦画,foo就會(huì)被直接添加到myObject上疮茄。
然而,如果foo存在于原型鏈上層根暑,賦值語句myObject.foo="foo"的行為就會(huì)有些不同力试。
如果屬性名foo既出現(xiàn)在myObject中也出現(xiàn)在myObject的[[Prototype]]鏈上層,那么就會(huì)發(fā)生屏蔽排嫌。myObject中包含的foo屬性會(huì)屏蔽原型鏈上層的所有foo屬性畸裳,因?yàn)閙yObject.foo總是會(huì)選擇原型鏈中最低層的foo屬性。
屏蔽比我們想想中更加復(fù)雜淳地。下面分析下如果foo不直接存在于myObject中而是存在于原型鏈上層時(shí)myObject.foo=“bar”會(huì)出現(xiàn)的三種情況:
1怖糊、如果在[[Prototype]]鏈上層存在名為foo的普通數(shù)據(jù)訪問屬性并且沒有被標(biāo)記為只讀(writable:false),那就會(huì)直接在myObject中添加一個(gè)名為foo的新屬性颇象,它是屏蔽屬性伍伤。
2、如果在[[Prototype]]鏈上層存在foo遣钳,但是它被標(biāo)記為只讀(writable:false)嚷缭,那么無法修改已有屬性或者在myObject上創(chuàng)建屏蔽屬性。如果運(yùn)行在嚴(yán)格模式下耍贾,代碼會(huì)拋出一個(gè)錯(cuò)誤。否則路幸,這條賦值語句會(huì)被忽略荐开,總之,不會(huì)發(fā)生屏蔽简肴。
3晃听、如果在[[Prototype]]鏈上層存在foo并且它是一個(gè)setter,那就一定會(huì)調(diào)用這個(gè)setter。foo不會(huì)被添加到(或者說屏蔽于)myObject能扒,也不會(huì)重新定義foo這個(gè)setter佣渴。
如果你希望在第二種和第三中情況下也屏蔽foo,那就不能使用=操作符來賦值初斑,而是使用Object.defineProperty(..)來向myObject添加foo辛润。
有些情況下回隱式產(chǎn)生屏蔽,一定要當(dāng)心:

var anotherObject = {
    a: 2
};
var myObject = Object.create(anotherObject);
anotherObject.a; // 2
myObject.a; // 2
anotherObject.hasOwnProperty("a"); // true
myObject.hasOwnProperty("a"); // false
myObject.a++; // 隱式屏蔽见秤!
anotherObject.a; // 2
myObject.a; // 3
myObject.hasOwnProperty("a"); // true

盡管myObject.a++看起來應(yīng)該(通過委托)查找并增加anotherObject.a屬性砂竖,但是別忘了++操作相當(dāng)于myObject.a=myObject.a+1。因此++操作首先會(huì)通過[[Prototype]]查找屬性a并從anotherObject.a獲取當(dāng)前屬性值2鹃答,然后給這個(gè)值加1乎澄,接著用[[Put]]將值3賦給myObject中新建的屏蔽屬性a。
修改委托屬性時(shí)一定要小心测摔。如果想讓anotherObject.a的值增加置济,唯一的辦法是anotherObject.a++。

“類函數(shù)”
JavaScript中有一種奇怪的行為一直在被無恥地濫用锋八,那就是模仿類浙于。這種奇怪的“類似類”的行為利用了函數(shù)的一種特殊特性:所有的函數(shù)默認(rèn)都會(huì)擁有一個(gè)名為Prototype的共有并且不可枚舉的屬性,它會(huì)指向另一個(gè)對(duì)象:

function Foo() {
    // ...
}
Foo.prototype; // { }

這個(gè)對(duì)象通常被稱為Foo的原型查库,因?yàn)槲覀兺ㄟ^名為Foo.Prototype的屬性引用來訪問它路媚。這個(gè)對(duì)象到底是什么?最直接的解釋就是樊销,這個(gè)對(duì)象是在調(diào)用new Foo()時(shí)創(chuàng)建的整慎,最后會(huì)被(有點(diǎn)武斷地)關(guān)聯(lián)到這個(gè)“Foo點(diǎn)prototype”對(duì)象上。

function Foo() {
    // ...
}
var a = new Foo();
Object.getPrototypeOf(a) === Foo.prototype; // true

調(diào)用new Foo()時(shí)會(huì)創(chuàng)建a围苫,其中的一步就是給a一個(gè)內(nèi)部的[[Prototype]]鏈接裤园,關(guān)聯(lián)到Foo.prototype指向的那個(gè)對(duì)象。實(shí)際上剂府,絕大多數(shù)JavaScript開發(fā)者不知道的秘密是:new Foo()這個(gè)函數(shù)調(diào)用實(shí)際上并沒有直接創(chuàng)建關(guān)聯(lián)拧揽,這個(gè)關(guān)聯(lián)只是一個(gè)意外的副作用。new Foo()只是間接完成了我們的目標(biāo):一個(gè)關(guān)聯(lián)到其他對(duì)象的新對(duì)象腺占。
繼承意味著復(fù)制操作淤袜,JavaScript(默認(rèn))并不會(huì)復(fù)制對(duì)象屬性。相反衰伯,JavaScript會(huì)在兩個(gè)對(duì)象之間創(chuàng)建一個(gè)關(guān)聯(lián)铡羡,這樣一個(gè)對(duì)象就可以通過委托訪問另一個(gè)對(duì)象的屬性和函數(shù)。委托這個(gè)術(shù)語可以更加準(zhǔn)確滴描述JavaScript中對(duì)象的關(guān)聯(lián)機(jī)制意鲸。還有個(gè)偶爾會(huì)用到的JavaScript術(shù)語差異繼承烦周【”基本原則是在描述對(duì)象行為時(shí),使用其不同于普遍描述的特性读慎。

function Foo() {
    // ...
}
Foo.prototype.constructor === Foo; // true
var a = new Foo();
a.constructor === Foo; // true

Foo.prototype默認(rèn)(在代碼中第一行聲明時(shí)J)有一個(gè)公有并且不可枚舉的屬性.constructor,這個(gè)屬性引用的是對(duì)象關(guān)聯(lián)的函數(shù)夭委。此外幅狮,可以看到通過“構(gòu)造函數(shù)”調(diào)用new Foo()創(chuàng)建的對(duì)象也有一個(gè).constructor屬性,指向“創(chuàng)建這個(gè)對(duì)象的函數(shù)”闰靴。
上一段代碼很容易讓人認(rèn)為Foo是一個(gè)構(gòu)造函數(shù)彪笼,因?yàn)槲覀兪褂胣ew來調(diào)用它并且看到它“構(gòu)造”了一個(gè)對(duì)象。實(shí)際上蚂且,F(xiàn)oo和你程序中的其他函數(shù)沒有任何區(qū)別配猫。函數(shù)本身并不是構(gòu)造函數(shù),然而杏死,當(dāng)你在普通的函數(shù)調(diào)用前面加上new關(guān)鍵字之后泵肄,就會(huì)把這個(gè)函數(shù)調(diào)用變成一個(gè)“構(gòu)造函數(shù)調(diào)用”。實(shí)際上淑翼,new會(huì)劫持所有普通函數(shù)并用構(gòu)造對(duì)象的形式來調(diào)用它腐巢。換句話說,在JavaScript中對(duì)于“構(gòu)造函數(shù)”最準(zhǔn)確的解釋是玄括,所有帶new的函數(shù)調(diào)用冯丙。

技術(shù)

function Foo(name) {
    this.name = name;
}
Foo.prototype.myName = function () {
    return this.name;
};
var a = new Foo("a");
var b = new Foo("b");
a.myName(); // "a"
b.myName(); // "b"

在這段代碼中,看起來似乎創(chuàng)建a和b時(shí)會(huì)把Foo.prototype對(duì)象復(fù)制到這兩個(gè)對(duì)象中遭京,然而事實(shí)并不是這樣胃惜。在前面介紹默認(rèn)[[Get]]算法時(shí)介紹過[[Prototype]]鏈,以及當(dāng)屬性不直接存在于對(duì)象中時(shí)如何通過它來進(jìn)行查找哪雕。
因此船殉,在創(chuàng)建的過程中,a和b的內(nèi)部[[Prototype]]都會(huì)關(guān)聯(lián)到Foo.prototype上斯嚎。但a和b中無法找到myName時(shí)利虫,它會(huì)(通過委托)在Foo.prototype上找到。

回顧“構(gòu)造函數(shù)”
之前討論.constructor屬性時(shí)我們說過堡僻,看起來a.constructor===Foo為真意味著a確實(shí)有一個(gè)指向Foo的.constructor屬性糠惫,但是事實(shí)不是這樣。實(shí)際上钉疫,.constructor引用同樣被委托給了Foo.prototype硼讽,而Foo.prototype.constructor默認(rèn)指向Foo。
Foo.prototype的.constructor屬性只是Foo函數(shù)在聲明時(shí)的默認(rèn)屬性陌选。如果你創(chuàng)建了一個(gè)新對(duì)象并替換了函數(shù)默認(rèn)的.prototype對(duì)象引用理郑,那么新對(duì)象并不會(huì)自動(dòng)獲得.constructor屬性。

function Foo() { /* .. */ }
Foo.prototype = { /* .. */ }; // 創(chuàng)建一個(gè)新原型對(duì)象
var a1 = new Foo();
a1.constructor === Foo; // false!
a1.constructor === Object; // true!

a1并沒有.constructor屬性咨油,所以它會(huì)委托[[Prototype]]鏈上的Foo.prototype您炉。但是這個(gè)對(duì)象也沒有.constructor屬性(不過默認(rèn)的Foo.prototype對(duì)象有這個(gè)屬性!)役电,所以它會(huì)繼續(xù)委托赚爵,這次會(huì)委托給委托鏈頂端的Object.prototype。這個(gè)對(duì)象有.constructor屬性法瑟,指向內(nèi)置的Object(..)函數(shù)冀膝。
當(dāng)然,你可以給Foo.prototype添加一個(gè).constructor屬性霎挟,不過這需要手動(dòng)添加一個(gè)符合正常行為的不可枚舉屬性窝剖。

function Foo() { /* .. */ }
Foo.prototype = { /* .. */ }; // 創(chuàng)建一個(gè)新原型對(duì)象
// 需要在Foo.prototype 上“修復(fù)”丟失的.constructor 屬性
// 新對(duì)象屬性起到Foo.prototype 的作用
Object.defineProperty(Foo.prototype, "constructor", {
    enumerable: false,
    writable: true,
    configurable: true,
    value: Foo // 讓.constructor 指向Foo
});

原型風(fēng)格

function Foo(name) {
    this.name = name;
}
Foo.prototype.myName = function () {
    return this.name;
};
function Bar(name, label) {
    Foo.call(this, name);
    this.label = label;
}
// 我們創(chuàng)建了一個(gè)新的Bar.prototype 對(duì)象并關(guān)聯(lián)到Foo.prototype
Bar.prototype = Object.create(Foo.prototype);
// 注意!現(xiàn)在沒有Bar.prototype.constructor 了
// 如果你需要這個(gè)屬性的話可能需要手動(dòng)修復(fù)一下它
Bar.prototype.myLabel = function () {
    return this.label;
};
var a = new Bar("a", "obj a");
a.myName(); // "a"
a.myLabel(); // "obj a"

這段代碼的核心部分就是語句Bar.prototype = Object.create( Foo.prototype )酥夭。調(diào)用Object.create(..)會(huì)憑空創(chuàng)建一個(gè)“新”對(duì)象并把新對(duì)象內(nèi)部的[[Prototype]]關(guān)聯(lián)到你指定的對(duì)象(本例中是Foo.prototype)赐纱。換句話說,這條語句的意思是:“創(chuàng)建一個(gè)新的Bar.prototype對(duì)象并把它關(guān)聯(lián)到Foo.prototype”熬北。
注意:下面這兩種方式是常見的錯(cuò)誤做法疙描,實(shí)際上它們都存在一些問題:

// 和你想要的機(jī)制不一樣!
Bar.prototype = Foo.prototype;
// 基本上滿足你的需求讶隐,但是可能會(huì)產(chǎn)生一些副作用 :(
Bar.prototype = new Foo();

Bar.prototype=Foo.prototype并不會(huì)創(chuàng)建一個(gè)關(guān)聯(lián)到Bar.prototype的新對(duì)象起胰,它只是讓Bar.prototype直接引用Foo.prototype對(duì)象。因此當(dāng)你執(zhí)行類似Bar.prototype.
myLabel = ...的賦值語句時(shí)會(huì)直接修改Foo.prototype對(duì)象本身巫延。顯然這不是你想要的結(jié)果效五,否則你根本不需要Bar對(duì)象,直接使用Foo就可以了烈评,這樣代碼也會(huì)更簡(jiǎn)單一些火俄。
Bar.prototype = new Foo()的確會(huì)創(chuàng)建一個(gè)關(guān)聯(lián)到Bar.prototype的新對(duì)象。但是它使用了Foo(..)的“構(gòu)造函數(shù)調(diào)用”讲冠,如果函數(shù)Foo有一些副作用(比如寫日志瓜客、修改狀態(tài)、注冊(cè)到其他對(duì)象竿开,給this添加數(shù)據(jù)屬性谱仪,等等)的話,就會(huì)影響到Bar()的“后代”否彩,后果不堪設(shè)想疯攒。
因此,要?jiǎng)?chuàng)建一個(gè)合適的關(guān)聯(lián)對(duì)象列荔,我們必須使用Object.create(..)而不是使用具有副作用的Foo(..)敬尺。這樣做唯一的缺點(diǎn)就是需要?jiǎng)?chuàng)建一個(gè)新對(duì)象然后把舊對(duì)象拋棄掉枚尼,不能直接修改已有的默認(rèn)對(duì)象。
如果能有一個(gè)標(biāo)準(zhǔn)并且可靠的方法來修改對(duì)象的[[Prototype]]關(guān)聯(lián)就好了砂吞。在ES6之前署恍,我們只能通過設(shè)置.proto屬性來實(shí)現(xiàn),但是這個(gè)方法并不是標(biāo)準(zhǔn)并且無法兼容所有瀏覽器蜻直。ES6添加了輔助函數(shù)Object.setPrototypeOf(..)盯质,可以用標(biāo)準(zhǔn)并且考考的方法來修改關(guān)聯(lián)。
對(duì)比下兩種把Bar.prototype關(guān)聯(lián)到Foo.prototype的方法:

// ES6 之前需要拋棄默認(rèn)的Bar.prototype
Bar.ptototype = Object.create( Foo.prototype );
// ES6 開始可以直接修改現(xiàn)有的Bar.prototype
Object.setPrototypeOf( Bar.prototype, Foo.prototype );

如果忽略掉Object.create(..)方法帶來的輕微性能損失(拋棄的對(duì)象需要進(jìn)行垃圾回收)概而,它實(shí)際上比ES6及其之后的方法更短并且可讀性更高呼巷。不過無論如何,這是兩種完全不同的語法赎瑰。

檢查“類”關(guān)系
假設(shè)有對(duì)象a王悍,如何找到對(duì)象a委托的對(duì)象(如果存在的話)呢?在傳統(tǒng)的面相類環(huán)境中乡范,檢查一個(gè)實(shí)例(JavaScript中的對(duì)象)的繼承(JavaScript中的委托關(guān)聯(lián))通常被稱為內(nèi)逝涿(或者反射)。

function Foo() {
    // ...
}
Foo.prototype.blah = ...;
var a = new Foo();

我們?nèi)绾瓮ㄟ^內(nèi)省找出a的“祖先”(委托關(guān)聯(lián))呢晋辆?第一種方法是站在“類”的角度來判斷:a instanceof Foo;//true
instanceof操作符的左操作數(shù)是一個(gè)普通的對(duì)象渠脉,右操作數(shù)是一個(gè)函數(shù)。instanceof回答的問題是:在a的整條[[Prototype]]鏈中是否有指向Foo.prototype的對(duì)象瓶佳?
可惜芋膘,這個(gè)方法只能處理對(duì)象(a)和函數(shù)(帶.prototype引用的Foo)之間的關(guān)系。如果你想判斷兩個(gè)對(duì)象(比如a和b)之間是否通過[[Prototype]]鏈關(guān)聯(lián)霸饲,只用instanceof無法實(shí)現(xiàn)为朋。
第二種判斷[[Prototype]]反射的方法,它更加簡(jiǎn)潔:

Foo.prototype.isPrototypeOf( a ); // true
//在a的整條[[Prototype]]鏈中是否出現(xiàn)過Foo.prototype?
// 非常簡(jiǎn)單:b 是否出現(xiàn)在c 的[[Prototype]] 鏈中厚脉?
b.isPrototypeOf( c );

我們也可以直接獲取一個(gè)對(duì)象的[[Prototype]]鏈习寸。在ES5中,標(biāo)準(zhǔn)的方法是:```JavaScript
Object.getPrototypeOf(a)

可以驗(yàn)證一下傻工,這個(gè)對(duì)象引用是否和我們想的一樣:
```JavaScript
Object.getPrototypeOf( a ) === Foo.prototype; // true

絕大多數(shù)(不是所有O枷)瀏覽器也支持一種非標(biāo)準(zhǔn)的方法來訪問內(nèi)部[[Prototype]]屬性:

a.__proto__ === Foo.prototype; // true

這個(gè)奇怪的.__proto__(在ES6之前并不是標(biāo)準(zhǔn)!)屬性“神奇地”引用了內(nèi)部的[[Prototype]]對(duì)象中捆,如果你想直接查找(甚至可以通過.__proto__.__ptoto__...來遍歷)原型鏈的話鸯匹,這個(gè)方法非常有用。
和我們之前說過的.constructor一樣泄伪,.proto實(shí)際上并不存在于你正在使用的對(duì)象中(本例中是a)殴蓬。實(shí)際上,它和其他的常用函數(shù)(.toString()蟋滴、.isPrototypeOf(..)染厅,等等)一樣痘绎,存在于內(nèi)置的Object.prototype中,它們是不可枚舉的肖粮。
.proto看起來很像一個(gè)屬性简逮,但是實(shí)際上它更像一個(gè)getter/setter。.proto的實(shí)現(xiàn)大致上是這樣的:

Object.defineProperty(Object.prototype, "__proto__", {
    get: function () {
        return Object.getPrototypeOf(this);
    },
    set: function (o) {
        // ES6 中的setPrototypeOf(..)
        Object.setPrototypeOf(this, o);
        return o;
    }
});

因此尿赚,訪問(獲取值)a.proto時(shí),實(shí)際上是調(diào)用了a.proto()(調(diào)用getter函數(shù))蕉堰。雖然getter函數(shù)存在于Object.prototype對(duì)象中凌净,但是它的this指向?qū)ο骯(this的綁定規(guī)則),所以和Object.getPrototypeOf(a)結(jié)果相同屋讶。

對(duì)象關(guān)聯(lián)
[[Prototype]]機(jī)制就是存在于對(duì)象中的一個(gè)內(nèi)部鏈接冰寻,它會(huì)引用其他對(duì)象。
通常來說皿渗,這個(gè)鏈接的作用是:如果在對(duì)象上沒有找到需要的屬性或者方法引用斩芭,引擎就會(huì)繼續(xù)在[[Prototype]]關(guān)聯(lián)的對(duì)象上進(jìn)行查找。同理乐疆,如果在后者中也沒有找到需要的引用就會(huì)繼續(xù)查找它的[[Prototype]]划乖,以此類推。這一系列對(duì)象的鏈接稱為“原型鏈”挤土。

創(chuàng)建關(guān)聯(lián)

var foo = {
    something: function () {
        console.log("Tell me something good...");
    }
};
var bar = Object.create(foo);
bar.something(); // Tell me something good...

Object.create(..)會(huì)創(chuàng)建一個(gè)新對(duì)象(bar)并把它關(guān)聯(lián)到我們指定的對(duì)象(foo)琴庵,這樣我們就可以充分發(fā)揮[[Prototype]]機(jī)制的威力(委托)并且避免不必要的麻煩(比如使用new的構(gòu)造函數(shù)調(diào)用會(huì)生成.prototype和.constructor引用)。
Object.create(null)會(huì)創(chuàng)建一個(gè)擁有空(或者說null)[[Prototype]]鏈接的對(duì)象仰美,這個(gè)對(duì)象無法進(jìn)行委托迷殿。由于這個(gè)對(duì)象沒有原型鏈,所以instanceof操作符無法進(jìn)行判斷咖杂,因此總是會(huì)返回false庆寺。這些特殊的空[[Prototype]]對(duì)象通常被稱作“字典”,它們完全不會(huì)受到原型鏈的干擾诉字,因此非常適合用來存儲(chǔ)數(shù)據(jù)懦尝。
Object.create()的polyfill代碼
Object.create(..)是在ES5中新增的函數(shù),所以在ES5之前的環(huán)境中(比如就IE)如果要支持這個(gè)功能的話就需要使用一段簡(jiǎn)單的polyfill代碼奏窑,它部分實(shí)現(xiàn)了Object.create(..)的功能:

if (!Object.create) {
    Object.create = function (o) {
        function F() { }
        F.prototype = o;
        return new F();
    };
}

這段代碼polyfill代碼使用了一個(gè)一次性函數(shù)F导披,我們通過改寫它的.prototype屬性使其指向想要關(guān)聯(lián)的對(duì)象,然后再使用new F()來構(gòu)造一個(gè)新對(duì)象進(jìn)行關(guān)聯(lián)埃唯。

關(guān)聯(lián)關(guān)系是備用

var anotherObject = {
    cool: function () {
        console.log("cool!");
    }
};
var myObject = Object.create(anotherObject);
myObject.cool(); // "cool!"

由于存在[[Prototype]]機(jī)制撩匕,這段代碼可以正常工作。但是如果你這樣寫只是為了讓myObject在無法處理屬性或者方法時(shí)可以使用備用的anotherObject墨叛,那么你的軟件就會(huì)變得有點(diǎn)“神奇”止毕,而且很難理解和維護(hù)模蜡。
這并不是說任何情況下都不應(yīng)該選擇備用這種設(shè)計(jì)模式,但是這在JavaScript中并不是很常見扁凛。所以如果你使用的是這種模式忍疾,那或許應(yīng)當(dāng)退后一步并重新思考一下這種模式是否合適。
當(dāng)你給開發(fā)者設(shè)計(jì)軟件時(shí)谨朝,假設(shè)要調(diào)用myObject.cool()卤妒,如果myObject中不存在cool()時(shí)這條語句也可以正常工作的話,那你的API設(shè)計(jì)就會(huì)變得很“神奇”字币,對(duì)于未來維護(hù)你軟件的開發(fā)者來說這可能不太好理解则披。
但是你可以讓你的API設(shè)計(jì)不那么“神奇”,同時(shí)仍然能發(fā)揮[[Prototype]]關(guān)聯(lián)的威力:

var anotherObject = {
    cool: function () {
        console.log("cool!");
    }
};
var myObject = Object.create(anotherObject);
myObject.doCool = function () {
    this.cool(); // 內(nèi)部委托洗出!
};
myObject.doCool(); // "cool!"

這里我們調(diào)用的myObject.doCool()是實(shí)際存在于myObject中的士复,這可以讓我們的API設(shè)計(jì)更加清晰。從內(nèi)部來說翩活,我們的實(shí)現(xiàn)遵循的是委托設(shè)計(jì)模式阱洪,通過[[Prototype]]委托到anotherObject.cool()。

我把思念寫在這里菠镇,天堂的您是否能知道
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末冗荸,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子利耍,更是在濱河造成了極大的恐慌俏竞,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件堂竟,死亡現(xiàn)場(chǎng)離奇詭異魂毁,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)出嘹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門席楚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人税稼,你說我怎么就攤上這事烦秩。” “怎么了郎仆?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵只祠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我扰肌,道長(zhǎng)抛寝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮盗舰,結(jié)果婚禮上晶府,老公的妹妹穿的比我還像新娘。我一直安慰自己钻趋,他們只是感情好川陆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著蛮位,像睡著了一般较沪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上失仁,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天购对,我揣著相機(jī)與錄音,去河邊找鬼陶因。 笑死,一個(gè)胖子當(dāng)著我的面吹牛垂蜗,可吹牛的內(nèi)容都是我干的楷扬。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼贴见,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼烘苹!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起片部,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬榮一對(duì)情侶失蹤镣衡,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后档悠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體廊鸥,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年辖所,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了惰说。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡缘回,死狀恐怖吆视,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情酥宴,我是刑警寧澤啦吧,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站拙寡,受9級(jí)特大地震影響授滓,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一褒墨、第九天 我趴在偏房一處隱蔽的房頂上張望炫刷。 院中可真熱鬧,春花似錦郁妈、人聲如沸浑玛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)顾彰。三九已至,卻和暖如春胃碾,著一層夾襖步出監(jiān)牢的瞬間涨享,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工仆百, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留厕隧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓俄周,卻偏偏與公主長(zhǎng)得像吁讨,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子峦朗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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