本文基于谷歌瀏覽器(版本 72.0.3626.121)的實驗結(jié)果所得功咒。
我們從如下一個簡單的例子展開討論,并配以相關(guān)的圖幫助理解:
function Foo() {...};
let f1 = new Foo();
以上代碼表示創(chuàng)建一個構(gòu)造函數(shù)Foo()桦踊,并用new關(guān)鍵字實例化該構(gòu)造函數(shù)得到一個實例化對象f1椅野。雖然是簡簡單單的兩行代碼,然而它們背后的關(guān)系卻是錯綜復(fù)雜的籍胯,如下圖所示:
看到這圖別怕竟闪,讓我們一步步剖析,徹底搞懂它們杖狼!
圖的說明:右下角為圖例瘫怜,紅色箭頭表示__proto__屬性指向、綠色箭頭表示prototype屬性的指向本刽、棕色實線箭頭表示本身具有的constructor屬性的指向,棕色虛線箭頭表示繼承而來的constructor屬性的指向赠涮;藍色方塊表示對象子寓,淺綠色方塊表示函數(shù)(這里為了更好看清,F(xiàn)oo()僅代表是函數(shù)笋除,并不是指執(zhí)行函數(shù)Foo后得到的結(jié)果斜友,圖中的其他函數(shù)同理)。圖的中間部分即為它們之間的聯(lián)系垃它,圖的最左邊即為例子代碼鲜屏。
首先烹看,我們需要牢記兩點:①__proto__和constructor屬性是對象所獨有的;
② prototype屬性是函數(shù)所獨有的洛史。
但是由于JS中函數(shù)也是一種對象惯殊,所以函數(shù)也擁有__proto__和constructor屬性,這點是致使我們產(chǎn)生困惑的很大原因之一也殖。上圖有點復(fù)雜土思,我們把它按照屬性分別拆開,然后進行分析:
proto
第一忆嗜,這里我們僅留下 __proto__屬性己儒,它是對象所獨有的
,可以看到__proto__屬性都是由一個對象指向一個對象
捆毫,即指向它們的原型對象(也可以理解為父對象)闪湾,那么這個屬性的作用是什么呢?它的作用就是當(dāng)訪問一個對象的屬性時绩卤,如果該對象內(nèi)部不存在這個屬性途样,那么就會去它的\_\_proto\_\_屬性所指向的那個對象(可以理解為父對象)里找,如果父對象也不存在這個屬性省艳,則繼續(xù)往父對象的\_\_proto\_\_屬性所指向的那個對象(可以理解為爺爺對象)里找娘纷,如果還沒找到,則繼續(xù)往上找…直到原型鏈頂端null跋炕,此時若還沒找到赖晶,則返回undefined
,由以上這種通過__proto__屬性來連接對象直到null的一條鏈即為我們所謂的原型鏈辐烂。
prototype
第二遏插,接下來我們看 prototype 屬性:
prototype屬性,別忘了一點纠修,就是我們前面提到要牢記的兩點中的第二點胳嘲,它是函數(shù)所獨有的,它是從一個函數(shù)指向一個對象扣草。它的含義是函數(shù)的原型對象了牛,也就是這個函數(shù)(其實所有函數(shù)都可以作為構(gòu)造函數(shù))所創(chuàng)建的實例的原型對象,由此可知:f1.__proto__ === Foo.prototype辰妙,它們兩個完全一樣鹰祸。那prototype屬性的作用又是什么呢?
它的作用
就是包含可以由特定類型的所有實例共享的屬性和方法密浑,也就是讓該函數(shù)所實例化的對象們都可以找到公用的屬性和方法
蛙婴。任何函數(shù)在創(chuàng)建的時候,其實會默認同時創(chuàng)建該函數(shù)的prototype對象尔破。
constructor
最后街图,我們來看一下 constructor 屬性:
constructor屬性也是對象才擁有的浇衬,它是從一個對象指向一個函數(shù),含義就是指向該對象的構(gòu)造函數(shù)餐济,每個對象都有構(gòu)造函數(shù)(本身擁有或繼承而來耘擂,繼承而來的要結(jié)合__proto__屬性查看會更清楚點,如下圖所示)颤介,從上圖中可以看出Function這個對象比較特殊梳星,它的構(gòu)造函數(shù)就是它自己(因為Function可以看成是一個函數(shù),也可以是一個對象)滚朵,所有函數(shù)和對象最終都是由Function構(gòu)造函數(shù)得來冤灾,所以constructor屬性的終點就是Function這個函數(shù)。
這里解釋一下上段中“每個對象都有構(gòu)造函數(shù)”這句話辕近。這里的意思是每個對象都可以找到其對應(yīng)的constructor韵吨,因為創(chuàng)建對象的前提是需要有constructor,而這個constructor可能是對象自己本身顯式定義的或者通過__proto__在原型鏈中找到的移宅。而單從constructor這個屬性來講归粉,只有prototype對象才有。每個函數(shù)在創(chuàng)建的時候漏峰,JS會同時創(chuàng)建一個該函數(shù)對應(yīng)的prototype對象糠悼,而函數(shù)創(chuàng)建的對象.__proto__ === 該函數(shù).prototype,該函數(shù).prototype.constructor===該函數(shù)本身浅乔,故通過函數(shù)創(chuàng)建的對象即使自己沒有constructor屬性倔喂,它也能通過__proto__找到對應(yīng)的constructor,所以任何對象最終都可以找到其構(gòu)造函數(shù)(null如果當(dāng)成對象的話靖苇,將null除外)席噩。如下:
總結(jié)一下:
- 我們需要牢記兩點:①__proto__和constructor屬性是對象所獨有的;② prototype屬性是函數(shù)所獨有的贤壁,因為函數(shù)也是一種對象悼枢,所以函數(shù)也擁有__proto__和constructor屬性。
- __proto__屬性的作用就是當(dāng)訪問一個對象的屬性時脾拆,如果該對象內(nèi)部不存在這個屬性馒索,那么就會去它的__proto__屬性所指向的那個對象(父對象)里找,一直找名船,直到__proto__屬性的終點null绰上,然后返回undefined,通過__proto__屬性將對象連接起來的這條鏈路即我們所謂的原型鏈包帚。
- prototype屬性的作用就是讓該函數(shù)所實例化的對象們都可以找到公用的屬性和方法,即f1.__proto__ === Foo.prototype运吓。
- constructor屬性的含義就是指向該對象的構(gòu)造函數(shù)渴邦,所有函數(shù)(此時看成對象了)最終的構(gòu)造函數(shù)都指向Function疯趟。