深入研究原型

以下筆記參考自《你不知道的JavaScript上卷》

一.原型

1.[[Prototype]]是什么?
  • 它是JavaScript的對(duì)象的一個(gè)特殊的內(nèi)置屬性,其實(shí)就是順著原型鏈引用其他的對(duì)象。
2.Object.create()方法

①該方法會(huì)創(chuàng)建一個(gè)對(duì)象反惕,并把這個(gè)對(duì)象(obj)[[Prototype]]關(guān)聯(lián)到指定對(duì)象(person)

var person = {
    name: 'Gerg'
};
var obj = Object.create(person);
//__proto__只是[[Prototype]]內(nèi)置屬性的一種表現(xiàn)形式,但非標(biāo)準(zhǔn)形式
obj.__proto__ === person;
>>>true

obj.name;
>>>"Gerg"
Object.create().png

②現(xiàn)在obj對(duì)象的[[Prototype]]關(guān)聯(lián)到了person锰蓬。雖然obj.a并不存在,但是屬性訪問(wèn)仍能成功地(在person中)找到了該屬性值"Gerg"眯漩。

③如果person中也找不到name屬性芹扭,并且[[Prototype]]不為空的話,就會(huì)繼續(xù)查找下去赦抖。

④這個(gè)過(guò)程會(huì)持續(xù)找到匹配的屬性名為止舱卡,或者查找完整條[[Prototype]]鏈。如果是后者队萤,[[Get]]操作的返回值就是undefined轮锥。

for...in遍歷對(duì)象的原理與[[Prototype]]鏈相似,任何可以通過(guò)原型鏈訪問(wèn)到(并且是可枚舉的要尔,Enumerable: true;)的屬性都會(huì)被枚舉舍杜。使用in操作符檢查屬性是否在對(duì)象存在時(shí)新娜,同樣會(huì)查找對(duì)象的整條原型鏈(無(wú)論屬性可枚舉與否)。

var person = {
    name: 'Gerg'
};
var obj = Object.create(person);
Object.defineProperty(obj,'age',{
    configurable: true,
    enumerable: false,
    writable: true,
    value: 21
});
for(var v in obj){
    console.log('found: ' + v);
}
>>>found: name

'name' in obj;
>>>true

'age' in obj;
>>>true
3.屬性設(shè)置
//準(zhǔn)備
var person = {};
var obj = Object.create(person);
//完整解析以下過(guò)程:
obj.name = 'asan';
屬性設(shè)置.png

var person = {};
var obj = Object.create(person);
obj.name = 'asan';
obj.name;
>>>"asan"

person.name;
>>>undefined
  • 如果name屬性不是直接存在于obj對(duì)象中既绩,[[Prototype]]鏈就會(huì)向上遍歷概龄,類似[[Get]]操作。如果整條原型鏈都找不到name屬性饲握,name屬性就會(huì)被直接添加到obj對(duì)象上旁钧。

var person = {
    name: 'Gerg'
};
var obj = Object.create(person);
obj.name;
>>>"Gerg"

obj.name = 'asan';
obj.name;
>>>"asan"

person.name;
>>>"Gerg"
  • 如果obj對(duì)象中包含了名為name的普通數(shù)據(jù)訪問(wèn)屬性(即通過(guò)原型鏈可訪問(wèn)到name屬性),這條賦值語(yǔ)句只會(huì)修改已有的屬性互拾。

  • 情況一[[Prototype]]鏈上層存在名為name的普通數(shù)據(jù)訪問(wèn)屬性歪今,并且沒(méi)有被標(biāo)記為只讀(也就是writable: true;),那么當(dāng)在obj對(duì)象上添加一個(gè)名為name的新屬性颜矿,會(huì)屏蔽掉原來(lái)[[Prototype]]上的同名屬性寄猩。
var person = {
    
};
var obj = Object.create(person);
var higherObj = Object.create(obj);
Object.defineProperty(higherObj,'name',{
    writable: true,
    value: 'pangzi'
});
obj.name = 'asan';
obj.name;
>>>"asan"
  • 情況二:如果[[Prototype]]鏈上層存在name屬性,但是它被標(biāo)記為只讀(writable: false;)骑疆,那么無(wú)法修改已有屬性田篇,或者說(shuō)無(wú)法在obj對(duì)象上創(chuàng)建屏蔽屬性。在嚴(yán)格模式下箍铭,代碼會(huì)出錯(cuò)泊柬。否則,這條賦值語(yǔ)句會(huì)被忽略诈火。
var person = {
    
};
var obj = Object.create(person);
Object.defineProperty(obj,'name',{
    writable: false,
    value: 'Gerg'
});
obj.name = 'asan';
obj.name;
>>>"Gerg"
  • 情況三:如果在[[Prototype]]鏈上層存在name屬性兽赁,并且它是一個(gè)setter,那么就一定會(huì)調(diào)用這個(gè)setter冷守。name屬性不會(huì)被添加到(或者屏蔽)obj對(duì)象刀崖,也不會(huì)重新定義name這個(gè)setter

  • 如果屬性名name既出現(xiàn)在obj對(duì)象中拍摇,也出現(xiàn)在obj[[Prototype]]鏈上層亮钦,那么又會(huì)發(fā)生屏蔽(obj對(duì)象中的name屬性屏蔽了higherObj對(duì)象中的同名屬性),因?yàn)?code>obj.name總是選擇原型鏈中最底層的name屬性充活。
var person = {
    name: 'Gerg'
};
var obj = Object.create(person);
obj.name = 'qin';
var higherObj = Object.create(obj);
higherObj.name = 'pangzi';
obj.name = 'asan';
obj.name;
>>>"asan"
4.隱式屏蔽
var person = {
    name: 'Gerg'    
};
var obj = Object.create(person);
person.name;
>>>"Gerg"

obj.name;
>>>"Gerg"

person.hasOwnProperty('name');
>>>true

obj.hasOwnProperty('name');
>>>false

obj.name = 'asan';
obj.name;
>>>"asan"

person.name;
>>>"Gerg"

obj.hasOwnProperty('name');
>>>true

obj.name = 'asan';首選會(huì)通過(guò)[[Prototype]]查找屬性name(在person對(duì)象中找到同名屬性)蜂莉,用屬性值'asan'將原來(lái)的'Gerg'覆蓋掉,接著用[[Put]]'asan'賦值給obj中新創(chuàng)建的屏蔽屬性name混卵。

5."類"

①繼承意味著復(fù)制操作映穗,JavaScript(默認(rèn))并不會(huì)復(fù)制對(duì)象屬性。相反淮菠,JavaScript會(huì)在兩個(gè)對(duì)象之間創(chuàng)建一個(gè)關(guān)聯(lián)男公,這樣以來(lái)荤堪,一個(gè)對(duì)象就可以通過(guò)委托訪問(wèn)另一個(gè)對(duì)象的屬性和函數(shù)合陵。

②委托這個(gè)術(shù)語(yǔ)更為精確地描述 JavaScript中對(duì)象的關(guān)聯(lián)機(jī)制枢赔。

new會(huì)劫持所有普通函數(shù)并用構(gòu)造對(duì)象的形式來(lái)調(diào)用它。

function nothingSpecial(){
    
}
var obj = new nothingSpecial();
obj;
>>>nothingSpecial {}
  • nothingSpecial只是個(gè)普通的函數(shù)拥知,當(dāng)它被new操作符調(diào)用時(shí)踏拜,它就會(huì)構(gòu)造一個(gè)函數(shù)并賦值給obj

JavaScript中的構(gòu)造函數(shù):帶new操作符的函數(shù)調(diào)用低剔。

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

b.myName();
>>>"b"

a.__proto__ === Foo.prototype;
>>>true
Done.png
6.回顧"構(gòu)造函數(shù)"

①一個(gè)普遍而又感覺(jué)理所應(yīng)當(dāng)?shù)?strong>錯(cuò)覺(jué):a.constructor === Foo;意味著對(duì)象a確實(shí)有個(gè)constructor屬性指向Foo速梗。

②其實(shí)這只不過(guò)是一種虛假的安全感而已。何以證明襟齿?

function Foo(){};
Foo.prototype = {};
var a = new Foo();
a.constructor === Foo;
>>>false

③那么真相到底是怎么樣的呢姻锁?

  • Foo.prototype中的constructor屬性只是Foo函數(shù)在聲明時(shí)的默認(rèn)屬性。如果在聲明之后猜欺,你創(chuàng)建了一個(gè)新對(duì)象并替換了函數(shù)默認(rèn)的prototype對(duì)象引用位隶,那么新對(duì)象就不會(huì)自動(dòng)獲得constructor屬性。

④所以a.constructor === Foo;整個(gè)過(guò)程應(yīng)該是這樣的:

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

⑤More:

function Foo(){};
Foo.prototype = {};
var a = new Foo();
a.constructor === Foo;
>>>false

a.constructor === Object;
>>>true
  • 這是為啥开皿?

對(duì)象a中并沒(méi)有constructor屬性涧黄,所以它會(huì)委托[[Prototype]]鏈上的Foo.prototype對(duì)象。但是這個(gè)對(duì)象中也沒(méi)有constructor屬性(不過(guò)赋荆,默認(rèn)的Foo.prototype對(duì)象中有個(gè)屬性K裢住),所以它會(huì)繼續(xù)向上委托窄潭,這次會(huì)委托給委托鏈頂端的Object.prototype春宣。這個(gè)對(duì)象由constructor屬性,所以指向內(nèi)置的Object函數(shù)嫉你。

⑥修改上圖的錯(cuò)誤

修改版.png

⑦如果你閑的慌信认,當(dāng)然可以人工為Foo.prototype添加一個(gè)constructor屬性。

function Foo(){};
Foo.prototype = {};
var a = new Foo();
Object.defineProperty(Foo.prototype,'constructor',{
    configurable: true,
    //注意constructor屬性在默認(rèn)情況下是不可枚舉的
    enumerable: false,
    writable: true,
    value: Foo
});
a.constructor === Foo;
>>>true
7.(原型)繼承

①兩種把Bar.prototype關(guān)聯(lián)到Foo.prototype的方法:

//ES6之前需要拋棄默認(rèn)的Bar.prototype
Bar.prototype = Object.create(Foo.prototype);

//ES6開(kāi)始可以直接修改現(xiàn)有的Bar.prototype
Object.setPrototypeOf(Bar.prototype,Foo.prototype);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末均抽,一起剝皮案震驚了整個(gè)濱河市嫁赏,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌油挥,老刑警劉巖潦蝇,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異深寥,居然都是意外死亡攘乒,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門(mén)惋鹅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)则酝,“玉大人,你說(shuō)我怎么就攤上這事闰集」炼铮” “怎么了般卑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)爽雄。 經(jīng)常有香客問(wèn)我蝠检,道長(zhǎng),這世上最難降的妖魔是什么挚瘟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任叹谁,我火速辦了婚禮,結(jié)果婚禮上乘盖,老公的妹妹穿的比我還像新娘焰檩。我一直安慰自己,他們只是感情好订框,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布锅尘。 她就那樣靜靜地躺著,像睡著了一般布蔗。 火紅的嫁衣襯著肌膚如雪藤违。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天纵揍,我揣著相機(jī)與錄音顿乒,去河邊找鬼。 笑死泽谨,一個(gè)胖子當(dāng)著我的面吹牛璧榄,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吧雹,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼骨杂,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了雄卷?” 一聲冷哼從身側(cè)響起搓蚪,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎丁鹉,沒(méi)想到半個(gè)月后妒潭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡揣钦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年雳灾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片冯凹。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谎亩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情匈庭,我是刑警寧澤夫凸,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布误算,位于F島的核電站胸墙,受9級(jí)特大地震影響龟再,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜紊选,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望道逗。 院中可真熱鬧兵罢,春花似錦、人聲如沸滓窍。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)吏夯。三九已至此蜈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間噪生,已是汗流浹背裆赵。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留跺嗽,地道東北人战授。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像桨嫁,于是被迫代替她去往敵國(guó)和親植兰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

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