合肥阿裏巧巧信息科技有限公司

18955110833

原型和原型鏈,你真的理解嗎(ma)?

2023-04-20 18:06 欄目: 技術學堂 查看()

原型和原型鏈,你真的理解嗎(ma)?

JavaScript中(zhōng),原型(prototype原型鏈(prototype chain是非常重要的概念,但是很多人對它們的掌握都有點不太清楚,本文帶大(dà)家徹底搞懂它們!

 

非常重要的基本概念

首先我(wǒ)(wǒ)們需要搞清楚幾個非常重要的基本概念:

所有的對象都是通過new 函數創建的,且所有的對象都是引用類型

image.png

如上,字面量{}隻是創建對象的一(yī)種語法糖,其實上面的兩種寫法是完全等價的。而Object其實就是一(yī)個js内置的構造函數。

 

所有的函數也都是對象,都是通過new Function創建的(可以稱爲函數對象)

image.png

既然函數也都是對象,又(yòu)稱爲函數對象,而且所有的對象都是通過new 函數創建的,那麽函數又(yòu)是怎麽創建的呢?其實函數都是通過new Function創建的。

如上,同理可得,上面的兩種寫法也是完全等價的。而Object既然是一(yī)個函數,所以它也不例外(wài),它其實就是相當于var Object = new Function()。

 

思考:其實Function也是一(yī)個函數對象,那它又(yòu)是怎麽創建的呢?

結論:Function函數是JS内置的,不需要創建!

綜上所述,可以用一(yī)張圖來形象的展示出它們的關系:

image.png

原型prototype

什麽是原型?

所有的函數都有一(yī)個屬性叫prototype,稱之爲函數原型

 

image.png

 

可以運行如上代碼看看效果,ObjectArray其實都是js内置的構造函數。

 

默認情況下(xià),prototype是一(yī)個普通的object對象

image.png

通過這個特點,我(wǒ)(wǒ)們可以在原型鏈上查找實例化後的對象所對應的構造函數,這個後面會講到。

 

隐式原型__proto__

什麽是隐式原型?

所有的對象都有一(yī)個屬性叫__proto__,稱之爲隐式原型

image.png

 

可以運行如上代碼看看效果,所有的對象在被創建的時候就會有一(yī)個叫__proto__的屬性。

默認情況下(xià),__proto__指向創建該對象的函數的原型,即prototype

 

image.png

運行如上代碼,可以驗證結論。在這裏其實就已經初步形成了一(yī)種三角關系了。

image.png

原型鏈prototype chain

到重點了,上面講了原型和隐式原型,估計有的人會問:搞這麽麻煩,它們有什麽用呢?

image.png

如上代碼,我(wǒ)(wǒ)通過構造函數User實例化了兩個對象user1和user2,因爲它們都有各自屬性和方法,所以user1和user2的sayHello方法是不相等的。

但是我(wǒ)(wǒ)們可以發現,它們的方法其實是一(yī)樣的,完全沒必要創建多次。可以假設一(yī)下(xià),如果我(wǒ)(wǒ)們創建了很多個示例,那麽這樣就會浪費(fèi)内存空間。

現在讓我(wǒ)(wǒ)們來改下(xià)代碼

image.png

 

先看結果,這時候我(wǒ)(wǒ)們會發現,代碼依然能夠如期運行,而且user1user2sayHello方法是完全相等的!

那爲什麽user1user2中(zhōng)并沒有sayHello這個屬性,但是卻能夠運行呢?其實這就是原型鏈的強大(dà)之處。

 

那這個到底是怎麽實現的呢?原型鏈它到底是什麽呢?

 

image.png

運行如上代碼,需要理解以下(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)。

 

讀到這裏,再看上面的問題,爲什麽實例對象user1user2中(zhōng)并沒有sayHello這個方法,但是卻能夠運行?答案就是可以在原型鏈上查找到sayHello這個方法。

通過這種方法,我(wǒ)(wǒ)們就可以實現多個實例對象共享一(yī)個原型對象,這樣就可以極大(dà)的減少對内存空間的消耗且極大(dà)的提升了代碼的可複用性。這就是原型鏈的作用。(其實這也是js實現繼承的基本原理,這裏就不展開(kāi)說了)

核心:原型鏈的全貌

下(xià)面就是js中(zhōng)原型鏈的全貌了

image.png

這裏有兩個比較特殊的點需要注意一(yī)下(xià):

Objectprototype__protp__指向null

Function__protp__指向自身的prototype

仔細對照這張圖,你是否能夠理解如下(xià)問題:

爲什麽任意一(yī)個對象都能調用toString和hasOwnProperty方法了嗎(ma)?

爲什麽任意一(yī)個函數都能調用call和toString方法了嗎(ma)?

爲什麽任意一(yī)個數組都能調用自身沒有的api了嗎(ma),如filter,push等方法了嗎(ma)?

其實答案都是因爲原型上有!

如果你能夠看到這裏并且完全理解了,那麽其實你已經對原型和原型鏈已經有了一(yī)個比較全面的認知(zhī)了。


掃二維碼與項目經理溝通

我(wǒ)(wǒ)們在微信上24小(xiǎo)時期待你的聲音

解答本文疑問/技術咨詢/運營咨詢/技術建議/互聯網交流

鄭重申明:善達信息以外(wài)的任何單位或個人,不得使用該案例作爲工(gōng)作成功展示!