JS中的prototype、__proto__與constructor(圖解)

話不多說,直接上栗子:

function Foo() {...};
let f1 = new Foo();

針對(duì)上述的兩行簡(jiǎn)單的代碼,prototype、proto與constructor三個(gè)的關(guān)系是怎樣的呢?如下圖:

20190311194017886.png

圖片說明:右下角為圖例,紅色箭頭表示proto屬性指向、綠色箭頭表示prototype屬性的指向、棕色實(shí)線箭頭表示本身具有的constructor屬性的指向,棕色虛線箭頭表示繼承而來的constructor屬性的指向;藍(lán)色方塊表示對(duì)象,淺綠色方塊表示函數(shù)(這里為了更好看清,F(xiàn)oo()僅代表是函數(shù),并不是指執(zhí)行函數(shù)Foo后得到的結(jié)果,圖中的其他函數(shù)同理)。圖的中間部分即為它們之間的聯(lián)系,圖的最左邊即為例子代碼。
首先,我們需要牢記兩點(diǎn):①proto和constructor屬性是對(duì)象所獨(dú)有的;② prototype屬性是函數(shù)所獨(dú)有的。但是由于JS中函數(shù)也是一種對(duì)象,所以函數(shù)也擁有proto和constructor屬性,這點(diǎn)是致使我們產(chǎn)生困惑的很大原因之一。上圖有點(diǎn)復(fù)雜,我們把它按照屬性分別拆開,然后進(jìn)行分析:
20190311192930650.png

第一,這里我們僅留下 proto 屬性,它是對(duì)象所獨(dú)有的,可以看到proto屬性都是由一個(gè)對(duì)象指向一個(gè)對(duì)象,即指向它們的原型對(duì)象(也可以理解為父對(duì)象),那么這個(gè)屬性的作用是什么呢?它的作用就是當(dāng)訪問一個(gè)對(duì)象的屬性時(shí),如果該對(duì)象內(nèi)部不存在這個(gè)屬性,那么就會(huì)去它的proto屬性所指向的那個(gè)對(duì)象(可以理解為父對(duì)象)里找,如果父對(duì)象也不存在這個(gè)屬性,則繼續(xù)往父對(duì)象的proto屬性所指向的那個(gè)對(duì)象(可以理解為爺爺對(duì)象)里找,如果還沒找到,則繼續(xù)往上找…直到原型鏈頂端null(可以理解為原始人。。。),此時(shí)若還沒找到,則返回undefined(可以理解為,再往上就已經(jīng)不是“人”的范疇了,找不到了,到此結(jié)束),由以上這種通過proto屬性來連接對(duì)象直到null的一條鏈即為我們所謂的原型鏈。

第二,接下來我們看 prototype 屬性:

20190311193033876.png

prototype屬性,別忘了一點(diǎn),就是我們前面提到要牢記的兩點(diǎn)中的第二點(diǎn),它是函數(shù)所獨(dú)有的,它是從一個(gè)函數(shù)指向一個(gè)對(duì)象。它的含義是函數(shù)的原型對(duì)象,也就是這個(gè)函數(shù)(其實(shí)所有函數(shù)都可以作為構(gòu)造函數(shù))所創(chuàng)建的實(shí)例的原型對(duì)象,由此可知:f1.proto === Foo.prototype,它們兩個(gè)完全一樣。那prototype屬性的作用又是什么呢?它的作用就是包含可以由特定類型的所有實(shí)例共享的屬性和方法,也就是讓該函數(shù)所實(shí)例化的對(duì)象們都可以找到公用的屬性和方法。任何函數(shù)在創(chuàng)建的時(shí)候,其實(shí)會(huì)默認(rèn)同時(shí)創(chuàng)建該函數(shù)的prototype對(duì)象。

最后,我們來看一下 constructor 屬性:

20190311193745414.png

constructor屬性也是對(duì)象才擁有的,它是從一個(gè)對(duì)象指向一個(gè)函數(shù),含義就是指向該對(duì)象的構(gòu)造函數(shù),每個(gè)對(duì)象都有構(gòu)造函數(shù)(本身擁有或繼承而來,繼承而來的要結(jié)合proto屬性查看會(huì)更清楚點(diǎn),如下圖所示),從上圖中可以看出Function這個(gè)對(duì)象比較特殊,它的構(gòu)造函數(shù)就是它自己(因?yàn)镕unction可以看成是一個(gè)函數(shù),也可以是一個(gè)對(duì)象),所有函數(shù)和對(duì)象最終都是由Function構(gòu)造函數(shù)得來,所以constructor屬性的終點(diǎn)就是Function這個(gè)函數(shù)。
20190311194017886.png

這里解釋一下上段中“每個(gè)對(duì)象都有構(gòu)造函數(shù)”這句話。這里的意思是每個(gè)對(duì)象都可以找到其對(duì)應(yīng)的constructor,因?yàn)閯?chuàng)建對(duì)象的前提是需要有constructor,而這個(gè)constructor可能是對(duì)象自己本身顯式定義的或者通過proto在原型鏈中找到的。而單從constructor這個(gè)屬性來講,只有prototype對(duì)象才有。每個(gè)函數(shù)在創(chuàng)建的時(shí)候,JS會(huì)同時(shí)創(chuàng)建一個(gè)該函數(shù)對(duì)應(yīng)的prototype對(duì)象,而函數(shù)創(chuàng)建的對(duì)象.proto === 該函數(shù).prototype,該函數(shù).prototype.constructor===該函數(shù)本身,故通過函數(shù)創(chuàng)建的對(duì)象即使自己沒有constructor屬性,它也能通過proto找到對(duì)應(yīng)的constructor,所以任何對(duì)象最終都可以找到其構(gòu)造函數(shù)(null如果當(dāng)成對(duì)象的話,將null除外)。如下:
20190311192013184.png

總結(jié)一下:

  • 我們需要牢記兩點(diǎn):①proto和constructor屬性是對(duì)象所獨(dú)有的;② prototype屬性是函數(shù)所獨(dú)有的,因?yàn)楹瘮?shù)也是一種對(duì)象,所以函數(shù)也擁有proto和constructor屬性。
  • proto屬性的作用就是當(dāng)訪問一個(gè)對(duì)象的屬性時(shí),如果該對(duì)象內(nèi)部不存在這個(gè)屬性,那么就會(huì)去它的proto屬性所指向的那個(gè)對(duì)象(父對(duì)象)里找,一直找,直到proto屬性的終點(diǎn)null,然后返回undefined,再往上找就相當(dāng)于在null上取值,會(huì)報(bào)錯(cuò)。通過proto屬性將對(duì)象連接起來的這條鏈路即我們所謂的原型鏈。
  • prototype屬性的作用就是讓該函數(shù)所實(shí)例化的對(duì)象們都可以找到公用的屬性和方法,即f1.proto === Foo.prototype。
  • constructor屬性的含義就是指向該對(duì)象的構(gòu)造函數(shù),所有函數(shù)(此時(shí)看成對(duì)象了)最終的構(gòu)造函數(shù)都指向Function。

參考博客:https://blog.csdn.net/cc18868876837/article/details/81211729

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容