圖解JavaScript:對象、繼承與原型鏈

根據(jù)理論繪制示意圖
構(gòu)造函數(shù)、原型
每個function都有一個原型,function的
prototype
屬性指向了這個function的原型構(gòu)造函數(shù)是function的一個種類,所以
每個object都是由某個構(gòu)造函數(shù)產(chǎn)生的,object的
__proto__
屬性指向產(chǎn)生這個object的構(gòu)造函數(shù)的原型
●?每個構(gòu)造函數(shù)都有一個原型,構(gòu)造函數(shù)的prototype
屬性指向了這個構(gòu)造函數(shù)的原型
注:原型是從屬于function的,應當說“function的原型”,而“object的原型”是“object的構(gòu)造函數(shù)的原型”的簡略說法。

function是object的一個種類,所以
產(chǎn)生function的構(gòu)造函數(shù)是Function,即Function是function的構(gòu)造函數(shù),所以
Function是function的構(gòu)造函數(shù),所以Function是一個function,所以Function是它本身的構(gòu)造函數(shù),所以
● 每個function都是由某個構(gòu)造函數(shù)產(chǎn)生的,function的__proto__
屬性指向了產(chǎn)生這個function的構(gòu)造函數(shù)的原型
●?Function有一個原型,Function的prototype
屬性指向了Function的原型
●?每個function都是由Function產(chǎn)生的,function的__proto__
屬性指向了Function的原型
●?Function的__proto__
屬性指向了Function的原型

類、實例
JavaScript沒有專門的類定義。
假如有一個名為Person的構(gòu)造函數(shù),由Person產(chǎn)生了一個名為person1的object,我們可以認為Person是一個類的名字,person1是Person類的一個實例。

圖中黃色框表示function,綠色框表示object。
結(jié)合前文內(nèi)容可知,person1是Person類的一個實例,表現(xiàn)為person1的__proto__
屬性指向Person的原型。
那么從圖中可以發(fā)現(xiàn),Person的__proto__
屬性指向Function的原型,所以說Person(一個function)是Function類的一個實例。
這是因為產(chǎn)生function的構(gòu)造函數(shù)是Function,所以所有的function都是Function的實例。
注:prototype
是構(gòu)造函數(shù)的屬性,__proto__
是實例的屬性。然而,構(gòu)造函數(shù)本身是Function的實例,所以構(gòu)造函數(shù)也有__proto__
屬性。
繼承
這里直接給出結(jié)論:Teacher類繼承Person類,表現(xiàn)為Teacher的原型的__proto__
屬性指向Person的原型。

圖中綠色箭頭表示實例化自,藍色箭頭表示繼承。
觀察圖片左下角,我們還可以這樣說,Teacher類繼承Person類,表現(xiàn)為Teacher的原型是Person類的一個實例。這個說法在后面還會提到。
繼續(xù)給出如下信息:
Function類繼承Object類,即
Object是構(gòu)造函數(shù),所以Object是一個function,所以Function是Object的構(gòu)造函數(shù),所以
所有的類都直接或間接地繼承Object類,沒有指定繼承來源的類(如Person類)都默認直接繼承Object類,所以
Object類是繼承的終點,Object的原型的
__proto__
屬性為null
●?Function的原型的__proto__
屬性指向Object的原型
●?Object的__proto__
屬性指向了Function的原型
●?Person的原型的__proto__
屬性指向Object的原型

object、function
上面所說的“名為Person的function”,實際上是一個“名為Person的指向一個function的變量”
同理,“名為person1的object”,實際上是一個“名為person1的指向一個object的變量”
object本身沒有名字,而function本身有名字,保存在function的
name
屬性中function的
length
屬性中保存函數(shù)的形參個數(shù)所有object都會從它的構(gòu)造函數(shù)的原型上繼承一個
constructor
屬性,保存對創(chuàng)建該object的構(gòu)造函數(shù)的引用,換句話說,
●?構(gòu)造函數(shù)的原型的 constructor
屬性指向該構(gòu)造函數(shù)本身

在瀏覽器中進行實驗
Object
首先嘗試在瀏覽器控制臺打印Object
,它應該是一個指向Object類的構(gòu)造函數(shù)的內(nèi)置變量。

輸出的“?”表明這是一個function,“Object()”表明函數(shù)名為“Object”且形參列表為空。后面的大括號中本應輸出函數(shù)體,由于Object()是內(nèi)置函數(shù),故顯示為“[native code]”。
如何把Object類的構(gòu)造函數(shù)作為一個object而不是function打印出來?我們可以手動創(chuàng)建一個object,令它的一個成員變量(即“屬性”)指向Object類的構(gòu)造函數(shù)。
執(zhí)行{Object}
,它是{Object: Object}
的簡寫,表示使用對象字面量語法(大括號)創(chuàng)建一個object,并把內(nèi)置變量Object
(指向Object類的構(gòu)造函數(shù))賦值給這個object的名為Object
的屬性。圖示如下:

控制臺輸出如下:

輸出的“{Object: ?}”表明這是一個object,且它有一個名為Object
的屬性,該屬性指向一個function(或者說“該屬性是一個function”)。
將其展開,可以發(fā)現(xiàn)它還有一個名為__proto__
的屬性(注意縮進),且__proto__
屬性指向一個Object類的實例(即object)。而Object
屬性指向一個函數(shù)名為“Object”且形參列表為空的function,它就是Object類的構(gòu)造函數(shù)。這個Object
屬性可以被展開,即達到了將一個function作為object打印出來的目的。
觀察Object類的構(gòu)造函數(shù),發(fā)現(xiàn)它的確有length
、name
、prototype
、__proto__
屬性:
length
屬性為數(shù)值1(注意并不是理論值“0”)name
屬性為字符串“Object”prototype
屬性指向一個object,它是Object這個構(gòu)造函數(shù)的原型__proto__
屬性指向一個function,根據(jù)前文所述,它是Function這個構(gòu)造函數(shù)的原型(它并不是一個object,是例外情況),稍后進行驗證
我們手動創(chuàng)建的object必為Object類的實例,所以它的__proto__
屬性應當與Object類的構(gòu)造函數(shù)的prototype
屬性指向同一個object。
我們將它們各自展開,發(fā)現(xiàn)它們完全一致:

驗證如下:

接著驗證Object的原型的 constructor
屬性指向Object本身:


注:訪問一個對象的屬性時,如果該對象內(nèi)部不存在這個屬性,那么就會去它的__proto__
屬性所指向的那個對象(可以理解為父對象)里找,如果父對象也不存在這個屬性,則接著去父對象的__proto__
屬性所指向的那個對象(可以理解為爺爺對象)里找,如果還沒找到,則繼續(xù)往上找……直到原型鏈頂端null
。
最后,Object的原型的__proto__
屬性為null
,而不是undefined
(其實 __proto__
是個定義在 Object.prototype
上的訪問器屬性,稍后會進行說明)。

選取幾個方法作為代表,完善示意圖:

Function
執(zhí)行{Function}
,它是{Function: Function}
的簡寫,表示使用對象字面量語法(大括號)創(chuàng)建一個object,并把內(nèi)置變量Function
(指向Function類的構(gòu)造函數(shù))賦值給這個object的名為Function
的屬性。圖示如下:

控制臺輸出如下:

輸出的“{Function: ?}”表明這是一個object,且它有一個名為Function
的屬性,該屬性指向一個function(或者說“該屬性是一個function”)。
將其展開,可以發(fā)現(xiàn)Function
屬性指向一個函數(shù)名為“Function”且形參列表為空的function,它就是Function類的構(gòu)造函數(shù)。
觀察Function類的構(gòu)造函數(shù),發(fā)現(xiàn)它的確有length
、name
、prototype
、__proto__
屬性:
length
屬性為數(shù)值1(注意并不是理論值“0”)name
屬性為字符串“Function”prototype
屬性指向一個function,它是Function這個構(gòu)造函數(shù)的原型__proto__
屬性指向一個function,它同樣是Function這個構(gòu)造函數(shù)的原型
Function類的構(gòu)造函數(shù)的prototype
、__proto__
屬性與Object類的構(gòu)造函數(shù)的__proto__
屬性應當指向同一個function,驗證如下:



觀察Function的原型,由于它是一個function,所以有length
、name
、__proto__
屬性,然而特殊之處是它沒有prototype
屬性:
length
屬性為數(shù)值0name
屬性為空字符串__proto__
屬性指向一個object,根據(jù)前文所述,它是Object的原型,驗證如下:


Function的原型雖然是一個function,但是沒有prototype
屬性:

最后,驗證Function的原型的 constructor
屬性指向Function本身:


那么Object.constructor
指向誰?Function.constructor
指向誰?

選取幾個方法作為代表,完善示意圖(注意Function的原型改用黃色框,表示它是一個function):

使用Javascript中的原型
https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes#使用javascript中的原型
在控制臺執(zhí)行如下代碼,創(chuàng)建doSomething函數(shù):


我們可以添加一些屬性到doSomething的原型上面:


然后,我們可以使用new運算符來在現(xiàn)在這個原型的基礎(chǔ)上,創(chuàng)建一個doSomething的實例:


就像上面看到的,
doSomeInstancing
的__proto__
屬性就是doSomething.prototype
. 但是這又有什么用呢? 好吧,當你訪問doSomeInstancing
的一個屬性, 瀏覽器首先查找doSomeInstancing
是否有這個屬性. 如果doSomeInstancing
沒有這個屬性, 然后瀏覽器就會在doSomeInstancing
的__proto__
中查找這個屬性(也就是 doSomething.prototype). 如果 doSomeInstancing 的__proto__
有這個屬性, 那么 doSomeInstancing 的__proto__
上的這個屬性就會被使用. 否則, 如果 doSomeInstancing 的__proto__
沒有這個屬性, 瀏覽器就會去查找 doSomeInstancing 的__proto__
的__proto__
,看它是否有這個屬性. 默認情況下, 所有函數(shù)的原型屬性的__proto__
就是window.Object.prototype
. 所以 doSomeInstancing 的__proto__
的__proto__
(也就是 doSomething.prototype 的__proto__
(也就是Object.prototype
)) 會被查找是否有這個屬性. 如果沒有在它里面找到這個屬性, 然后就會在 doSomeInstancing 的__proto__
的__proto__
的__proto__
里面查找. 然而這有一個問題: doSomeInstancing 的__proto__
的__proto__
的__proto__
不存在. 最后, 原型鏈上面的所有的__proto__
都被找完了, 瀏覽器所有已經(jīng)聲明了的__proto__
上都不存在這個屬性,然后就得出結(jié)論,這個屬性是undefined
.
執(zhí)行如下代碼:
輸出結(jié)果為:
new 操作符的作用
當執(zhí)行:
JavaScript 實際上執(zhí)行的是:
可以認為在使用new 操作符創(chuàng)建一個實例的過程中,存在如下的中間狀態(tài):

Object.create()
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create
Object.create()
方法創(chuàng)建一個新對象,使用現(xiàn)有的對象來提供新創(chuàng)建的對象的__proto__
。






注意d.__proto__
是undefined
,而不是null
。




注意控制臺把f作為Function類的一個實例打印出來。


[未完]