欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

一文帶你搞懂JavaScript中的原型和原型鏈

 更新時(shí)間:2023年08月28日 08:25:00   作者:LBruse  
JavaScript是基于原型繼承的語(yǔ)言,每個(gè)對(duì)象都有一個(gè)原型(prototype),本文則是重點(diǎn)對(duì)prototype相關(guān)知識(shí)點(diǎn)做拆解和梳理,感興趣的可以了解下

原型和原型鏈

JavaScriptJava這種面向?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)

輸出WechatUserprototype屬性,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è)classprototype都會(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í)的存在了,所以雖然Objectprototype也有__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 WechatUserprototype

到此這篇關(guān)于一文帶你搞懂JavaScript中的原型和原型鏈的文章就介紹到這了,更多相關(guān)JavaScript原型和原型鏈內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論