摘要:的隱式原型是母,母是由構造函數構造的,但函數的隱式原型又是。。。。可能是考慮到它也是由構造函數生成的吧,所以返回的值也是。
首先,我們暫且把object類型和function類型分開來,因為 function是一個特殊的對象類型,我們這里這是便于區分,把function類型多帶帶拿出來。順便一提,typeof也是多帶帶把function設為一種類型,我們可以用typeof來驗證我們的猜想。
從上圖中可以看到,所有的橘色箭頭(constructor)都指向function Function(){},所有的黑色箭頭(__proto__)開始有兩條路可以走,但是最后都匯聚在了一起,指向null。而天藍色箭頭(prototype)不具有連續性,一般都是一個function指向一個object。然后我們可以得出以下結論:
所有對象的構造函數(constructor)最終指向function Function( ){ }這個函數,包括他自己。ps:為了方便,我們稱它為母Function。
所有對象的__proto__最終指向null,即一切原型鏈的末端就是null。
所謂的一切源于對象,這里的”對象”指的是Object{}(紅色虛線框內,也就是Object的prototype)。ps:為了方便,我們在這里叫它母Object。
除了Object的原型(prototype)直接指向母Object,其他所有類型的(構造器的)原型(prototype)都先指向一個由母Object派生出來的一個子Object,再指向母Object,Function的原型為function{}。ps:構造器,即構造函數。
哲學部分:這幅圖包含兩個”先有雞還是先有蛋”的問題:
function(){}是由母Function構造的,但它同時又是母Function的原型。。
function(){}的隱式原型(__proto__)是母Object,母Object是由構造函數 function Object(){}構造的,但函數function Object(){}的隱式原型又是function (){}。。。。
當然除了”先有雞還是先有蛋”,原型鏈中還有一個最最有哲理的問題:
構造函數function Function(){}的構造函數就是他自己,就是說,他自己把自己生下來了。。。。。 = =
至于為什么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的編排好難用 !
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79872.html
摘要:構造函數的屬性指向原型對象原型對象的屬性指向構造函數實例對象的指向原型對象所有引用類型默認都繼承了,而這個繼承也是通過原型鏈實現的。第一種方式是使用操作符,只要用這個操作符來測試實例與原型鏈中出現過的構造函數,結果就會返回。 理解對象 首先對象的定義是:無序屬性的集合,其屬性可以包含基本值、對象或者函數。嚴格來講,這就相當于說對象是一組沒有特定順序的值。對象的每個屬性或方法都有一個名...
摘要:對應的關系圖如下講解了構造函數和原型對象之間的關系,那么實例對象和原型對象之間的關系又是怎么樣的呢下面講解。原型對象的指向的是構造函數和本身沒有屬性,但是其原型對象有該屬性,因此也能獲取到構造函數。 JavaScript進階 - 1. 原型和原型鏈的概念 我們好多經常會被問道JavaScript原型和原型鏈的概念,還有關于繼承,new操作符相關的概念。本文就專門整理了原型和原型鏈的概念...
摘要:在這個情況下我們可能需要使用構造函數,其以指定的模式來創造對象。構造函數也有自己的,值為,也通過其屬性關聯到。從邏輯上來說,這是以棧的形式實現的,它叫作執行上下文棧。 原文:http://dmitrysoshnikov.com/ecmascript/javascript-the-core/ 對象 原型鏈 構造函數 執行上下文棧 執行上下文 變量對象 活動對象 作用域鏈 閉包 Thi...
閱讀 817·2021-10-13 09:39
閱讀 3697·2021-10-12 10:12
閱讀 1741·2021-08-13 15:07
閱讀 1006·2019-08-29 15:31
閱讀 2883·2019-08-26 13:25
閱讀 1776·2019-08-23 18:38
閱讀 1879·2019-08-23 18:25
閱讀 1857·2019-08-23 17:20