[[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()。