一文帶你搞懂JavaScript中的原型和原型鏈
原型和原型鏈
JavaScript
和Java
這種面向?qū)ο蟮恼Z(yǔ)言不太一樣,JavaScript
是基于原型繼承
的語(yǔ)言。雖然在ES6
及之后,class
、extend
語(yǔ)法也漸漸取代了之前修改prototype
實(shí)現(xiàn)繼承的方式,但本質(zhì)上還是通過(guò)修改prototype
來(lái)實(shí)現(xiàn)繼承的。本文則是重點(diǎn)對(duì)prototype
相關(guān)知識(shí)點(diǎn)做拆解和梳理
通過(guò)class聲明并實(shí)例化對(duì)象
在java
中聲明并實(shí)例化對(duì)象是這樣的
package geek.springboot.application.entity; public class WechatUser { private String name; private String openId; private String avatar; public WechatUser(String name, String openId, String avatar) { this.name = name; this.openId = openId; this.avatar = avatar; } // 打印輸出當(dāng)前微信用戶信息 public void print() { System.out.println("name: " + this.name + " openId: " + this.openId + " avatar: " + this.avatar); } // java程序啟動(dòng)入口 public static void main(String[] args) { WechatUser user = new WechatUser("Bruse", "opwrogfajadfoa113", "avatar-1.png"); user.print(); } }
JavaScript
實(shí)例化對(duì)象是這樣的
class WechatUser { constructor(name, openId, avatar) { this.name = name this.openId = openId this.avatar = avatar } print(){ console.log(`name: ${this.name} openId:${this.openId} avatar: ${this.avatar}`) } } const user = new WechatUser('Bruse', 'sfoqioiooa1', 'avatar-1.jpg') user.print()
輸出name: Bruse openId:sfoqioiooa1 avatar: avatar-1.jpg
從語(yǔ)法上看兩者差別并不大,class
定義對(duì)象模板,定義了對(duì)象該有的屬性和方法,然后通過(guò)new
關(guān)鍵字將對(duì)象進(jìn)行實(shí)例化
extend繼承
通過(guò)extend
便可讓不同的class
實(shí)現(xiàn)繼承關(guān)系,達(dá)到代碼復(fù)用的效果
class MiniProgramUser extends WechatUser { constructor(name, openId, avatar, appId) { // 調(diào)用父類的構(gòu)造函數(shù) super(name, openId, avatar); this.appId = appId } // 重寫(xiě)父類方法 print() { console.log(`name: ${this.name} openId:${this.openId} avatar: ${this.avatar} appId: ${this.appId}`) } }
輸出name: Bruse openId:sfoqioiooa1 avatar: avatar-1.jpg appId: appId13322
原型
以上示例演示了如何用class
進(jìn)行聲明和實(shí)例化對(duì)象,但其實(shí)class
只不過(guò)是所謂的語(yǔ)法糖
,本質(zhì)上JavaScript
并不會(huì)像Java
那樣基于類實(shí)現(xiàn)面向?qū)ο蟆?/p>
class WechatUser{}
實(shí)際上也還是個(gè)函數(shù),class
只是個(gè)語(yǔ)法糖
,它等同于
function WechatUser() {}
隱式原型和顯式原型
隱式原型
const user = new WechatUser('Bruse', 'sfoqioiooa1', 'avatar-1.jpg') console.log('user.__proto__ ', user.__proto__)
以上邊的代碼為例,其實(shí)在創(chuàng)建出來(lái)的user
對(duì)象中,有一個(gè)__protocol__
屬性,這個(gè)即每個(gè)實(shí)例都有的隱式原型
,打印輸出如下
顯式原型
console.log('WechatUser.prototype ',WechatUser.prototype)
輸出WechatUser
的prototype
屬性,prototype
即原型
的意思,每個(gè)class(function)都有顯式原型
,結(jié)果如下
隱式原型和顯式原型的關(guān)系
可以看到無(wú)論是user.__proto__
還是WechatUser.prototype
,都有print
方法,constructor
都是WechatUser
,那么是否也就意味著user.__proto__[實(shí)例的隱式原型]
===WechatUser.prototype[class的顯式原型]
?
console.log('equals ', user.__proto__ === WechatUser.prototype)
輸出為equals true
,證明user.__proto__
的確等于WechatUser.prototype
,引用地址是同一個(gè)。
這里的關(guān)系可以用下圖表示
- 每個(gè)class都有顯式原型prototype
- 每個(gè)實(shí)例都有隱式原型__proto__
- 實(shí)例的__proto__指向其所對(duì)應(yīng)的class的prototype
基于原型的屬性/方法查找
基于上邊的內(nèi)容,其實(shí)可以總結(jié)出:獲取實(shí)例屬性或執(zhí)行方法時(shí),會(huì)先在實(shí)例自身進(jìn)行尋找有沒(méi)有相關(guān)的屬性或方法,有的話就獲取或調(diào)用,沒(méi)有的話,會(huì)順著實(shí)例的__proto__往上找到實(shí)例對(duì)應(yīng)的class的prototype,并對(duì)prototype進(jìn)行變量查找或方法調(diào)用。這也就是所謂的基于原型的繼承方式
原型鏈
搞明白了基于原型是怎么回事后,那接下來(lái)就是多個(gè)原型之間的關(guān)聯(lián)形成原型鏈
const miniUser = new MiniProgramUser('Bruse', 'sfoqioiooa1', 'avatar-1.jpg', "appId13322")
這里聲明一個(gè)miniUser
,它是基于MiniProgramUser
實(shí)例化的,所以miniUser.__proto__
相等于MiniProgramUser.prototype
。
但其實(shí)MiniProgramUser.prototype
也有一個(gè)__proto__
屬性,輸出如下
miniUser
的隱式原型等于MiniProgramUser
的顯式原型,可MiniProgramUser
的顯式原型的隱式原型(是有點(diǎn)繞)
又等于誰(shuí)呢?
因?yàn)槎xMiniProgramUser
這個(gè)class
的時(shí)候,使用了extend
關(guān)鍵詞,表示其繼承于WechatUser
,而且WechatUser
也有自己的prototype
,輸出如下
那么嘗試將MiniProgramUser.prototype.__proto__
與WechatUser.prototype
比較,結(jié)果如下
console.log('equals', MiniProgramUser.prototype.__proto__ === WechatUser.prototype)
輸出equals true
,證明MiniProgramUser
的顯式原型
的隱式原型
等于WechatUser
的顯示原型,隱約間形成了一條原型鏈
原型鏈的盡頭
那么在這里其實(shí)也可以做一個(gè)舉一反三,既然每個(gè)class
的prototype
都會(huì)有一個(gè)__proto__
,既然WechatUser
這個(gè)class
并沒(méi)有在代碼中顯式指定繼承于哪個(gè)class
,那么WechatUser.prototype.__proto__
應(yīng)該就等同于Object.prototype
,輸出驗(yàn)證如下
console.log(WechatUser.prototype.__proto__ === Object.prototype)
結(jié)果為true
這里也有一個(gè)知識(shí)點(diǎn),因?yàn)?code>Object在JavaScript
所有對(duì)象中是最頂級(jí)的存在了,所以雖然Object
的prototype
也有__proto__
,但它實(shí)際上不指向任何對(duì)象,僅為null
console.log('Object.prototype.__proto__ ', Object.prototype.__proto__)
輸出 Object.prototype.__proto__ null
原型鏈總結(jié)
這里可以用一張圖清楚表示形成的原型鏈?zhǔn)窃趺礃拥?/p>
typeof vs instanceof
typeof
typeof
是用來(lái)判斷當(dāng)前變量是何種類型?基本類型?引用類型?也就是說(shuō)它能
- 識(shí)別所有值類型
- 識(shí)別函數(shù)
- 判斷是否引用類型,但只能判斷出為
object
,沒(méi)法再細(xì)分
判斷值類型
const name = 'Bruse' typeof name // 輸出'string' const sym = Symbol('sym') typeof sym // 輸出'symbol' const done = false typeof done // 輸出'boolean'
識(shí)別函數(shù)
typeof console.log // 'function' function print () { console.log(1+1) } typeof print // 'function'
引用類型則非?;\統(tǒng)地識(shí)別為object
typeof null // 'object' typeof [1,2,3] // 'object' typeof {name: 'Bruse', age: 16} // 'object'
instanceof
instanceof也是用作類型判斷的,只不過(guò)比typeof
更精準(zhǔn)了,它可以判斷出當(dāng)前變量是否該class
構(gòu)建出來(lái)的。
[] instanceof Array // true [] instanceof Object // true {} instanceof Object // true miniUser instanceof MiniProgramUser // true miniUser instanceof WechatUser // true miniUser instanceof Object // true
結(jié)合出上邊原型鏈
的知識(shí),其實(shí)可以搞清楚instanceof
的原理,其實(shí)就是根據(jù)instanceof
左邊變量miniUser
的原型鏈一層層往上找,判斷prototype
或__proto__
是否等于instanceof
右邊的class
WechatUser
的prototype
到此這篇關(guān)于一文帶你搞懂JavaScript中的原型和原型鏈的文章就介紹到這了,更多相關(guān)JavaScript原型和原型鏈內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JavaScript實(shí)現(xiàn)頁(yè)面滾動(dòng)圖片加載(仿lazyload效果)
網(wǎng)上的很多這樣的效果都是用jQuery的方法,可是如果不用jQuery的站長(zhǎng)難道就不能用這種方法了么2011-07-07使用JavaScript制作待辦事項(xiàng)列表的示例代碼
這篇文章主要介紹了如何使用 JavaScript創(chuàng)建待辦事項(xiàng)列表HTML的完整信息和教程,文中但是示例代碼講解詳細(xì),感興趣的同學(xué)可以動(dòng)手試一試2022-01-0120行JS代碼實(shí)現(xiàn)粘貼板復(fù)制功能
本文給大家分析20行JS代碼實(shí)現(xiàn)粘貼板功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2018-02-02用javascript對(duì)一個(gè)json數(shù)組深度賦值示例
本節(jié)主要介紹了用javascript對(duì)一個(gè)json數(shù)組深度賦值的具體實(shí)現(xiàn),需要的朋友可以參考下2014-07-07JavaScript輸出當(dāng)前時(shí)間Unix時(shí)間戳的方法
這篇文章主要介紹了JavaScript輸出當(dāng)前時(shí)間Unix時(shí)間戳的方法,涉及javascript中Date及getTime等函數(shù)操作時(shí)間的使用技巧,需要的朋友可以參考下2015-04-04微信小程序?qū)崿F(xiàn)驗(yàn)證碼獲取倒計(jì)時(shí)效果
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)驗(yàn)證碼獲取倒計(jì)時(shí)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-02-02