理解js中的[[Prototype]]

在面向?qū)ο笤O(shè)計(jì)中,最多被使用的就是類了。但是js中沒(méi)有類的概念即纲,也就沒(méi)有繼承等一系列相應(yīng)的操作。然后會(huì)有一種仿類的方式博肋,實(shí)現(xiàn)繼承方式低斋,其中就使用到了一種重要的概念:[[Prototype]]蜂厅,但是要如何理解[[Prototype]],什么才是[[Prototype]]膊畴,該如何使用呢掘猿?

  1. [[Prototype]]是什么?

    先看一段代碼:

    // case1
    function Fun(name){
        this.name = name
    }
    var obj = new Fun('obj')
    console.log(Object.getPrototypeOf(obj) === Fun.prototype)
    console.log(obj.__proto__ === Fun.prototype)
    輸出:
    true
    true
    
    // case2
    var a={
        name: 'a'
    }
    var b = Object.create(a)
    console.log(b.name)
    console.log(Object.getPrototypeOf(b) == a)
    輸出:
    a
    true
    

    如case1中通過(guò)構(gòu)造函數(shù)方式調(diào)用方法Fun新生成對(duì)象,通過(guò)Object.getPrototypeof方法獲取到對(duì)象的[[Prototype]]鏈指向了Fun.prototype屬性值唇跨。(其實(shí)有些瀏覽器中支持__proto__獲取對(duì)象的[[Prototype]]鏈)

    case2中通過(guò)Object.create方式創(chuàng)建一個(gè)新對(duì)象b關(guān)聯(lián)到對(duì)象a稠通,b的prototype指向了a,此時(shí)雖然b對(duì)象沒(méi)有屬性name轻绞,但是console.log(b.name)輸出了a采记。此處prototype就起到了重要的作用。js中獲取一個(gè)對(duì)象的屬性值涉及到對(duì)象的[[Get]]操作:先從對(duì)象的直接屬性里面找尋政勃,如果未找到唧龄,此時(shí)會(huì)沿著對(duì)象的[[Prototype]]鏈上找尋,直至最頂層的[[Prototype]](一般Object.prototype為最頂層),如果還未找到奸远,就會(huì)返回undefined,這也很好的解釋了b.name既棺。

    prototype只是對(duì)象中的一個(gè)屬性,它讓對(duì)象可以關(guān)聯(lián)到另外一個(gè)對(duì)象懒叛。

    prototype會(huì)影響到j(luò)s中的[[Get]]操作和[[Put]]操作丸冕,至于如何影響[[Get]]操作上述已描述過(guò)烹植,下面描述下[[Put]]操作

    再看段代碼:

    var a={
        name: 'a',
        set id(val){
            this._id_=val
        },
        get id(){
            return this._id_
        }
    }
    Object.defineProperty(a,'desc',{
        writable:false,
        value: 'desc of a'
    })
    var b = Object.create(a)
    // case1:writable=true屬性
    b.name = 'b'
    console.log('a.name:' + a.name)     // a.name:a
    console.log('b.name:' + b.name)     // b.name:b
    // case2:writable=false 屬性
    b.desc='desc of b'                  // 默認(rèn)無(wú)效许饿,在strict模式下會(huì)報(bào)錯(cuò)!者蠕!
    console.log('a.desc:' + a.desc)     // a.desc:desc of a
    console.log('b.desc:' + b.desc)     // b.desc:desc of a
    // case3:setter屬性
    b.id = 'b_id'
    console.log('a.id:' + a.id)         // a.id:undefined
    console.log('b.id:' + b.id)         // b.id:b_id
    
    

    當(dāng)給對(duì)象屬性賦值時(shí)會(huì)執(zhí)行[[Put]]操作诅迷,如果屬性直屬于該對(duì)象佩番,則直接更改屬性值。如果屬性不直屬于該對(duì)象同時(shí)也不存在對(duì)象的[[Prototype]]鏈上時(shí)罢杉,直接給該對(duì)象創(chuàng)建該屬性并賦值趟畏。如存在于[[Prototype]]鏈上時(shí),就像上面的代碼示例中表現(xiàn)的那樣:

    1. 如果該屬性可寫滩租,此時(shí)則在對(duì)象上創(chuàng)建同樣的屬性并賦值赋秀,此時(shí)屏蔽了[[Prototype]]鏈上的屬性值。
    2. 如果該屬性不可寫律想,若運(yùn)行在非嚴(yán)格模式下猎莲,代碼默認(rèn)無(wú)效,若在嚴(yán)格模式下蜘欲,會(huì)拋錯(cuò)益眉。
    3. 如果屬性是一個(gè)setter,此時(shí)執(zhí)行該setter姥份,然后也會(huì)在對(duì)象上創(chuàng)建同樣的屬性并賦值郭脂,屏蔽[[Prototype]]鏈上的屬性值。

    由此可以得出結(jié)論:對(duì)象中的[[Prototype]]其實(shí)是其中的一個(gè)屬性澈歉,用來(lái)關(guān)聯(lián)到另外一個(gè)對(duì)象展鸡。它起到了一個(gè)委托關(guān)聯(lián)的作用,可以訪問(wèn)到被關(guān)聯(lián)對(duì)象中的屬性埃难、方法莹弊。類似于其余面向?qū)ο笳Z(yǔ)言中的類繼承概念,只是類似涡尘,其中并唔發(fā)生類繼承中的拷貝操作忍弛。

  2. prototype使用場(chǎng)景?
    基本上了解到了[[Prototype]]鏈考抄,那么會(huì)在那些場(chǎng)景中使用呢细疚?
    主要應(yīng)用于“類”繼承中。
    js中很多場(chǎng)景會(huì)被設(shè)計(jì)成:將眾多操作對(duì)象抽象成一個(gè)抽象的"類"川梅,然后定義n多“子類”疯兼,通過(guò)重寫繼承過(guò)來(lái)的方法實(shí)現(xiàn)“偽多態(tài)”的現(xiàn)象。
    代碼示例:

    function View(name){
        this.name = name
    }
    View.prototype.print= function(){
        console.log(this.name)
    }
    
    function IView(name, id){
        View.call(this,name)
        this.id = id
    }
    // ES5
    IView.prototype=Object.create(View.prototype)
    // Object.setPrototypeOf(IView.prototype, View.prototype) ES6
    
    IView.prototype.print=function(){
        View.prototype.print.call(this)
        console.log('name:%s,id:%s',this.name,this.id)
    }
    var v = new IView('iview', 1)
    v.print()
    // 輸出: 
    iview
    name:iview,id:1
    

    上述代碼中定義了兩個(gè)“類”:View和IView贫途,然后通過(guò)IView.prototype=Object.create(View.prototype)吧彪,看上去讓IView繼承View,再重寫View原型鏈中的方法print丢早。
    當(dāng)然要實(shí)現(xiàn)上述功能姨裸,并非要通過(guò)創(chuàng)造偽類(構(gòu)造函數(shù)方式調(diào)用/new 函數(shù)調(diào)用)的方式。因?yàn)樯厦娲a中雖然IView看上去繼承View怨酝,但是實(shí)質(zhì)上并沒(méi)有像繼承一樣拷貝一份View中的內(nèi)容到IView中傀缩,只是通過(guò)[[Prototype]]方式讓IView關(guān)聯(lián)到View上。這樣就不是真正意義上類繼承的操作方式了凫碌。

    下面介紹種通過(guò)關(guān)聯(lián)的方式(符合js特征)

    var View={
        init(name){
            this.name = name
        },
        print(){
            console.log(this.name)
        }
    }
    
    var IView = Object.create(View)
    IView.set=function(name,id){
        this.init(name)
        this.id=id
    }
    // **注意此處不可再定義IView.init方法扑毡,否則會(huì)出現(xiàn)循環(huán)調(diào)用(報(bào)錯(cuò):RangeError: Maximum call stack size exceeded)**
    IView.show=function(){
        this.print()
        console.log('name:%s,id:%s',this.name,this.id)
    }
    
    var v = Object.create(IView)
    v.set('iview',2)
    v.show()
    // 輸出:
    iview
    name:iview,id:2
    

    上述通過(guò)Object.create方式將IView的[[Prototype]]指向了View,然后再創(chuàng)建對(duì)象關(guān)聯(lián)到IView對(duì)象上盛险。同樣實(shí)現(xiàn)了上述代碼片斷1中的效果瞄摊。

  3. ES6中的語(yǔ)法糖class
    ES6中引入了class關(guān)鍵字語(yǔ)法糖,從此不需要通過(guò)改寫Prototype苦掘,像上述第一段代碼中的方式實(shí)現(xiàn)"類"繼承了换帜。
    將上述代碼用class改寫:

    class View {
        construtor(name){
            this.name = name
        }
        print(){
            console.log(this.name)
        }
    }
    
    class IView extends View {
        construtor(name,id){
            super(name)
            this.id=id
        }
        print(){
            super.print()
            console.log('name:%s,id:%s',this.name,this.id)
        }
    }
    
    var v = new IView('iview',3)
    v.print()
    

最后(Last but not least)


[[Prototype]]原型鏈提供了一個(gè)對(duì)象關(guān)聯(lián)到另一個(gè)對(duì)象的方式。js中被沒(méi)有類的概念鹤啡,實(shí)現(xiàn)類似的類繼承的方式惯驼,可以通過(guò)委托關(guān)聯(lián)的形式實(shí)現(xiàn)我們想要的需求,這樣更有利于加深我們對(duì)js中[[Prototype]]的理解。


參考資料:[美]KYLE SIMPSON著 趙望野 梁杰 譯 《你不知道的javascript》上卷

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末祟牲,一起剝皮案震驚了整個(gè)濱河市隙畜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌说贝,老刑警劉巖议惰,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異乡恕,居然都是意外死亡言询,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門傲宜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)运杭,“玉大人,你說(shuō)我怎么就攤上這事函卒×俱荆” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵谆趾,是天一觀的道長(zhǎng)躁愿。 經(jīng)常有香客問(wèn)我,道長(zhǎng)沪蓬,這世上最難降的妖魔是什么彤钟? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮跷叉,結(jié)果婚禮上逸雹,老公的妹妹穿的比我還像新娘。我一直安慰自己云挟,他們只是感情好梆砸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著园欣,像睡著了一般帖世。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沸枯,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天日矫,我揣著相機(jī)與錄音,去河邊找鬼绑榴。 笑死哪轿,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的翔怎。 我是一名探鬼主播窃诉,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼杨耙,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了飘痛?” 一聲冷哼從身側(cè)響起珊膜,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎敦冬,沒(méi)想到半個(gè)月后辅搬,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體唯沮,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡脖旱,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了介蛉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片萌庆。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖币旧,靈堂內(nèi)的尸體忽然破棺而出践险,到底是詐尸還是另有隱情,我是刑警寧澤吹菱,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布巍虫,位于F島的核電站,受9級(jí)特大地震影響鳍刷,放射性物質(zhì)發(fā)生泄漏占遥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一输瓜、第九天 我趴在偏房一處隱蔽的房頂上張望瓦胎。 院中可真熱鬧,春花似錦尤揣、人聲如沸搔啊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)负芋。三九已至,卻和暖如春嗜愈,著一層夾襖步出監(jiān)牢的瞬間旧蛾,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工芝硬, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蚜点,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓拌阴,卻偏偏與公主長(zhǎng)得像绍绘,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 作為小白一直都被這幾個(gè)概念困擾陪拘,最近瀏覽了很多人的文章厂镇,再結(jié)合自己的想法把自己的理解寫成此文(希望各位把有錯(cuò)的地方...
    WMLJS閱讀 834評(píng)論 1 3
  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情左刽,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式捺信。簡(jiǎn)單...
    舟漁行舟閱讀 7,724評(píng)論 2 17
  • 單例模式 適用場(chǎng)景:可能會(huì)在場(chǎng)景中使用到對(duì)象,但只有一個(gè)實(shí)例欠痴,加載時(shí)并不主動(dòng)創(chuàng)建迄靠,需要時(shí)才創(chuàng)建 最常見(jiàn)的單例模式,...
    Obeing閱讀 2,058評(píng)論 1 10
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程喇辽,因...
    小菜c閱讀 6,365評(píng)論 0 17
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理掌挚,服務(wù)發(fā)現(xiàn),斷路器菩咨,智...
    卡卡羅2017閱讀 134,633評(píng)論 18 139