一文帶你搞懂JavaScript中的原型和原型鏈
原型和原型鏈
JavaScript
和Java
這種面向對象的語言不太一樣,JavaScript
是基于原型繼承
的語言。雖然在ES6
及之后,class
、extend
語法也漸漸取代了之前修改prototype
實現繼承的方式,但本質上還是通過修改prototype
來實現繼承的。本文則是重點對prototype
相關知識點做拆解和梳理
通過class聲明并實例化對象
在java
中聲明并實例化對象是這樣的
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; } // 打印輸出當前微信用戶信息 public void print() { System.out.println("name: " + this.name + " openId: " + this.openId + " avatar: " + this.avatar); } // java程序啟動入口 public static void main(String[] args) { WechatUser user = new WechatUser("Bruse", "opwrogfajadfoa113", "avatar-1.png"); user.print(); } }
JavaScript
實例化對象是這樣的
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
定義對象模板,定義了對象該有的屬性和方法,然后通過new
關鍵字將對象進行實例化
extend繼承
通過extend
便可讓不同的class
實現繼承關系,達到代碼復用的效果
class MiniProgramUser extends WechatUser { constructor(name, openId, avatar, appId) { // 調用父類的構造函數 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
進行聲明和實例化對象,但其實class
只不過是所謂的語法糖
,本質上JavaScript
并不會像Java
那樣基于類實現面向對象。
class WechatUser{}
實際上也還是個函數,class
只是個語法糖
,它等同于
function WechatUser() {}
隱式原型和顯式原型
隱式原型
const user = new WechatUser('Bruse', 'sfoqioiooa1', 'avatar-1.jpg') console.log('user.__proto__ ', user.__proto__)
以上邊的代碼為例,其實在創(chuàng)建出來的user
對象中,有一個__protocol__
屬性,這個即每個實例都有的隱式原型
,打印輸出如下
顯式原型
console.log('WechatUser.prototype ',WechatUser.prototype)
輸出WechatUser
的prototype
屬性,prototype
即原型
的意思,每個class(function)都有顯式原型
,結果如下
隱式原型和顯式原型的關系
可以看到無論是user.__proto__
還是WechatUser.prototype
,都有print
方法,constructor
都是WechatUser
,那么是否也就意味著user.__proto__[實例的隱式原型]
===WechatUser.prototype[class的顯式原型]
?
console.log('equals ', user.__proto__ === WechatUser.prototype)
輸出為equals true
,證明user.__proto__
的確等于WechatUser.prototype
,引用地址是同一個。
這里的關系可以用下圖表示
- 每個class都有顯式原型prototype
- 每個實例都有隱式原型__proto__
- 實例的__proto__指向其所對應的class的prototype
基于原型的屬性/方法查找
基于上邊的內容,其實可以總結出:獲取實例屬性或執(zhí)行方法時,會先在實例自身進行尋找有沒有相關的屬性或方法,有的話就獲取或調用,沒有的話,會順著實例的__proto__往上找到實例對應的class的prototype,并對prototype進行變量查找或方法調用。這也就是所謂的基于原型的繼承方式
原型鏈
搞明白了基于原型是怎么回事后,那接下來就是多個原型之間的關聯(lián)形成原型鏈
const miniUser = new MiniProgramUser('Bruse', 'sfoqioiooa1', 'avatar-1.jpg', "appId13322")
這里聲明一個miniUser
,它是基于MiniProgramUser
實例化的,所以miniUser.__proto__
相等于MiniProgramUser.prototype
。
但其實MiniProgramUser.prototype
也有一個__proto__
屬性,輸出如下
miniUser
的隱式原型等于MiniProgramUser
的顯式原型,可MiniProgramUser
的顯式原型的隱式原型(是有點繞)
又等于誰呢?
因為定義MiniProgramUser
這個class
的時候,使用了extend
關鍵詞,表示其繼承于WechatUser
,而且WechatUser
也有自己的prototype
,輸出如下
那么嘗試將MiniProgramUser.prototype.__proto__
與WechatUser.prototype
比較,結果如下
console.log('equals', MiniProgramUser.prototype.__proto__ === WechatUser.prototype)
輸出equals true
,證明MiniProgramUser
的顯式原型
的隱式原型
等于WechatUser
的顯示原型,隱約間形成了一條原型鏈
原型鏈的盡頭
那么在這里其實也可以做一個舉一反三,既然每個class
的prototype
都會有一個__proto__
,既然WechatUser
這個class
并沒有在代碼中顯式指定繼承于哪個class
,那么WechatUser.prototype.__proto__
應該就等同于Object.prototype
,輸出驗證如下
console.log(WechatUser.prototype.__proto__ === Object.prototype)
結果為true
這里也有一個知識點,因為Object
在JavaScript
所有對象中是最頂級的存在了,所以雖然Object
的prototype
也有__proto__
,但它實際上不指向任何對象,僅為null
console.log('Object.prototype.__proto__ ', Object.prototype.__proto__)
輸出 Object.prototype.__proto__ null
原型鏈總結
這里可以用一張圖清楚表示形成的原型鏈是怎么樣的
typeof vs instanceof
typeof
typeof
是用來判斷當前變量是何種類型?基本類型?引用類型?也就是說它能
- 識別所有值類型
- 識別函數
- 判斷是否引用類型,但只能判斷出為
object
,沒法再細分
判斷值類型
const name = 'Bruse' typeof name // 輸出'string' const sym = Symbol('sym') typeof sym // 輸出'symbol' const done = false typeof done // 輸出'boolean'
識別函數
typeof console.log // 'function' function print () { console.log(1+1) } typeof print // 'function'
引用類型則非?;\統(tǒng)地識別為object
typeof null // 'object' typeof [1,2,3] // 'object' typeof {name: 'Bruse', age: 16} // 'object'
instanceof
instanceof也是用作類型判斷的,只不過比typeof
更精準了,它可以判斷出當前變量是否該class
構建出來的。
[] instanceof Array // true [] instanceof Object // true {} instanceof Object // true miniUser instanceof MiniProgramUser // true miniUser instanceof WechatUser // true miniUser instanceof Object // true
結合出上邊原型鏈
的知識,其實可以搞清楚instanceof
的原理,其實就是根據instanceof
左邊變量miniUser
的原型鏈一層層往上找,判斷prototype
或__proto__
是否等于instanceof
右邊的class
WechatUser
的prototype
到此這篇關于一文帶你搞懂JavaScript中的原型和原型鏈的文章就介紹到這了,更多相關JavaScript原型和原型鏈內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
JavaScript實現頁面滾動圖片加載(仿lazyload效果)
網上的很多這樣的效果都是用jQuery的方法,可是如果不用jQuery的站長難道就不能用這種方法了么2011-07-07