摘要:下面輪到我們的主角原型繼承登場了,它從另一個(gè)角度解決了重用的問題。原型繼承的原理原型對象中的由兩部分組成,普通屬性的集合,和原型屬性。原型繼承的實(shí)現(xiàn)在上面的例子中,通過直接修改了屬性值,實(shí)現(xiàn)了原型繼承。使用原型繼承,同樣可以達(dá)到重用的目的。
繼承的本質(zhì):重用
在探討 JavaScript 的原型繼承之前,先不妨想想為什么要繼承?
考慮一個(gè)場景,如果我們有兩個(gè)對象,它們一部分屬性相同,另一部屬性不同。通常一個(gè)好的設(shè)計(jì)方案是將相同邏輯抽出來,實(shí)現(xiàn)重用。
以 xiaoMing liLei 兩位同學(xué)舉例。這兩位同學(xué)有自己的名字,并且會介紹自己。抽象為程序?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 + "."); } }
使用過 java 的同學(xué),可能第一眼就想到了用面向?qū)ο髞斫鉀Q這個(gè)問題。創(chuàng)造一個(gè) Person 的類,然后實(shí)例化 xiaoMing 和 liLei 兩個(gè)對象。在 ES6 中也有類似于 java 中類的概念:class。
下面使用 ES6 的語法,用面向?qū)ο蟮乃悸穪碇貥?gòu)上面的代碼。
class Person { constructor(name){ this.name = name } hello(){ console.log(this.name); } } var xiaoMing = new Person("xiaoMing"); var liLei = new Person("liLei");
可以看到,使用類創(chuàng)建對象,達(dá)到了重用的目的。它基于的邏輯是,兩個(gè)或多個(gè)對象的結(jié)構(gòu)功能類似,可以抽象出一個(gè)模板,依照模板復(fù)制出多個(gè)相似的對象。
使用類創(chuàng)建對象,就像自行車制造商一遍一遍地重用相同的藍(lán)圖來制造大量的自行車。
然解決重用問題的方案,當(dāng)然不止一種。傳統(tǒng)面向?qū)ο蟮念?,只是其中的一種方案。下面輪到我們的主角“原型繼承”登場了,它從另一個(gè)角度解決了重用的問題。
原型繼承的原理 原型對象JavaScript 中的 object 由兩部分組成,普通屬性的集合,和原型屬性。
var o = { a : "a", ... __proto__: prototypeObj }
普通屬性指的就是 a;原型屬性 指的是 __proto__。這本不屬于規(guī)范的一部分,后來 chrome 通過 __proto__ 將這個(gè)語言底層屬性給暴露出來了,慢慢的被大家所接受,也就添加到 ES6 規(guī)范中了。 o.__proto__ 的值 prototypeObj 也就是 原型對象 。原型對象其實(shí)也就是一個(gè)普通對象,之所以叫原型對象的原因,只是因?yàn)樗窃蛯傩运傅闹怠?/p>
原型對象所以特殊,是因?yàn)樗鼡碛幸粋€(gè)普通對象沒有的能力:將它的屬性共享給其他對象。
在 ES6 規(guī)范 中,對它是如下定義的:
object that provides shared properties for other objects屬性讀操作
回到最開始的例子,看看如何利用原型繼承實(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 對象上,并沒直接擁有 hello 屬性(方法),但是卻能讀取該屬性(執(zhí)行該方法),這是為什么?
想象一個(gè)場景,你在做數(shù)學(xué)作業(yè),遇到一個(gè)很難的題目,你不會做。而你有一個(gè)好兄弟,數(shù)學(xué)很厲害,你去請教他,把這道題做出來了。
xiaoMing 對象上,沒有 hello 屬性,但是它有一個(gè)好兄弟,prototypeObj。屬性讀操作,在 xiaoMing 身上沒有找到 hello 屬性,就會去問它的兄弟 prototypeObj。所以 hello 方法會被執(zhí)行。
原型鏈還是做數(shù)學(xué)題的例子。你的數(shù)學(xué)題目很難,你的兄弟也沒有答案,他推薦你去問另外一個(gè)同學(xué)。這樣直到有了答案或者再也沒有人可以問,你就不會再問下去。這樣就好像有一條無形鏈條把你和同學(xué)們牽在了一起。
在 JS 中,讀操作通過 __proto__ 會一層一層鏈下去的結(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)
在上面的例子中,通過直接修改了 __proto__ 屬性值,實(shí)現(xiàn)了原型繼承。但是在實(shí)際生產(chǎn)中,
用這種方式來改變和繼承屬性是對性能影響非常嚴(yán)重的,所以并不推薦。
代替的方式是使用 Object.create() 方法。
調(diào)用 Object.create() 方法會創(chuàng)建一個(gè)新對象,同時(shí)指定該對象的原型對象為傳入的第一個(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 的作者,對這種原型繼承的實(shí)現(xiàn)取了一個(gè)很好玩的名字 OLOO (objects-linked-to-other-objects) ,這種實(shí)現(xiàn)方式的優(yōu)點(diǎn)是沒有使用任何類的概念,只有 object,所以它是很符合 javaScript 的特性的。
因?yàn)镴S 中本無類,只有 object。
無奈的是,喜歡類的程序員是在太多,所以在 ES6 新增了 class 概念。下一篇會講 class 在 JS 中的實(shí)現(xiàn)原理
小結(jié)通過類來創(chuàng)建對象,使得開發(fā)者不必寫重復(fù)的代碼,以達(dá)到代碼重用的目的。它基于的邏輯是,兩個(gè)或多個(gè)對象的結(jié)構(gòu)功能類似,可以抽象出一個(gè)模板,依照模板復(fù)制出多個(gè)相似的對象。就像自行車制造商一遍一遍地重用相同的藍(lán)圖來制造大量的自行車。
使用原型繼承,同樣可以達(dá)到重用的目的。它基于的邏輯是,兩個(gè)或多個(gè)對象的對象有一部分共用屬性,可以將共用的屬性抽象到另一個(gè)獨(dú)立公共對象上,通過特殊的原型屬性,將公共對象和普通對象鏈接起來,再利用屬性讀(寫)規(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)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/86779.html