javascript
JS 原型链图形详解
JS原型鏈
這篇文章是「深入ECMA-262-3」系列的一個(gè)概覽和摘要。每個(gè)部分都包含了對(duì)應(yīng)章節(jié)的鏈接,所以你可以閱讀它們以便對(duì)其有更深的理解。
對(duì)象
ECMAScript做為一個(gè)高度抽象的面向?qū)ο笳Z言,是通過對(duì)象來交互的。即使ECMAScript里邊也有基本類型,但是,當(dāng)需要的時(shí)候,它們也會(huì)被轉(zhuǎn)換成對(duì)象。
一個(gè)對(duì)象就是一個(gè)屬性集合,并擁有一個(gè)獨(dú)立的prototype(原型)對(duì)象。這個(gè)prototype可以是一個(gè)對(duì)象或者null。*
讓我們看一個(gè)關(guān)于對(duì)象的基本例子。一個(gè)對(duì)象的prototype是以內(nèi)部的[[Prototype]]屬性來引用的。但是,在示意圖里邊我們將會(huì)使用__<internal-property>__下劃線標(biāo)記來替代兩個(gè)括號(hào),對(duì)于prototype對(duì)象來說是:__proto__\。
對(duì)于以下代碼:
?
basic-object.png這些prototype有什么用?讓我們以原型鏈(prototype chain)的概念來回答這個(gè)問題。
?
原型鏈
原型對(duì)象也是簡(jiǎn)單的對(duì)象并且可以擁有它們自己的原型。如果一個(gè)原型對(duì)象的原型是一個(gè)非null的引用,那么以此類推,這就叫作原型鏈。
原型鏈?zhǔn)且粋€(gè)用來實(shí)現(xiàn)繼承和共享屬性的有限對(duì)象鏈。
考慮這么一個(gè)情況,我們擁有兩個(gè)對(duì)象,它們之間只有一小部分不同,其他部分都相同。顯然,對(duì)于一個(gè)設(shè)計(jì)良好的系統(tǒng),我們將會(huì)重用相似的功能/代碼,而不是在每個(gè)單獨(dú)的對(duì)象中重復(fù)它。在基于類的系統(tǒng)中,這個(gè)代碼重用風(fēng)格叫作類繼承-你把相似的功能放入類A中,然后類B和類C繼承類A,并且擁有它們自己的一些小的額外變動(dòng)。
ECMAScript中沒有類的概念。但是,代碼重用的風(fēng)格并沒有太多不同(盡管從某些方面來說比基于類(class-based)的方式要更加靈活)并且通過原型鏈來實(shí)現(xiàn)。這種繼承方式叫作委托繼承(delegation based inheritance)(或者,更貼近ECMAScript一些,叫作原型繼承(prototype based inheritance))。
跟例子中的類A,B,C相似,在ECMAScript中你創(chuàng)建對(duì)象:a,b,c。于是,對(duì)象a中存儲(chǔ)對(duì)象b和c中通用的部分。然后b和c只存儲(chǔ)它們自身的額外屬性或者方法。
?
var a = { x: 10, calculate: function (z) {return this.x + this.y + z }}; var b = { y: 20, __proto__: a }; var c = { y: 30, __proto__: a }; // call the inherited method b.calculate(30); // 60 c.calculate(40); // 80規(guī)則很簡(jiǎn)單:如果一個(gè)屬性或者一個(gè)方法在對(duì)象自身中無法找到(也就是對(duì)象自身沒有一個(gè)那樣的屬性),然后它會(huì)嘗試在原型鏈中尋找這個(gè)屬性/方法。如果這個(gè)屬性在原型中沒有查找到,那么將會(huì)查找這個(gè)原型的原型,以此類推,遍歷整個(gè)原型鏈(當(dāng)然這在類繼承中也是一樣的,當(dāng)解析一個(gè)繼承的方法的時(shí)候-我們遍歷class鏈( class chain))。第一個(gè)被查找到的同名屬性/方法會(huì)被使用。因此,一個(gè)被查找到的屬性叫作繼承屬性。如果在遍歷了整個(gè)原型鏈之后還是沒有查找到這個(gè)屬性的話,返回undefined值。
?
注意,繼承方法中所使用的this的值被設(shè)置為原始對(duì)象,而并不是在其中查找到這個(gè)方法的(原型)對(duì)象。也就是,在上面的例子中this.y取的是b和c中的值,而不是a中的值。但是,this.x是取的是a中的值,并且又一次通過原型鏈機(jī)制完成。
如果沒有明確為一個(gè)對(duì)象指定原型,那么它將會(huì)使用__proto__的默認(rèn)值-Object.prototype。Object.prototype對(duì)象自身也有一個(gè)__proto__屬性,這是原型鏈的終點(diǎn)并且值為null。
下一張圖展示了對(duì)象a,b,c之間的繼承層級(jí):
prototype-chain.png注意: ES5標(biāo)準(zhǔn)化了一個(gè)實(shí)現(xiàn)原型繼承的可選方法,即使用Object.create函數(shù):
var b = Object.create(a, {y: {value: 20}}); var c = Object.create(a, {y: {value: 30}});
你可以在對(duì)應(yīng)的章節(jié)獲取到更多關(guān)于ES5新API的信息。 ES6標(biāo)準(zhǔn)化了 __proto__屬性,并且可以在對(duì)象初始化的時(shí)候使用它。
通常情況下需要對(duì)象擁有相同或者相似的狀態(tài)結(jié)構(gòu)(也就是相同的屬性集合),賦以不同的狀態(tài)值。在這個(gè)情況下我們可能需要使用構(gòu)造函數(shù)(constructorfunction),其以指定的模式來創(chuàng)造對(duì)象。
構(gòu)造函數(shù)
除了以指定模式創(chuàng)建對(duì)象之外,構(gòu)造函數(shù)也做了另一個(gè)有用的事情-它自動(dòng)地為新創(chuàng)建的對(duì)象設(shè)置一個(gè)原型對(duì)象。這個(gè)原型對(duì)象存儲(chǔ)在ConstructorFunction.prototype屬性中。
換句話說,我們可以使用構(gòu)造函數(shù)來重寫上一個(gè)擁有對(duì)象b和對(duì)象c的例子。因此,對(duì)象a(一個(gè)原型對(duì)象)的角色由Foo.prototype來扮演:
// a constructor function function Foo(y) { this.y = y; } Foo.prototype.x = 10; Foo.prototype.calculate = function (z) {return this.x + this.y + z; };var b = new Foo(20); var c = new Foo(30); // 調(diào)用繼承方法 b.calculate(30); // 60 c.calculate(40); // 80 console.log( b.__proto__ === Foo.prototype) // true console.log(c.__proto__ === Foo.prototype) // true//同時(shí) Foo.prototype 同時(shí)會(huì)創(chuàng)建一個(gè)特殊的屬性 constructor, b.constructor === Foo // true c.constructor === Foo, // true Foo.prototype.constructor === Foo // true b.calculate === b.__proto__.calculate // true b.__proto__.calculate === Foo.prototype.calculate // true?
這個(gè)代碼可以表示為如下關(guān)系:
?
這張圖又一次說明了每個(gè)對(duì)象都有一個(gè)原型。構(gòu)造函數(shù)Foo也有自己的__proto__,值為Function.prototype,Function.prototype也通過其__proto__屬性關(guān)聯(lián)到Object.prototype。因此,重申一下,Foo.prototype就是Foo的一個(gè)明確的屬性,指向?qū)ο?strong>b和對(duì)象c的原型。?
正式來說,如果思考一下分類的概念(并且我們已經(jīng)對(duì)Foo進(jìn)行了分類),那么構(gòu)造函數(shù)和原型對(duì)象合在一起可以叫作「類」。實(shí)際上,舉個(gè)例子,Python的第一級(jí)(first-class)動(dòng)態(tài)類(dynamic classes)顯然是以同樣的屬性/方法處理方案來實(shí)現(xiàn)的。從這個(gè)角度來說,Python中的類就是ECMAScript使用的委托繼承的一個(gè)語法糖。
注意: 在ES6中「類」的概念被標(biāo)準(zhǔn)化了,并且實(shí)際上以一種構(gòu)建在構(gòu)造函數(shù)上面的語法糖來實(shí)現(xiàn),就像上面描述的一樣。從這個(gè)角度來看原型鏈成為了類繼承的一種具體實(shí)現(xiàn)方式:
// ES6 class Foo {constructor(name) {this._name = name;}getName {return this._name;} } class Bar extends Foo {getName {return super.getName + ' Doe';} } var bar = newBar('John'); console.log(bar.getName); // John Doe?
轉(zhuǎn)自JavaScript. The core.
http://www.yidianzixun.com/n/0CjThYEE?s=9&appid=xiaomi&ver=3.5.5&utk=03smxf0g
轉(zhuǎn)載于:https://www.cnblogs.com/jiuyi/p/5331592.html
總結(jié)
以上是生活随笔為你收集整理的JS 原型链图形详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (王道408考研操作系统)第二章进程管理
- 下一篇: Spring注意事项(各部分理解)