摘要:下面輪到我們的主角原型繼承登場(chǎng)了,它從另一個(gè)角度解決了重用的問(wèn)題。原型繼承的原理原型對(duì)象中的由兩部分組成,普通屬性的集合,和原型屬性。原型繼承的實(shí)現(xiàn)在上面的例子中,通過(guò)直接修改了屬性值,實(shí)現(xiàn)了原型繼承。使用原型繼承,同樣可以達(dá)到重用的目的。
繼承的本質(zhì):重用
在探討 JavaScript 的原型繼承之前,先不妨想想為什么要繼承?
考慮一個(gè)場(chǎng)景,如果我們有兩個(gè)對(duì)象,它們一部分屬性相同,另一部屬性不同。通常一個(gè)好的設(shè)計(jì)方案是將相同邏輯抽出來(lái),實(shí)現(xiàn)重用。
以 xiaoMing liLei 兩位同學(xué)舉例。這兩位同學(xué)有自己的名字,并且會(huì)介紹自己。抽象為程序?qū)ο?,可以做如下表示?/p>
var xiaoMing = { name : "xiaoMing", hello : function(){ console.log( "Hello, my name is "+ this.name + "."); } } var liLei = { name : "liLei", hello : function(){ console.log( "Hello, my name is "+ this.name + "."); } }
使用過(guò) java 的同學(xué),可能第一眼就想到了用面向?qū)ο髞?lái)解決這個(gè)問(wèn)題。創(chuàng)造一個(gè) Person 的類(lèi),然后實(shí)例化 xiaoMing 和 liLei 兩個(gè)對(duì)象。在 ES6 中也有類(lèi)似于 java 中類(lèi)的概念:class。
下面使用 ES6 的語(yǔ)法,用面向?qū)ο蟮乃悸穪?lái)重構(gòu)上面的代碼。
class Person { constructor(name){ this.name = name } hello(){ console.log(this.name); } } var xiaoMing = new Person("xiaoMing"); var liLei = new Person("liLei");
可以看到,使用類(lèi)創(chuàng)建對(duì)象,達(dá)到了重用的目的。它基于的邏輯是,兩個(gè)或多個(gè)對(duì)象的結(jié)構(gòu)功能類(lèi)似,可以抽象出一個(gè)模板,依照模板復(fù)制出多個(gè)相似的對(duì)象。
使用類(lèi)創(chuàng)建對(duì)象,就像自行車(chē)制造商一遍一遍地重用相同的藍(lán)圖來(lái)制造大量的自行車(chē)。
然解決重用問(wèn)題的方案,當(dāng)然不止一種。傳統(tǒng)面向?qū)ο蟮念?lèi),只是其中的一種方案。下面輪到我們的主角“原型繼承”登場(chǎng)了,它從另一個(gè)角度解決了重用的問(wèn)題。
原型繼承的原理 原型對(duì)象JavaScript 中的 object 由兩部分組成,普通屬性的集合,和原型屬性。
var o = { a : "a", ... __proto__: prototypeObj }
普通屬性指的就是 a;原型屬性 指的是 __proto__。這本不屬于規(guī)范的一部分,后來(lái) chrome 通過(guò) __proto__ 將這個(gè)語(yǔ)言底層屬性給暴露出來(lái)了,慢慢的被大家所接受,也就添加到 ES6 規(guī)范中了。 o.__proto__ 的值 prototypeObj 也就是 原型對(duì)象 。原型對(duì)象其實(shí)也就是一個(gè)普通對(duì)象,之所以叫原型對(duì)象的原因,只是因?yàn)樗窃蛯傩运傅闹怠?/p>
原型對(duì)象所以特殊,是因?yàn)樗鼡碛幸粋€(gè)普通對(duì)象沒(méi)有的能力:將它的屬性共享給其他對(duì)象。
在 ES6 規(guī)范 中,對(duì)它是如下定義的:
object that provides shared properties for other objects屬性讀操作
回到最開(kāi)始的例子,看看如何利用原型繼承實(shí)現(xiàn)重用的目的。
var prototypeObj = { hello: function(){ console.log( "Hello, my name is "+ this.name + "."); } // ... } var xiaoMing = { name : "xiaoMing", __proto__ : prototypeObj } var liLei = { name : "liLei", __proto__ : prototypeObj } xiaoMing.hello(); // Hello, my name is xiaoMing. liLei.hello(); // Hello, my name is liLei.
xiaoMing liLei 對(duì)象上,并沒(méi)直接擁有 hello 屬性(方法),但是卻能讀取該屬性(執(zhí)行該方法),這是為什么?
想象一個(gè)場(chǎng)景,你在做數(shù)學(xué)作業(yè),遇到一個(gè)很難的題目,你不會(huì)做。而你有一個(gè)好兄弟,數(shù)學(xué)很厲害,你去請(qǐng)教他,把這道題做出來(lái)了。
xiaoMing 對(duì)象上,沒(méi)有 hello 屬性,但是它有一個(gè)好兄弟,prototypeObj。屬性讀操作,在 xiaoMing 身上沒(méi)有找到 hello 屬性,就會(huì)去問(wèn)它的兄弟 prototypeObj。所以 hello 方法會(huì)被執(zhí)行。
原型鏈還是做數(shù)學(xué)題的例子。你的數(shù)學(xué)題目很難,你的兄弟也沒(méi)有答案,他推薦你去問(wèn)另外一個(gè)同學(xué)。這樣直到有了答案或者再也沒(méi)有人可以問(wèn),你就不會(huì)再問(wèn)下去。這樣就好像有一條無(wú)形鏈條把你和同學(xué)們牽在了一起。
在 JS 中,讀操作通過(guò) __proto__ 會(huì)一層一層鏈下去的結(jié)構(gòu),就叫 原型鏈。
var deepPrototypeObj = { hello: function(){ console.log( "Hello, my name is "+ this.name + "."); } __proto__ : null } var prototypeObj = { __proto__ : deepPrototypeObj } var xiaoMing = { name : "xiaoMing", __proto__ : prototypeObj } var liLei = { name : "liLei", __proto__ : prototypeObj } xiaoMing.hello(); // Hello, my name is xiaoMing. liLei.hello(); // Hello, my name is liLei.原型繼承的實(shí)現(xiàn)
在上面的例子中,通過(guò)直接修改了 __proto__ 屬性值,實(shí)現(xiàn)了原型繼承。但是在實(shí)際生產(chǎn)中,
用這種方式來(lái)改變和繼承屬性是對(duì)性能影響非常嚴(yán)重的,所以并不推薦。
代替的方式是使用 Object.create() 方法。
調(diào)用 Object.create() 方法會(huì)創(chuàng)建一個(gè)新對(duì)象,同時(shí)指定該對(duì)象的原型對(duì)象為傳入的第一個(gè)參數(shù)。
我們將上面的例子改一下。
var prototypeObj = { hello: function(){ console.log( "Hello, my name is "+ this.name + "."); } // ... } var xiaoMing = Object.create(prototypeObj); var liLei = Object.create(prototypeObj); xiaoMing.name = "xiaoMing"; liLei.name = "liLei"; xiaoMing.hello(); // Hello, my name is xiaoMing. liLei.hello(); // Hello, my name is liLei.
You-Dont-Know-JS 的作者,對(duì)這種原型繼承的實(shí)現(xiàn)取了一個(gè)很好玩的名字 OLOO (objects-linked-to-other-objects) ,這種實(shí)現(xiàn)方式的優(yōu)點(diǎn)是沒(méi)有使用任何類(lèi)的概念,只有 object,所以它是很符合 javaScript 的特性的。
因?yàn)镴S 中本無(wú)類(lèi),只有 object。
無(wú)奈的是,喜歡類(lèi)的程序員是在太多,所以在 ES6 新增了 class 概念。下一篇會(huì)講 class 在 JS 中的實(shí)現(xiàn)原理
小結(jié)通過(guò)類(lèi)來(lái)創(chuàng)建對(duì)象,使得開(kāi)發(fā)者不必寫(xiě)重復(fù)的代碼,以達(dá)到代碼重用的目的。它基于的邏輯是,兩個(gè)或多個(gè)對(duì)象的結(jié)構(gòu)功能類(lèi)似,可以抽象出一個(gè)模板,依照模板復(fù)制出多個(gè)相似的對(duì)象。就像自行車(chē)制造商一遍一遍地重用相同的藍(lán)圖來(lái)制造大量的自行車(chē)。
使用原型繼承,同樣可以達(dá)到重用的目的。它基于的邏輯是,兩個(gè)或多個(gè)對(duì)象的對(duì)象有一部分共用屬性,可以將共用的屬性抽象到另一個(gè)獨(dú)立公共對(duì)象上,通過(guò)特殊的原型屬性,將公共對(duì)象和普通對(duì)象鏈接起來(lái),再利用屬性讀(寫(xiě))規(guī)則進(jìn)行遍歷查找,實(shí)現(xiàn)屬性共享。
參考文章ES6 規(guī)范
You-Dont-Know-JS
MDN Object.create()
JavaScript difference between proto and prototype
proto VS. prototype in JavaScript
JavaScript. The core
Understanding "Prototypes" in JavaScript
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/86779.html
摘要:如下所示在規(guī)范中,已經(jīng)正式把屬性添加到規(guī)范中也可以通過(guò)設(shè)置和獲取對(duì)象的原型對(duì)象對(duì)象之間的關(guān)系可以用下圖來(lái)表示但規(guī)范主要介紹了如何利用構(gòu)造函數(shù)去構(gòu)建原型關(guān)系。 前言 在軟件工程中,代碼重用的模式極為重要,因?yàn)樗麄兛梢燥@著地減少軟件開(kāi)發(fā)的成本。在那些主流的基于類(lèi)的語(yǔ)言(比如Java,C++)中都是通過(guò)繼承(extend)來(lái)實(shí)現(xiàn)代碼復(fù)用,同時(shí)類(lèi)繼承引入了一套類(lèi)型規(guī)范。而JavaScript是...
摘要:深入之繼承的多種方式和優(yōu)缺點(diǎn)深入系列第十五篇,講解各種繼承方式和優(yōu)缺點(diǎn)。對(duì)于解釋型語(yǔ)言例如來(lái)說(shuō),通過(guò)詞法分析語(yǔ)法分析語(yǔ)法樹(shù),就可以開(kāi)始解釋執(zhí)行了。 JavaScript深入之繼承的多種方式和優(yōu)缺點(diǎn) JavaScript深入系列第十五篇,講解JavaScript各種繼承方式和優(yōu)缺點(diǎn)。 寫(xiě)在前面 本文講解JavaScript各種繼承方式和優(yōu)缺點(diǎn)。 但是注意: 這篇文章更像是筆記,哎,再讓我...
摘要:深入理解原型與繼承看過(guò)不少書(shū)籍,不少文章,對(duì)于原型與繼承的說(shuō)明基本上讓人不明覺(jué)厲,特別是對(duì)于習(xí)慣了面向?qū)ο缶幊痰娜藖?lái)說(shuō)更難理解,這里我就給大家說(shuō)說(shuō)我的理解。 深入理解:JavaScript原型與繼承 看過(guò)不少書(shū)籍,不少文章,對(duì)于原型與繼承的說(shuō)明基本上讓人不明覺(jué)厲,特別是對(duì)于習(xí)慣了面向?qū)ο缶幊痰娜藖?lái)說(shuō)更難理解,這里我就給大家說(shuō)說(shuō)我的理解。 首先JavaScript是一門(mén)基于原型編程的語(yǔ)言...
摘要:原型鏈與繼承當(dāng)談到繼承時(shí),只有一種結(jié)構(gòu)對(duì)象。如果對(duì)該圖不怎么理解,不要著急,繼續(xù)往下看基于原型鏈的繼承對(duì)象是動(dòng)態(tài)的屬性包指其自己的屬性。當(dāng)使用操作符來(lái)作用這個(gè)函數(shù)時(shí),它就可以被稱(chēng)為構(gòu)造方法構(gòu)造函數(shù)。 原型鏈與繼承 當(dāng)談到繼承時(shí),JavaScript 只有一種結(jié)構(gòu):對(duì)象。每個(gè)實(shí)例對(duì)象(object )都有一個(gè)私有屬性(稱(chēng)之為proto)指向它的原型對(duì)象(prototype)。該原型對(duì)象也...
摘要:首先,需要來(lái)理清一些基礎(chǔ)的計(jì)算機(jī)編程概念編程哲學(xué)與設(shè)計(jì)模式計(jì)算機(jī)編程理念源自于對(duì)現(xiàn)實(shí)抽象的哲學(xué)思考,面向?qū)ο缶幊淌瞧湟环N思維方式,與它并駕齊驅(qū)的是另外兩種思路過(guò)程式和函數(shù)式編程。 JavaScript 中的原型機(jī)制一直以來(lái)都被眾多開(kāi)發(fā)者(包括本人)低估甚至忽視了,這是因?yàn)榻^大多數(shù)人沒(méi)有想要深刻理解這個(gè)機(jī)制的內(nèi)涵,以及越來(lái)越多的開(kāi)發(fā)者缺乏計(jì)算機(jī)編程相關(guān)的基礎(chǔ)知識(shí)。對(duì)于這樣的開(kāi)發(fā)者來(lái)說(shuō) J...
閱讀 2870·2021-10-14 09:42
閱讀 3184·2019-08-30 15:52
閱讀 3274·2019-08-30 14:02
閱讀 1116·2019-08-29 15:42
閱讀 540·2019-08-29 13:20
閱讀 1167·2019-08-29 12:24
閱讀 486·2019-08-26 10:20
閱讀 688·2019-08-23 18:31