一文帶你搞懂JavaScript中的原型和原型鏈
原型和原型鏈
JavaScript和Java這種面向?qū)ο蟮恼Z言不太一樣,JavaScript是基于原型繼承的語言。雖然在ES6及之后,class、extend語法也漸漸取代了之前修改prototype實(shí)現(xiàn)繼承的方式,但本質(zhì)上還是通過修改prototype來實(shí)現(xiàn)繼承的。本文則是重點(diǎn)對(duì)prototype相關(guān)知識(shí)點(diǎn)做拆解和梳理
通過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
從語法上看兩者差別并不大,class定義對(duì)象模板,定義了對(duì)象該有的屬性和方法,然后通過new關(guān)鍵字將對(duì)象進(jìn)行實(shí)例化
extend繼承
通過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
}
// 重寫父類方法
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只不過是所謂的語法糖,本質(zhì)上JavaScript并不會(huì)像Java那樣基于類實(shí)現(xiàn)面向?qū)ο蟆?/p>
class WechatUser{}實(shí)際上也還是個(gè)函數(shù),class只是個(gè)語法糖,它等同于
function WechatUser() {}隱式原型和顯式原型
隱式原型
const user = new WechatUser('Bruse', 'sfoqioiooa1', 'avatar-1.jpg')
console.log('user.__proto__ ', user.__proto__)以上邊的代碼為例,其實(shí)在創(chuàng)建出來的user對(duì)象中,有一個(gè)__protocol__屬性,這個(gè)即每個(gè)實(shí)例都有的隱式原型,打印輸出如下

顯式原型
console.log('WechatUser.prototype ',WechatUser.prototype)輸出WechatUser的prototype屬性,prototype即原型的意思,每個(gè)class(function)都有顯式原型,結(jié)果如下

隱式原型和顯式原型的關(guān)系
可以看到無論是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)行尋找有沒有相關(guān)的屬性或方法,有的話就獲取或調(diào)用,沒有的話,會(huì)順著實(shí)例的__proto__往上找到實(shí)例對(duì)應(yīng)的class的prototype,并對(duì)prototype進(jìn)行變量查找或方法調(diào)用。這也就是所謂的基于原型的繼承方式
原型鏈
搞明白了基于原型是怎么回事后,那接下來就是多個(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)繞)又等于誰呢?
因?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并沒有在代碼中顯式指定繼承于哪個(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是用來判斷當(dāng)前變量是何種類型?基本類型?引用類型?也就是說它能
- 識(shí)別所有值類型
- 識(shí)別函數(shù)
- 判斷是否引用類型,但只能判斷出為
object,沒法再細(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也是用作類型判斷的,只不過比typeof更精準(zhǔn)了,它可以判斷出當(dāng)前變量是否該class構(gòu)建出來的。
[] 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)頁面滾動(dòng)圖片加載(仿lazyload效果)
網(wǎng)上的很多這樣的效果都是用jQuery的方法,可是如果不用jQuery的站長難道就不能用這種方法了么2011-07-07
使用JavaScript制作待辦事項(xiàng)列表的示例代碼
這篇文章主要介紹了如何使用 JavaScript創(chuàng)建待辦事項(xiàng)列表HTML的完整信息和教程,文中但是示例代碼講解詳細(xì),感興趣的同學(xué)可以動(dòng)手試一試2022-01-01
20行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-07
JavaScript輸出當(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

