原型繼承補(bǔ)充(prototype和__proto__詳解)

在上篇文章中,由于篇幅的原因只是針對(duì)構(gòu)造函數(shù)的構(gòu)造過程和原型鏈的存取進(jìn)行深入的講解翻诉,有點(diǎn)偏原理性的講解还绘,并沒有對(duì)___proto___prototypeconstructor這些屬性之間的互相關(guān)系以及實(shí)際上的應(yīng)用分析清楚膘侮。所以本文的目的就是為了加深對(duì)原型繼承的理解套蒂,并能夠?qū)⑵鋺?yīng)用在實(shí)際上撼港。

prototype

//創(chuàng)建一個(gè)構(gòu)造函數(shù)。
function Fruit(){};
//輸出其原型對(duì)象:
console.log(Fruit.prototype);
Fruit.prototype.png
//如果手動(dòng)設(shè)置其prototype屬性的話营曼,那么將改變其原型對(duì)象
Fruit.prototype = {
    getName : function(){
        return this.name;
    },
    name : 'fruit',
};
//再次輸出其原型對(duì)象,這時(shí)候就發(fā)生了點(diǎn)奇怪的事情了
console.log(Fruit.prototype);

Fruit.prototype1.png

對(duì)比兩張圖片愚隧,有沒有發(fā)現(xiàn)異常蒂阱,心細(xì)的人可能發(fā)現(xiàn)了Fruit.prototype少了一個(gè)constructor屬性,那么Fruit.prototype.constructor屬性到底跑哪去了奸攻,我們?cè)诳刂婆_(tái)輸出一下看看蒜危。

console.log(Fruit.prototype.constructor)
//function Object() { [native code] }
//可能有人會(huì)感到奇怪,在Fruit.prototype中明明并沒有該屬性睹耐,那么這該死的constructor屬性從哪里來的辐赞。
//我們回到Fruit.prototype屬性,點(diǎn)開__proto__屬性,那么一切就明朗了硝训。

Fruit.prototype1.__proto__.png

Fruit.prototype中响委,只有其的__proto__擁有constructor屬性,所以是不是可以認(rèn)為其Fruit.prototype.constructor===Fruit.prototype.__proto__.constructor窖梁?事實(shí)上赘风,我們可以認(rèn)為二者指向同一個(gè)構(gòu)造函數(shù)。由于重寫了Fruit的原型對(duì)象纵刘,JavaScript引擎不能在顯式原型中找到constructor屬性邀窃,那么它將通過隱式原型鏈查找,找到了Fruit.prototype.__proto__的constructor屬性假哎。如果重寫原型就會(huì)導(dǎo)致constructor屬性的更改瞬捕,那么在實(shí)際開發(fā)的時(shí)候就會(huì)發(fā)生指向不明的錯(cuò)誤鞍历,如下所示:

function Fruit(){}
function Animal(){}
Animal.prototype = new Fruit();
var apple = new Fruit();
var cat = new Animal();
alert(apple.constructor===cat.constructor);//true
//apple和cat明明屬于兩個(gè)不同構(gòu)造器產(chǎn)生的實(shí)例,但是它們的constructor屬性指向同一構(gòu)造器產(chǎn)生的實(shí)例

所以在修改構(gòu)造函數(shù)的原型時(shí)候肪虎,應(yīng)該修正該原型對(duì)象的constructor屬性劣砍,通常修正方法有兩種:

//第一種方法:當(dāng)其原型修改時(shí),手動(dòng)更改其原型的```constructor```屬性的指向扇救。
Animal.prototype.constructor = Animal;
//第二種方法:保持原型的構(gòu)造器屬性刑枝,在子類構(gòu)造器函數(shù)內(nèi)初始化實(shí)例的構(gòu)造器屬性,
function Animal(){
    this.constructor = arguments.callee;
    //或者可以:this.constructor = Animal;
}

在網(wǎng)上對(duì)constructor屬性的作用有著許多不同的看法迅腔,有的人認(rèn)為其是為了將實(shí)例的構(gòu)造器的原型對(duì)象更好的暴露出來装畅,但是我個(gè)人認(rèn)為constructor屬性在整個(gè)原型繼承中其實(shí)是沒有起到什么作用的,甚至在JS語言中也是如此钾挟,因?yàn)槠淇勺x寫洁灵,所以其未必指向?qū)ο蟮臉?gòu)造函數(shù),像上面的保持原型構(gòu)造屬性不變掺出,只是從編程的習(xí)慣出發(fā)徽千,讓對(duì)象的constructor屬性指向其構(gòu)造函數(shù)。

說完了構(gòu)造函數(shù)的prototype屬性汤锨,由于我在上文就已經(jīng)介紹過了普通的函數(shù)與構(gòu)造函數(shù)并沒有什么本質(zhì)的區(qū)別双抽,所以現(xiàn)在我們開始將目光放在一些特殊的函數(shù)上面。

Function是JavaScript一個(gè)特殊的構(gòu)造函數(shù)闲礼,在JS中牍汹,每一個(gè)函數(shù)都是其對(duì)象(Object也是)。在控制臺(tái)輸出下Function.prototype得到這樣一個(gè)函數(shù)function () { [native code] }柬泽。再用

console.log(Function.prototype);
//function () { [native code] }
//用typeof判斷下其類型
console.log(typeof Function.prototype)//function
//既然其是function類型的慎菲,那么因?yàn)樗械暮瘮?shù)都有prototype對(duì)
//象,所以其肯定就有prototype屬性了锨并,那么我們現(xiàn)在可以輸出看看了露该,但是神奇的事情發(fā)生了。
console.log(Function.prototype.prototype)//undefined
//其居然輸出了undefined第煮,這發(fā)生了什么事情解幼??

翻閱了許多資料包警,終于讓我找到了其原因所在撵摆。而這與JavaScript的底層有關(guān)了。在上篇文章害晦,我們就說到了Object.prototype處于原型鏈的頂端特铝,而JavaScript在Object.prototype的基礎(chǔ)上又產(chǎn)生了一個(gè)Function.prototype對(duì)象,這個(gè)對(duì)象又被稱為[Function:Empty](空函數(shù),其是一個(gè)不同于一般函數(shù)的函數(shù)對(duì)象)苟呐。隨后又以該對(duì)象為基礎(chǔ)打造了兩個(gè)構(gòu)造函數(shù)痒芝,一個(gè)即為Function,另一個(gè)為Object。意不意外牵素,驚不驚喜!但是看到下面澄者,你又會(huì)剛到更加意外的笆呆。所以,在下面的代碼如此顯示粱挡,你就不會(huì)感到意外了赠幕。

console.log(Object.__proto__ === Function.prototype);//true
//Object的__proto__屬性指向Function.prototype。這又說明Object這個(gè)構(gòu)造器是從Function的原型生產(chǎn)出來的询筏。
console.log(Object.constructor === Function);//true
//Object.constructor屬性指向了它的構(gòu)造函數(shù)Function
//看著上面的代碼榕堰,是不是能夠得出Object是一個(gè)Function的實(shí)例對(duì)象的結(jié)論。

沒錯(cuò)嫌套,Object這個(gè)構(gòu)造函數(shù)是Function的一個(gè)實(shí)例(因?yàn)?code>Object是繼承自Function.prototype逆屡,甚至可以這樣說,所有的構(gòu)造函數(shù)都是 Function的一個(gè)實(shí)例踱讨。

__proto__

談完了prototype屬性魏蔗,現(xiàn)在我們開始來看看__proto__屬性,在上篇文章中痹筛,我們就已經(jīng)提到了__proto__指向的是當(dāng)前對(duì)象的原型對(duì)象莺治。由于在JS內(nèi)部,__proto__屬性是為了保持子類與父類的一致性帚稠,所以在對(duì)象初始化的時(shí)候谣旁,在其內(nèi)部生成該屬性,并拒絕用戶去修改該屬性滋早。盡管目前我們可以手動(dòng)去修改該屬性榄审,但是為了保持這種一致性,盡量不要去修改該屬性馆衔。廢話不多說瘟判,我們來看看一些示例:

//一個(gè)普通的函數(shù)
function Fruit(){};
console.log(Fruit.__proto__);//function(){ [native code] }
//貌似有點(diǎn)眼熟,像是上面的空函數(shù)角溃,動(dòng)手試試
console.log(Fruit.__proto__===Function.prototype)//true
//恩拷获,有點(diǎn)大驚小怪了,對(duì)象的__proto__就是指向構(gòu)造該對(duì)象的構(gòu)造函數(shù)的原型對(duì)象减细。
//如果二者不等的話匆瓜,那就出事了。
//現(xiàn)在來看看一個(gè)構(gòu)造函數(shù)構(gòu)造出來的對(duì)象
var apple = new Fruit();
console.log(apple.__proto__);
//其指向了Fruit.prototype,但是如果Fruit.prototype該變量驮吱,那會(huì)怎么樣呢茧妒?
Fruit.prototype = {};
console.log(apple.__proto__);
//貌似跟上面并沒有多大的變化,但是別急左冬,我們接下來看桐筏。
var banana = new Fruit();
console.log(banana.__proto__);
//{};這就對(duì)了,對(duì)象的__proto__就是指向原型對(duì)象的拇砰,當(dāng)構(gòu)造函數(shù)的原型對(duì)象改變的時(shí)候梅忌,其也將改變。
//至于為什么apple和banana的__proto__屬性會(huì)變化除破,這就涉及到內(nèi)存分配的問題了牧氮,在這里就不再展開。

由于每個(gè)對(duì)象都將擁有一個(gè)__proto__屬性瑰枫,那么apple.__proto__必然擁有__proto__屬性踱葛,那就讓我們一起探究下吧。

function Animal(){};
var dog = new Animal();
console.log(dog.__proto__.__proto__)
//Object {__defineGetter__: function, __defineSetter__: function, hasOwnProperty: function, __lookupGetter__: function, __lookupSetter__: function…}
//是不是很眼熟光坝,這跟上面的Object.prototypey一模一樣尸诽,輸出看看
console.log(dog.__proto__.__proto__==Object.prototype) //true

其實(shí)仔細(xì)分析下就應(yīng)該知道這樣的指向,dog.__proto__指向Animal.prototype,而Animal.prototype其實(shí)是一個(gè)對(duì)象實(shí)例教馆,由Object所構(gòu)造出來的逊谋,自然Animal.prototype.__proto__指向Object.prototype⊥疗蹋看完了對(duì)象的__proto__屬性胶滋,現(xiàn)在來看下函數(shù)的相關(guān)屬性。

console.log(Animal.__proto__===Function.prototype)//true;
console.log(Animal.__proto__.__proto__===Object.prototype)//true;
console.log(Animal.__proto__.__proto__.__proto__)//null

可能有人會(huì)對(duì)Animal.__proto__.__proto__.__proto__===null產(chǎn)生疑惑悲敷,有人也是因?yàn)檫@樣而認(rèn)為在整個(gè)原型鏈的頂端就是null,其實(shí)不然究恤,因?yàn)?code>null壓根就沒有任何屬性,自然對(duì)象和函數(shù)就不能從中繼承到什么東西了后德。
其實(shí)在JavaScript內(nèi)部部宿,當(dāng)實(shí)例化一個(gè)對(duì)象的時(shí)候,實(shí)例對(duì)象的__proto__指向了構(gòu)造函數(shù)的prototype屬性瓢湃,以此來繼承構(gòu)造函數(shù)prototype上所有屬性和方法理张。

總結(jié):其實(shí)如果能夠縷清__proto__prototype二者的關(guān)系,那么關(guān)于原型繼承就很簡單了绵患。每個(gè)對(duì)象都擁有了__proto__屬性雾叭,所有對(duì)象的__proto__屬性串聯(lián)起了一條原型鏈,連接了擁有繼承關(guān)系的對(duì)象落蝙,這條原型鏈的終點(diǎn)指向了Object.prototype织狐。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末暂幼,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子移迫,更是在濱河造成了極大的恐慌旺嬉,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厨埋,死亡現(xiàn)場(chǎng)離奇詭異邪媳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)荡陷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門悲酷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人亲善,你說我怎么就攤上這事《翰瘢” “怎么了蛹头?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長戏溺。 經(jīng)常有香客問我渣蜗,道長,這世上最難降的妖魔是什么旷祸? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任耕拷,我火速辦了婚禮,結(jié)果婚禮上托享,老公的妹妹穿的比我還像新娘骚烧。我一直安慰自己,他們只是感情好闰围,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布赃绊。 她就那樣靜靜地躺著,像睡著了一般羡榴。 火紅的嫁衣襯著肌膚如雪碧查。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天校仑,我揣著相機(jī)與錄音忠售,去河邊找鬼。 笑死迄沫,一個(gè)胖子當(dāng)著我的面吹牛稻扬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播邢滑,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼腐螟,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼愿汰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起乐纸,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤衬廷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后汽绢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吗跋,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年宁昭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了跌宛。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡积仗,死狀恐怖疆拘,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情寂曹,我是刑警寧澤哎迄,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站隆圆,受9級(jí)特大地震影響漱挚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜渺氧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一旨涝、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侣背,春花似錦白华、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至憔杨,卻和暖如春鸟赫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背消别。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國打工抛蚤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人寻狂。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓岁经,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蛇券。 傳聞我的和親對(duì)象是個(gè)殘疾皇子缀壤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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