原型和原型鏈,你真的理解嗎(ma)?
原型和原型鏈,你真的理解嗎(ma)?
在JavaScript
中(zhōng),原型(
prototype
)
和原型鏈(
prototype chain
)
是非常重要的概念,但是很多人對它們的掌握都有點不太清楚,本文帶大(dà)家徹底搞懂它們!
非常重要的基本概念
首先我(wǒ)(wǒ)們需要搞清楚幾個非常重要的基本概念:
所有的對象都是通過new
函數
創建的,且所有的對象都是引用類型
如上,字面量{}
隻是創建對象的一(yī)種語法糖,其實上面的兩種寫法是完全等價的。而Object
其實就是一(yī)個js
内置的構造函數。
所有的函數也都是對象,都是通過new Function
創建的(可以稱爲函數對象)
既然函數也都是對象,又(yòu)稱爲函數對象,而且所有的對象都是通過new 函數創建的,那麽函數又(yòu)是怎麽創建的呢?其實函數都是通過new Function創建的。
如上,同理可得,上面的兩種寫法也是完全等價的。而Object既然是一(yī)個函數,所以它也不例外(wài),它其實就是相當于var Object = new Function()。
思考:其實Function
也是一(yī)個函數對象,那它又(yòu)是怎麽創建的呢?
結論:Function函數是JS内置的,不需要創建!
綜上所述,可以用一(yī)張圖來形象的展示出它們的關系:
原型prototype
什麽是原型?
所有的函數都有一(yī)個屬性叫prototype
,稱之爲函數原型
可以運行如上代碼看看效果,Object
和Array
其實都是js
内置的構造函數。
默認情況下(xià),prototype
是一(yī)個普通的object
對象
通過這個特點,我(wǒ)(wǒ)們可以在原型鏈上查找實例化後的對象所對應的構造函數,這個後面會講到。
隐式原型__proto__
什麽是隐式原型?
所有的對象都有一(yī)個屬性叫__proto__
,稱之爲隐式原型
可以運行如上代碼看看效果,所有的對象在被創建的時候就會有一(yī)個叫__proto__
的屬性。
默認情況下(xià),__proto__
指向創建該對象的函數的原型,即prototype
運行如上代碼,可以驗證結論。在這裏其實就已經初步形成了一(yī)種三角關系了。
原型鏈prototype chain
到重點了,上面講了原型和隐式原型,估計有的人會問:搞這麽麻煩,它們有什麽用呢?
如上代碼,我(wǒ)(wǒ)通過構造函數User實例化了兩個對象user1和user2,因爲它們都有各自屬性和方法,所以user1和user2的sayHello方法是不相等的。
但是我(wǒ)(wǒ)們可以發現,它們的方法其實是一(yī)樣的,完全沒必要創建多次。可以假設一(yī)下(xià),如果我(wǒ)(wǒ)們創建了很多個示例,那麽這樣就會浪費(fèi)内存空間。
現在讓我(wǒ)(wǒ)們來改下(xià)代碼
先看結果,這時候我(wǒ)(wǒ)們會發現,代碼依然能夠如期運行,而且user1
和user2
的sayHello
方法是完全相等的!
那爲什麽user1
和user2
中(zhōng)并沒有sayHello
這個屬性,但是卻能夠運行呢?其實這就是原型鏈
的強大(dà)之處。
那這個到底是怎麽實現的呢?原型鏈它到底是什麽呢?
運行如上代碼,需要理解以下(xià)幾點:
首先根據上面所說的,user1是個實例化的對象,那麽user1.proto一(yī)定是指向User.prototype
那User.prototype其實也是個對象,既然是對象就有proto屬性,那麽User.prototype.proto一(yī)定是指向Object.prototype,也可以通過user1.proto.proto來訪問,即原型對象的原型對象
同理,那user1.proto.proto也是個對象,也有proto屬性,不過這裏就指向null了
因爲每個一(yī)對象都有隐式原型,隐式原型的指向就形成了一(yī)個鏈條,稱之爲原型鏈
所以上面的示例就會按照原型鏈繼續依次查找
當我(wǒ)(wǒ)們訪問一(yī)個對象的成員(yuán)時:
首先看該對象自身是否擁有該成員(yuán),如果有直接使用;
如果沒有再看該對象的隐式原型是否擁有該成員(yuán),如果有直接使用;
如果還沒有就會繼續查找原型對象的原型對象(該對象的隐式原型所指向的原型對象),直到找到爲止;
這樣形成的鏈條就被稱爲原型鏈(prototype chain)。
讀到這裏,再看上面的問題,爲什麽實例對象user1
和user2
中(zhōng)并沒有sayHello
這個方法,但是卻能夠運行?答案就是可以在原型鏈上查找到sayHello
這個方法。
通過這種方法,我(wǒ)(wǒ)們就可以實現多個實例對象共享一(yī)個原型對象,這樣就可以極大(dà)的減少對内存空間的消耗且極大(dà)的提升了代碼的可複用性。這就是原型鏈
的作用。(其實這也是js
實現繼承
的基本原理,這裏就不展開(kāi)說了)
核心:原型鏈
的全貌
下(xià)面就是js
中(zhōng)原型鏈
的全貌了
這裏有兩個比較特殊的點需要注意一(yī)下(xià):
Object
的prototype
的__protp__
指向null
Function
的__protp__
指向自身的prototype
仔細對照這張圖,你是否能夠理解如下(xià)問題:
爲什麽任意一(yī)個對象都能調用toString和hasOwnProperty方法了嗎(ma)?
爲什麽任意一(yī)個函數都能調用call和toString方法了嗎(ma)?
爲什麽任意一(yī)個數組都能調用自身沒有的api了嗎(ma),如filter,push等方法了嗎(ma)?
其實答案都是因爲原型上有!
如果你能夠看到這裏并且完全理解了,那麽其實你已經對原型和原型鏈已經有了一(yī)個比較全面的認知(zhī)了。
![](static/picture/1-230413122Q4949.png)
掃二維碼與項目經理溝通
我(wǒ)(wǒ)們在微信上24小(xiǎo)時期待你的聲音
解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯網交流