摘要:如果你已經(jīng)對(duì)機(jī)制已有了解,但是由于兩者對(duì)象機(jī)制的巨大本質(zhì)差異,對(duì)它和構(gòu)造函數(shù),實(shí)例對(duì)象的關(guān)系仍有疑惑,本文或許可以解答你的問(wèn)題。所有的原型對(duì)象都會(huì)自動(dòng)獲得一個(gè)屬性,這個(gè)屬性的值是指向原型所在的構(gòu)造函數(shù)的指針。
幫助面向?qū)ο箝_發(fā)者理解關(guān)于JavaScript對(duì)象機(jī)制
本文是以一個(gè)熟悉OO語(yǔ)言的開發(fā)者視角,來(lái)解釋JavaScript中的對(duì)象。
對(duì)于不了解JavaScript 語(yǔ)言,尤其是習(xí)慣了OO語(yǔ)言的開發(fā)者來(lái)說(shuō),由于語(yǔ)法上些許的相似會(huì)讓人產(chǎn)生心理預(yù)期,JavaScript中的原型繼承機(jī)制和class語(yǔ)法糖是讓人迷惑的。
如果你已經(jīng)對(duì)prototype機(jī)制已有了解,但是由于兩者對(duì)象機(jī)制的巨大(本質(zhì))差異,對(duì)它和構(gòu)造函數(shù),實(shí)例對(duì)象的關(guān)系仍有疑惑,本文或許可以解答你的問(wèn)題。
我們看下面的代碼,可以看出和OO語(yǔ)言相比,語(yǔ)法上也有很大分別:
// 定義一個(gè)類 class Foo { constructor() { this.a = "a"; } } //實(shí)例化對(duì)象 const foo = new Foo(); //定義原型的屬性 Foo.prototype.b = "b"; //實(shí)例可以訪問(wèn)屬性 foo.b // "b" //修改原型的屬性 Foo.prototype.b= "B"; //實(shí)例屬性值沒有被修改 foo.b // "b"
類已經(jīng)定義了怎么還能修改呢?prototype又是什么?
不存在面向?qū)ο?/b>對(duì)于熟悉了面向?qū)ο蟮拈_發(fā)者而言JS中種種非預(yù)期操作的存在,都是因?yàn)?strong>JavaScript中根本沒有面向?qū)ο蟮母拍?/strong>,只有對(duì)象,沒有類。
即使ES6新添了class語(yǔ)法,不意味著JS引入了面向?qū)ο螅皇窃屠^承的語(yǔ)法糖。
原型是什么什么是原型?如果說(shuō)類是面向?qū)ο笳Z(yǔ)言中對(duì)象的模版,原型就是 JS中創(chuàng)造對(duì)象的模版。
在面向類的語(yǔ)言中,實(shí)例化類,就像用模具制作東西一樣。實(shí)例化一個(gè)類就意味著“把類的形態(tài)復(fù)制到物理對(duì)象中”,對(duì)于每一個(gè)新實(shí)例來(lái)說(shuō)都會(huì)重復(fù)這個(gè)過(guò)程。
但是在JavaScript中,并沒有類似的復(fù)制機(jī)制。你不能創(chuàng)建一個(gè)類的多個(gè)實(shí)例,只能創(chuàng)建多個(gè)對(duì)象,它們[[Prototype]]關(guān)聯(lián)的是同一個(gè)對(duì)象。
//構(gòu)造函數(shù) function Foo(){ } //在函數(shù)的原型上添加屬性 Foo.prototype.prototypeAttribute0 = {status: "initial"}; const foo0 = new Foo(); const foo1 = new Foo(); foo0.prototypeAttribute0 === foo1.prototypeAttribute0 //true對(duì)象、構(gòu)造函數(shù)和原型的關(guān)系
當(dāng)我們創(chuàng)建一個(gè)新對(duì)象的時(shí)候,發(fā)生了什么,對(duì)象、構(gòu)造函數(shù)和原型到底什么。
先簡(jiǎn)單地概括:
原型用于定義共享的屬性和方法。
構(gòu)造函數(shù)用于定義實(shí)例屬性和方法,僅負(fù)責(zé)創(chuàng)造對(duì)象,與對(duì)象不存在直接的引用關(guān)系。
我們先不用class語(yǔ)法糖,這樣便于讀者理解和暴露出他們之間真正的關(guān)系。
// 先創(chuàng)建一個(gè)構(gòu)造函數(shù) 定義原型的屬性和方法 function Foo() { this.attribute0 = "attribute0"; }
當(dāng)創(chuàng)建了一個(gè)函數(shù),就會(huì)為該函數(shù)創(chuàng)建一個(gè)prototype屬性,它指向函數(shù)原型。
所有的原型對(duì)象都會(huì)自動(dòng)獲得一個(gè)constructor屬性,這個(gè)屬性的值是指向原型所在的構(gòu)造函數(shù)的指針。
現(xiàn)在定義原型的屬性和方法
Foo.prototype.prototypeMethod0 = function() { console.log("this is prototypeMethod0"); } Foo.prototype.prototypeAttribute0 = "prototypeAttribute0";
好了,現(xiàn)在,新建一個(gè)對(duì)象,
const foo = new Foo(); foo.attribute0 // "attribute0" foo.prototypeAttribute0 //"prototypeAttribute0" foo.prototypeMethod0() // this is prototypeMethod0
它擁有自己的實(shí)例屬性attribute0,并且可以訪問(wèn)在原型上定義的屬性和方法,他們之間的引用關(guān)系如圖所示。
當(dāng)調(diào)用構(gòu)造函數(shù)創(chuàng)建實(shí)例后,該實(shí)例的內(nèi)部會(huì)包含一個(gè)指針(內(nèi)部對(duì)象),指向構(gòu)造函數(shù)的原型對(duì)象。
當(dāng)讀取實(shí)例對(duì)象的屬性時(shí),會(huì)在實(shí)例中先搜尋,沒有找到,就會(huì)去原型鏈中搜索,且總是會(huì)選擇原型鏈中最底層的屬性進(jìn)行訪問(wèn)。
對(duì)象的原型可以通過(guò)__proto__在chrome等瀏覽器上訪問(wèn)。
__proto__是對(duì)象的原型指針,prototype是構(gòu)造函數(shù)所對(duì)應(yīng)的原型指針。
語(yǔ)法糖做了什么ES6推出了class語(yǔ)法,為定義構(gòu)造函數(shù)和原型增加了便利性和可讀性。
class Foo { constructor(){ this.attribute0 = "attribute0"; } prototypeMethod0(){ console.log("this is prototypeMethod0") } } /* 相當(dāng)于下面的聲明*/ function Foo() { this.attribute0 = "attribute0"; } Foo.prototype.prototypeMethod0 = function() { console.log("this is prototypeMethod0") }
class中的constractor相當(dāng)于構(gòu)造函數(shù),而class中的方法相當(dāng)于原型上的方法。、
值得注意的特性 屬性屏蔽 —— 避免實(shí)例對(duì)象無(wú)意修改原型看這段代碼,思考輸出的結(jié)果。
class Foo { prototypeMethod0(){ console.log("this is prototypeMethod0") } } const foo0 = new Foo(); const foo1 = new Foo(); foo0.prototypeMethod0 === foo0.__proto__.prototypeMethod0 // true foo0.prototypeMethod0 = () => console.log("foo0 method"); foo0.prototypeMethod0(); //?? foo1.prototypeMethod0(); //?? foo0.prototypeMethod0 === foo0.__proto__.prototypeMethod0 // ??
輸出的結(jié)果是
foo0.prototypeMethod0(); // foo0 method foo1.prototypeMethod0(); // this is prototypeMethod0 foo0.prototypeMethod0 === foo0.__proto__.prototypeMethod0 // false
我們知道對(duì)象(即便是原型對(duì)象),都是運(yùn)行時(shí)的。
創(chuàng)建之初,foo本身沒有prototypeMethod0這個(gè)屬性,訪問(wèn)foo0.prototypeMethod0將會(huì)讀取foo0.__proto__.prototypeMethod0。
直接修改foo0.prototypeMethod0沒有改變__proto__上的方法原因是存在屬性屏蔽。
現(xiàn)在的情況是:想要修改foo0.prototypeMethod0,prototypeMethod0在foo中不存在而在上層(即foo.__proto__中存在),并且這不是一個(gè)特殊屬性(如只讀)。
那么會(huì)在foo中添加一個(gè)新的屬性。
這便是為什么直接修改卻沒有影響__proto__的原因。
小結(jié)再溫習(xí)一遍這些定義:
原型用于定義共享的屬性和方法。
構(gòu)造函數(shù)用于定義實(shí)例屬性和方法,僅負(fù)責(zé)創(chuàng)造對(duì)象,與對(duì)象不存在直接的引用關(guān)系。
__proto__是對(duì)象的原型指針,prototype是構(gòu)造函數(shù)的原型指針。
在解釋原型作用的文章或書籍中,我們會(huì)聽到繼承這樣的術(shù)語(yǔ),其實(shí)更準(zhǔn)確地,委托對(duì)于JavaScript中的對(duì)象模型來(lái)說(shuō),是一個(gè)更合適的術(shù)語(yǔ)。
委托行為意味著某些對(duì)象在找不到屬性或者方法引用時(shí)會(huì)把這個(gè)請(qǐng)求委托給另一個(gè)對(duì)象。對(duì)象之間的關(guān)系不是復(fù)制而是委托。
參考
《JavaScript高級(jí)程序設(shè)計(jì)》
《你不知道的JavaScript》
本文僅供解惑,要在腦袋里形成系統(tǒng)的概念,還是要看書呀。
有疑問(wèn)歡迎大家一起討論。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/108193.html
摘要:大多數(shù)待遇豐厚的開發(fā)職位都要求開發(fā)者精通多線程技術(shù)并且有豐富的程序開發(fā)調(diào)試優(yōu)化經(jīng)驗(yàn),所以線程相關(guān)的問(wèn)題在面試中經(jīng)常會(huì)被提到。將對(duì)象編碼為字節(jié)流稱之為序列化,反之將字節(jié)流重建成對(duì)象稱之為反序列化。 JVM 內(nèi)存溢出實(shí)例 - 實(shí)戰(zhàn) JVM(二) 介紹 JVM 內(nèi)存溢出產(chǎn)生情況分析 Java - 注解詳解 詳細(xì)介紹 Java 注解的使用,有利于學(xué)習(xí)編譯時(shí)注解 Java 程序員快速上手 Kot...
摘要:主線程在啟動(dòng)程序時(shí)被創(chuàng)建,用于執(zhí)行函數(shù)。用戶自主創(chuàng)建的若干進(jìn)程相對(duì)于主線程而言就是子線程。子線程和主線程都是獨(dú)立的運(yùn)行單元,各自的執(zhí)行互不影響,因此能夠并發(fā)執(zhí)行。這就是的異步機(jī)制了。 某天突然寫了個(gè)方法要從后臺(tái)調(diào)用數(shù)據(jù),顯示在前臺(tái)頁(yè)面,但是輸出結(jié)果總是空undefined,得不到數(shù)據(jù)。多方找資料才發(fā)現(xiàn),原來(lái)是入了JS異步的坑。 我們常常聽到單線程、多線程、同步、異步這些概念,那么這些東...
摘要:轉(zhuǎn)行前端有哪些疑慮在人生的抉擇處,尋求一些別人的經(jīng)驗(yàn)和總結(jié),無(wú)可厚非,但是決定了就一定要堅(jiān)定的走下去,謹(jǐn)慎是為了更好的堅(jiān)持,而不是放棄的理由。寫在前面這里前后端指的是開發(fā)的前后端。 轉(zhuǎn)行前端有哪些疑慮? 在人生的抉擇處,尋求一些別人的經(jīng)驗(yàn)和總結(jié),無(wú)可厚非,但是決定了就一定要堅(jiān)定的走下去,謹(jǐn)慎是為了更好的堅(jiān)持,而不是放棄的理由。寫在前面:這里前后端指的是web開發(fā)的前后端。1、前端崗位需...
摘要:轉(zhuǎn)行前端有哪些疑慮在人生的抉擇處,尋求一些別人的經(jīng)驗(yàn)和總結(jié),無(wú)可厚非,但是決定了就一定要堅(jiān)定的走下去,謹(jǐn)慎是為了更好的堅(jiān)持,而不是放棄的理由。寫在前面這里前后端指的是開發(fā)的前后端。 轉(zhuǎn)行前端有哪些疑慮? 在人生的抉擇處,尋求一些別人的經(jīng)驗(yàn)和總結(jié),無(wú)可厚非,但是決定了就一定要堅(jiān)定的走下去,謹(jǐn)慎是為了更好的堅(jiān)持,而不是放棄的理由。寫在前面:這里前后端指的是web開發(fā)的前后端。1、前端崗位需...
閱讀 2805·2023-04-25 23:08
閱讀 1594·2021-11-23 09:51
閱讀 1575·2021-10-27 14:18
閱讀 3125·2019-08-29 13:25
閱讀 2839·2019-08-29 13:14
閱讀 2913·2019-08-26 18:36
閱讀 2200·2019-08-26 12:11
閱讀 821·2019-08-26 11:29