原型鏈是什麼?關於原型鏈中constructor、prototype及__proto__之間關係的認識

原型鏈關係圖

首先,咱們暫且把object類型和function類型分開來,由於 function是一個特殊的對象類型,咱們這裏這是便於區分,把function類型單獨拿出來。順便一提,typeof也是單獨把function設爲一種類型,咱們能夠用typeof來驗證咱們的猜測。函數

從上圖中能夠看到,全部的橘色箭頭(constructor)都指向function Function(){},全部的黑色箭頭(__proto__)開始有兩條路能夠走,可是最後都匯聚在了一塊兒,指向null。而天藍色箭頭(prototype)不具備連續性,通常都是一個function指向一個object。而後咱們能夠得出如下結論:學習

  1. 全部對象的構造函數(constructor)最終指向function Function( ){ }這個函數,包括他本身。ps:爲了方便,咱們稱它爲母Function。spa

  2. 全部對象的__proto__最終指向null,即一切原型鏈的末端就是null。prototype

  3. 所謂的一切源於對象,這裏的」對象」指的是Object{}(紅色虛線框內,也就是Object的prototype)。ps:爲了方便,咱們在這裏叫它母Object。翻譯

  4. 除了Object的原型(prototype)直接指向母Object,其餘全部類型的(構造器的)原型(prototype)都先指向一個由母Object派生出來的一個子Object,再指向母Object,Function的原型爲function{}。ps:構造器,即構造函數。code

  5. 哲學部分:這幅圖包含兩個」先有雞仍是先有蛋」的問題:對象

    • function(){}是由母Function構造的,但它同時又是母Function的原型。。blog

    • function(){}的隱式原型(__proto__)是母Object,母Object是由構造函數 function Object(){}構造的,但函數function Object(){}的隱式原型又是function (){}。。。。
      固然除了」先有雞仍是先有蛋」,原型鏈中還有一個最最有哲理的問題:繼承

    • 構造函數function Function(){}的構造函數就是他本身,就是說,他本身把本身生下來了。。。。。 = =原型鏈

  6. 至於爲何function(){}有倆個框框,實際上是由於它是原型中最特殊的。它是原型中惟一一個的function類型的,其餘的原型都是object類型。多是考慮到它也是由構造函數Function生成的吧,因此typeof返回的值也是function。

咱們再來講一下可能你們都很疑惑的問題,就是原型(prototype)和隱式原型(__proto__)的區別。

我說一下個人看法。相對於」原型」,我以爲」__proto__」更適合叫作父對象,由於在原型鏈中,負責鏈接各個對象的,就是」__proto__」。也就是說,我改寫對象的」prototype」,並不會影響對象在原型鏈中的位置,想脫離或者改變原型鏈,只能是改寫」__proto__」。

在原型鏈中,對象和它的」__proto__」指向的對象的關係,更像是別的語言中的子類和父類。子類會繼承父類的方法和屬性,並且會和父類保持同樣的類型。

通常來講,」__proto__」都是指向某一個對象的」prototype」屬性,因此對象會繼承的也就是其父對象」prototype」中的屬性和方法,可是並不會繼承其父對象自身的屬性方法。說的可能有點繞,咱們舉個栗子:

//以Object爲原型建立一個對象,obj。
var obj=new Object
/*這句話能夠翻譯爲:
var obj={}
obj.__proto__=Object.prototype
*/

這個時候,obj的」__proto__」指向的是Object.prototype,因此obj的父對象是Object.prototype,obj會繼承Object.prototype中的屬性方法,可是並不會繼承Object中的屬性和方法。這時候咱們用obj.toString()是能夠的,可是用obj.length就會報錯。這是由於toString()是寫在Object.prototype裏面的,而length是寫在Object裏面的。
在學習運算符的時候,相比不少初學者也會有和我同樣的疑問,爲何instanceof向上查找會在prototype裏查找,而不在」父對象」中查找。理解我上面所說,你們相比也會明白了吧。就拿上面的例子來講,其實Object只是obj的構造器,構造函數而已,obj真正的父對象是Object.prototype。
那講到這裏有的小夥伴就會有疑問了,那我要是想繼承Object裏面的屬性怎麼辦?其實也很簡單,設置obj的」__proto__」指向Object就能夠了。不過有一點,」__proto__」屬性畢竟是底槓開頭,是官方不想暴露在外面的屬性,能不用的時候最好不要用。其實就算不用」__proto__」也是能夠達到想要的效果的。Object裏面有一個能夠建立對象的方法create,用它咱們能夠輕鬆達到咱們的要求。舉個栗子:

//以Object爲原型建立一個對象,obj。
var obj= Object.create(Object)
/*這句話能夠翻譯爲:
var obj={}
obj.__proto__=Object
再說一下create的用法:
Object.create(prototype,[{code}]);//返回一個對象
能夠看到,這裏有兩個參數,第一個參數prototype至關於建立對象的__proto__,值得話隨便一個對象就能夠了,第二個參數能夠不填,裏面詳細寫建立對象的一些屬性和他們的屬性標籤。注意,create建立的是一個對象,和new同樣,不管以什麼爲父對象,返回值都是object。
*/

再來講說prototype,經過上圖你們會發現,prototype屬性只是一個函數和一個對象之間的一個橋樑,在這裏爲何要說是橋樑呢,爲何不說是函數的一個屬性呢?我曾寫過一個例子:

function fa(){}
fa.prototype.fname=’fa’
var ch=new fa()
ch.fname        //’fa’
fa.prototype={fname:’newfa’} //改寫prototype
ch.fname        //依舊是’fa’,
ch.hasOwnProporty(‘fname’)    //false
ch.__proto__==fa.prototype    //false

經過上述代碼你們能夠看出來了吧,ch繼承fa時只是讓ch.__proto__指向了fa.prototype指向的地址,而不是指向fa.prototype。因此就致使了改寫fa.prototype並不會影響ch.fname的值。改寫後ch.__proto__不等於fa.prototype了,也就是說ch和fa已經半毛錢關係都沒有了。基本上到這裏,你們都會對原型鏈有必定的認識了,至於對象中的特殊存在——function,你們能夠本身去探索一下。以上都是本身對於JS中原型鏈的認識,若有錯誤歡迎你們指正~ps:sf的編排好難用 !

相關文章
相關標籤/搜索