W3Cschool
恭喜您成為首批注冊(cè)用戶
獲得88經(jīng)驗(yàn)值獎(jiǎng)勵(lì)
我們還記得,可以使用諸如 ?new F()
? 這樣的構(gòu)造函數(shù)來(lái)創(chuàng)建一個(gè)新對(duì)象。
如果 F.prototype
是一個(gè)對(duì)象,那么 new
操作符會(huì)使用它為新對(duì)象設(shè)置 [[Prototype]]
。
請(qǐng)注意:
JavaScript 從一開(kāi)始就有了原型繼承。這是 JavaScript 編程語(yǔ)言的核心特性之一。
但是在過(guò)去,沒(méi)有直接對(duì)其進(jìn)行訪問(wèn)的方式。唯一可靠的方法是本章中會(huì)介紹的構(gòu)造函數(shù)的
"prototype"
屬性。目前仍有許多腳本仍在使用它。
請(qǐng)注意,這里的 F.prototype
指的是 F
的一個(gè)名為 "prototype"
的常規(guī)屬性。這聽(tīng)起來(lái)與“原型”這個(gè)術(shù)語(yǔ)很類似,但這里我們實(shí)際上指的是具有該名字的常規(guī)屬性。
下面是一個(gè)例子:
let animal = {
eats: true
};
function Rabbit(name) {
this.name = name;
}
Rabbit.prototype = animal;
let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
設(shè)置 Rabbit.prototype = animal
的字面意思是:“當(dāng)創(chuàng)建了一個(gè) new Rabbit
時(shí),把它的 [[Prototype]]
賦值為 animal
”。
這是結(jié)果示意圖:
在上圖中,"prototype"
是一個(gè)水平箭頭,表示一個(gè)常規(guī)屬性,[[Prototype]]
是垂直的,表示 rabbit
繼承自 animal
。
?
F.prototype
? 僅用在 ?new F
? 時(shí)
F.prototype
屬性僅在new F
被調(diào)用時(shí)使用,它為新對(duì)象的[[Prototype]]
賦值。
如果在創(chuàng)建之后,
F.prototype
屬性有了變化(F.prototype = <another object>
),那么通過(guò)new F
創(chuàng)建的新對(duì)象也將隨之擁有新的對(duì)象作為[[Prototype]]
,但已經(jīng)存在的對(duì)象將保持舊有的值。
每個(gè)函數(shù)都有 "prototype"
屬性,即使我們沒(méi)有提供它。
默認(rèn)的 "prototype"
是一個(gè)只有屬性 constructor
的對(duì)象,屬性 constructor
指向函數(shù)自身。
像這樣:
function Rabbit() {}
/* 默認(rèn)的 prototype
Rabbit.prototype = { constructor: Rabbit };
*/
我們可以檢查一下:
function Rabbit() {}
// 默認(rèn):
// Rabbit.prototype = { constructor: Rabbit }
alert( Rabbit.prototype.constructor == Rabbit ); // true
通常,如果我們什么都不做,constructor
屬性可以通過(guò) [[Prototype]]
給所有 rabbits 使用:
function Rabbit() {}
// 默認(rèn):
// Rabbit.prototype = { constructor: Rabbit }
let rabbit = new Rabbit(); // 繼承自 {constructor: Rabbit}
alert(rabbit.constructor == Rabbit); // true (from prototype)
我們可以使用 constructor
屬性來(lái)創(chuàng)建一個(gè)新對(duì)象,該對(duì)象使用與現(xiàn)有對(duì)象相同的構(gòu)造器。
像這樣:
function Rabbit(name) {
this.name = name;
alert(name);
}
let rabbit = new Rabbit("White Rabbit");
let rabbit2 = new rabbit.constructor("Black Rabbit");
當(dāng)我們有一個(gè)對(duì)象,但不知道它使用了哪個(gè)構(gòu)造器(例如它來(lái)自第三方庫(kù)),并且我們需要?jiǎng)?chuàng)建另一個(gè)類似的對(duì)象時(shí),用這種方法就很方便。
但是,關(guān)于 "constructor"
最重要的是……
……JavaScript 自身并不能確保正確的 "constructor"
函數(shù)值。
是的,它存在于函數(shù)的默認(rèn) "prototype"
中,但僅此而已。之后會(huì)發(fā)生什么 —— 完全取決于我們。
特別是,如果我們將整個(gè)默認(rèn) prototype 替換掉,那么其中就不會(huì)有 "constructor"
了。
例如:
function Rabbit() {}
Rabbit.prototype = {
jumps: true
};
let rabbit = new Rabbit();
alert(rabbit.constructor === Rabbit); // false
因此,為了確保正確的 "constructor"
,我們可以選擇添加/刪除屬性到默認(rèn) "prototype"
,而不是將其整個(gè)覆蓋:
function Rabbit() {}
// 不要將 Rabbit.prototype 整個(gè)覆蓋
// 可以向其中添加內(nèi)容
Rabbit.prototype.jumps = true
// 默認(rèn)的 Rabbit.prototype.constructor 被保留了下來(lái)
或者,也可以手動(dòng)重新創(chuàng)建 constructor
屬性:
Rabbit.prototype = {
jumps: true,
constructor: Rabbit
};
// 這樣的 constructor 也是正確的,因?yàn)槲覀兪謩?dòng)添加了它
在本章中,我們簡(jiǎn)要介紹了為通過(guò)構(gòu)造函數(shù)創(chuàng)建的對(duì)象設(shè)置 [[Prototype]]
的方法。稍后我們將看到更多依賴于此的高級(jí)編程模式。
一切都很簡(jiǎn)單,只需要記住幾條重點(diǎn)就可以清晰地掌握了:
F.prototype
? 屬性(不要把它與 ?[[Prototype]]
? 弄混了)在 ?new F
? 被調(diào)用時(shí)為新對(duì)象的 ?[[Prototype]]
? 賦值。F.prototype
? 的值要么是一個(gè)對(duì)象,要么就是 ?null
?:其他值都不起作用。"prototype"
? 屬性僅當(dāng)設(shè)置在一個(gè)構(gòu)造函數(shù)上,并通過(guò) ?new
? 調(diào)用時(shí),才具有這種特殊的影響。在常規(guī)對(duì)象上,prototype
沒(méi)什么特別的:
let user = {
name: "John",
prototype: "Bla-bla" // 這里只是普通的屬性
};
默認(rèn)情況下,所有函數(shù)都有 F.prototype = {constructor:F}
,所以我們可以通過(guò)訪問(wèn)它的 "constructor"
屬性來(lái)獲取一個(gè)對(duì)象的構(gòu)造器。
在下面的代碼中,我們創(chuàng)建了 new Rabbit
,然后嘗試修改它的 prototype。
最初,我們有以下代碼:
function Rabbit() {}
Rabbit.prototype = {
eats: true
};
let rabbit = new Rabbit();
alert( rabbit.eats ); // true
alert
會(huì)顯示什么?function Rabbit() {}
Rabbit.prototype = {
eats: true
};
let rabbit = new Rabbit();
Rabbit.prototype = {}; // *
alert( rabbit.eats ); // ?
function Rabbit() {}
Rabbit.prototype = {
eats: true
};
let rabbit = new Rabbit();
Rabbit.prototype.eats = false; // *
alert( rabbit.eats ); // ?
function Rabbit() {}
Rabbit.prototype = {
eats: true
};
let rabbit = new Rabbit();
delete rabbit.eats; // *
alert( rabbit.eats ); // ?
function Rabbit() {}
Rabbit.prototype = {
eats: true
};
let rabbit = new Rabbit();
delete Rabbit.prototype.eats; // *
alert( rabbit.eats ); // ?
true
?Rabbit.prototype
的賦值操作為新對(duì)象設(shè)置了 [[Prototype]]
,但它不影響已有的對(duì)象。
false
?對(duì)象通過(guò)引用被賦值。來(lái)自 Rabbit.prototype
的對(duì)象并沒(méi)有被賦值,它仍然是被 Rabbit.prototype
和 rabbit
的 [[Prototype]]
引用的單個(gè)對(duì)象。
所以當(dāng)我們通過(guò)一個(gè)引用更改其內(nèi)容時(shí),它對(duì)其他引用也是可見(jiàn)的。
true
?所有 delete
操作都直接應(yīng)用于對(duì)象。這里的 delete rabbit.eats
試圖從 rabbit
中刪除 eats
屬性,但 rabbit
對(duì)象并沒(méi)有 eats
屬性。所以這個(gè)操作不會(huì)有任何影響。
undefined
?屬性 eats
被從 prototype 中刪除,prototype 中就沒(méi)有這個(gè)屬性了。
重要程度: 5
想象一下,我們有一個(gè)由構(gòu)造函數(shù)創(chuàng)建的對(duì)象 ?obj
? —— 我們不知道使用的是哪個(gè)構(gòu)造函數(shù),但是我們想使用它創(chuàng)建一個(gè)新對(duì)象。
我們可以這樣做嗎?
let obj2 = new obj.constructor();
請(qǐng)給出一個(gè)可以使這樣的代碼正常工作的 obj
的構(gòu)造函數(shù)的例子。再給出會(huì)導(dǎo)致這樣的代碼無(wú)法正確工作的例子。
如果我們確信 "constructor"
屬性具有正確的值,那么就可以使用這種方法。
例如,如果我們不觸碰默認(rèn)的 "prototype"
,那么這段代碼肯定可以正常運(yùn)行:
function User(name) {
this.name = name;
}
let user = new User('John');
let user2 = new user.constructor('Pete');
alert( user2.name ); // Pete (worked!)
它起作用了,因?yàn)?nbsp;User.prototype.constructor == User
。
……但是如果有人,重寫(xiě)了 User.prototype
,并忘記可重新創(chuàng)建 constructor
以引用 User
,那么上面這段代碼就會(huì)運(yùn)行失敗。
例如:
function User(name) {
this.name = name;
}
User.prototype = {}; // (*)
let user = new User('John');
let user2 = new user.constructor('Pete');
alert( user2.name ); // undefined
為什么 user2.name
是 undefined
?
這是 new user.constructor('Pete')
的工作流程:
user
? 中尋找 ?constructor
?。沒(méi)找到。user
? 的原型是 ?User.prototype
?,它也沒(méi)有 ?constructor
?(因?yàn)槲覀儭巴洝痹谟覀?cè)設(shè)定它了)。User.prototype
? 是一個(gè)普通對(duì)象 ?{}
?,其原型是 ?Object.prototype
?。Object.prototype
?,有一個(gè)內(nèi)建的 ?Object.prototype.constructor == Object
?。所以就用它了。所以,最終我們得到了 let user2 = new Object('Pete')
。
可能這不是我們想要的。我們想創(chuàng)建 new User
而不是 new Object
。這就是缺少 constructor
的結(jié)果。
(以防你好奇,new Object(...)
調(diào)用會(huì)將其參數(shù)轉(zhuǎn)換為對(duì)象。這是理論上的,在實(shí)際中沒(méi)有人會(huì)調(diào)用 new Object
并傳入一個(gè)值,通常我們也不會(huì)使用 new Object
來(lái)創(chuàng)建對(duì)象)。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號(hào)-3|閩公網(wǎng)安備35020302033924號(hào)
違法和不良信息舉報(bào)電話:173-0602-2364|舉報(bào)郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號(hào)
聯(lián)系方式:
更多建議: