做爲一名前端工程師,必須搞懂JS中的prototype
、__proto__
與constructor
屬性,相信不少初學者對這些屬性存在許多困惑,容易把它們混淆,本文旨在幫助你們理清它們之間的關係並完全搞懂它們。這裏說明一點,__proto__
屬性的兩邊是各由兩個下劃線構成(這裏爲了方便你們看清,在兩下劃線之間加入了一個空格:_ _proto_ _
),本文基於谷歌瀏覽器(版本 72.0.3626.121)的實驗結果所得。javascript
如今正式開始! 讓咱們從以下一個簡單的例子展開討論,並配以相關的圖幫助理解:html
function Foo() {...};
let f1 = new Foo();
複製代碼
以上代碼表示建立一個構造函數Foo()
,並用new
關鍵字實例化該構造函數獲得一個實例化對象f1
。雖然是簡簡單單的兩行代碼,然而它們背後的關係倒是錯綜複雜的,以下圖所示: 前端
看到這圖別怕,讓咱們一步步剖析,完全搞懂它們!java
圖的說明:右下角爲圖例,紅色箭頭表示__proto__
屬性指向、綠色箭頭表示prototype
屬性的指向、棕色實線箭頭表示自己具備的constructor
屬性的指向,棕色虛線箭頭表示繼承而來的constructor
屬性的指向;藍色方塊表示對象,淺綠色方塊表示函數(這裏爲了更好看清,Foo()僅表明是函數,並非指執行函數Foo後獲得的結果,圖中的其餘函數同理)。圖的中間部分即爲它們之間的聯繫,圖的最左邊即爲例子代碼。瀏覽器
首先,咱們須要牢記兩點:①__proto__
和constructor
屬性是對象所獨有的;② prototype
屬性是函數所獨有的。可是因爲JS中函數也是一種對象,因此函數也擁有__proto__
和constructor
屬性,這點是導致咱們產生困惑的很大緣由之一。上圖有點複雜,咱們把它按照屬性分別拆開,而後進行分析: 前端工程師
__proto__
屬性,它是
對象所獨有的,能夠看到
__proto__
屬性都是由
一個對象指向一個對象,即指向它們的原型對象(也能夠理解爲父對象),那麼這個屬性的做用是什麼呢?它的
做用就是當訪問一個對象的屬性時,若是該對象內部不存在這個屬性,那麼就會去它的
__proto__
屬性所指向的那個對象(能夠理解爲父對象)裏找,若是父對象也不存在這個屬性,則繼續往父對象的
__proto__
屬性所指向的那個對象(能夠理解爲爺爺對象)裏找,若是還沒找到,則繼續往上找....直到原型鏈頂端
null(能夠理解爲原始人。。。),此時若還沒找到,則返回
undefined
(能夠理解爲,再往上就已經不是「人」的範疇了,找不到了,到此結束),由以上這種經過
__proto__
屬性來鏈接對象直到
null
的一條鏈即爲咱們所謂的
原型鏈。
第二,接下來咱們看 prototype
屬性: 函數
prototype
屬性,別忘了一點,就是咱們前面提到要牢記的兩點中的第二點,它是
函數所獨有的,它是從
一個函數指向一個對象。它的含義是
函數的原型對象,也就是這個函數(其實全部函數均可以做爲構造函數)所建立的實例的原型對象,由此可知:
f1.__proto__ === Foo.prototype
,它們兩個徹底同樣。那
prototype
屬性的做用又是什麼呢?它的
做用就是包含能夠由特定類型的全部實例共享的屬性和方法,也就是讓該函數所實例化的對象們均可以找到公用的屬性和方法。
任何函數在建立的時候,其實會默認同時建立該函數的prototype對象。
最後,咱們來看一下 constructor
屬性: spa
constructor
屬性也是
對象才擁有的,它是從
一個對象指向一個函數,含義就是
指向該對象的構造函數,每一個對象都有構造函數(自己擁有或繼承而來,繼承而來的要結合
__proto__
屬性查看會更清楚點,以下圖所示),從上圖中能夠看出
Function這個對象比較特殊,它的構造函數就是它本身(由於Function能夠當作是一個函數,也能夠是一個對象),全部函數和對象最終都是由Function構造函數得來,因此
constructor
屬性的終點就是
Function這個函數。
感謝網友的指出,這裏解釋一下上段中
「每一個對象都有構造函數」這句話。這裏的意思是每一個對象均可以找到其對應的constructor,由於建立對象的前提是須要有constructor,而這個constructor多是對象本身自己顯式定義的或者經過
__proto__
在原型鏈中找到的。
而單從constructor這個屬性來說,只有prototype對象纔有。每一個函數在建立的時候,JS會同時建立一個該函數對應的prototype對象,而
函數建立的對象.__proto__ === 該函數.prototype,該函數.prototype.constructor===該函數自己
,故經過函數建立的對象即便本身沒有constructor屬性,它也能經過
__proto__
找到對應的constructor,因此任何對象最終均可以找到其構造函數(null若是當成對象的話,將null除外)。以下:
總結一下:prototype
__proto__
和constructor
屬性是對象所獨有的;② prototype
屬性是函數所獨有的,由於函數也是一種對象,因此函數也擁有__proto__
和constructor
屬性。__proto__
屬性的做用就是當訪問一個對象的屬性時,若是該對象內部不存在這個屬性,那麼就會去它的__proto__
屬性所指向的那個對象(父對象)裏找,一直找,直到__proto__
屬性的終點null,undefined
__proto__
屬性將對象鏈接起來的這條鏈路即咱們所謂的原型鏈。prototype
屬性的做用就是讓該函數所實例化的對象們均可以找到公用的屬性和方法,即f1.__proto__ === Foo.prototype
。constructor
屬性的含義就是指向該對象的構造函數,全部函數(此時當作對象了)最終的構造函數都指向Function。 本文就此結束了,但願對那些對JS中的prototype
、__proto__
與constructor
屬性有困惑的同窗有所幫助。code
最後,感謝這兩篇博文,本文中的部份內容參考自這兩篇博文: www.cnblogs.com/xiaohuochai…