摘要:構(gòu)造函數(shù)除了以指定模式創(chuàng)建對(duì)象之外,構(gòu)造函數(shù)也做了另一個(gè)有用的事情它自動(dòng)地為新創(chuàng)建的對(duì)象設(shè)置一個(gè)原型對(duì)象。正式來(lái)說(shuō),如果思考一下分類(lèi)的概念并且我們已經(jīng)對(duì)進(jìn)行了分類(lèi),那么構(gòu)造函數(shù)和原型對(duì)象合在一起可以叫作類(lèi)。
這篇文章是「深入ECMA-262-3」系列的一個(gè)概覽和摘要。每個(gè)部分都包含了對(duì)應(yīng)章節(jié)的鏈接,所以你可以閱讀它們以便對(duì)其有更深的理解。
對(duì)象ECMAScript做為一個(gè)高度抽象的面向?qū)ο笳Z(yǔ)言,是通過(guò)對(duì)象來(lái)交互的。即使ECMAScript里邊也有基本類(lèi)型,但是,當(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]]屬性來(lái)引用的。但是,在示意圖里邊我們將會(huì)使用__
對(duì)于以下代碼:
var foo = { x: 10, y: 20 };
我們擁有一個(gè)這樣的結(jié)構(gòu),兩個(gè)明顯的自身屬性和一個(gè)隱含的__proto__屬性,這個(gè)屬性是對(duì)foo原型對(duì)象的引用:
這些prototype有什么用?讓我們以原型鏈(prototype chain)的概念來(lái)回答這個(gè)問(wèn)題。
原型對(duì)象也是簡(jiǎn)單的對(duì)象并且可以擁有它們自己的原型。如果一個(gè)原型對(duì)象的原型是一個(gè)非null的引用,那么以此類(lèi)推,這就叫作原型鏈。
原型鏈?zhǔn)且粋€(gè)用來(lái)實(shí)現(xiàn)繼承和共享屬性的有限對(duì)象鏈。
考慮這么一個(gè)情況,我們擁有兩個(gè)對(duì)象,它們之間只有一小部分不同,其他部分都相同。顯然,對(duì)于一個(gè)設(shè)計(jì)良好的系統(tǒng),我們將會(huì)重用相似的功能/代碼,而不是在每個(gè)多帶帶的對(duì)象中重復(fù)它。在基于類(lèi)的系統(tǒng)中,這個(gè)代碼重用風(fēng)格叫作類(lèi)繼承-你把相似的功能放入類(lèi)A中,然后類(lèi)B和類(lèi)C繼承類(lèi)A,并且擁有它們自己的一些小的額外變動(dòng)。
ECMAScript中沒(méi)有類(lèi)的概念。但是,代碼重用的風(fēng)格并沒(méi)有太多不同(盡管從某些方面來(lái)說(shuō)比基于類(lèi)(class-based)的方式要更加靈活)并且通過(guò)原型鏈來(lái)實(shí)現(xiàn)。這種繼承方式叫作委托繼承(delegation based inheritance)(或者,更貼近ECMAScript一些,叫作原型繼承(prototype based inheritance))。
跟例子中的類(lèi)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
足夠簡(jiǎn)單,是不是?我們看到b和c訪問(wèn)到了在對(duì)象a中定義的calculate方法。這是通過(guò)原型鏈實(shí)現(xiàn)的。
規(guī)則很簡(jiǎn)單:如果一個(gè)屬性或者一個(gè)方法在對(duì)象自身中無(wú)法找到(也就是對(duì)象自身沒(méi)有一個(gè)那樣的屬性),然后它會(huì)嘗試在原型鏈中尋找這個(gè)屬性/方法。如果這個(gè)屬性在原型中沒(méi)有查找到,那么將會(huì)查找這個(gè)原型的原型,以此類(lèi)推,遍歷整個(gè)原型鏈(當(dāng)然這在類(lèi)繼承中也是一樣的,當(dāng)解析一個(gè)繼承的方法的時(shí)候-我們遍歷class鏈( class chain))。第一個(gè)被查找到的同名屬性/方法會(huì)被使用。因此,一個(gè)被查找到的屬性叫作繼承屬性。如果在遍歷了整個(gè)原型鏈之后還是沒(méi)有查找到這個(gè)屬性的話,返回undefined值。
注意,繼承方法中所使用的this的值被設(shè)置為原始對(duì)象,而并不是在其中查找到這個(gè)方法的(原型)對(duì)象。也就是,在上面的例子中this.y取的是b和c中的值,而不是a中的值。但是,this.x是取的是a中的值,并且又一次通過(guò)原型鏈機(jī)制完成。
如果沒(méi)有明確為一個(gè)對(duì)象指定原型,那么它將會(huì)使用__proto__的默認(rèn)值-Object.prototype。Object.prototype對(duì)象自身也有一個(gè)__proto__屬性,這是原型鏈的終點(diǎn)并且值為null。
下一張圖展示了對(duì)象a,b,c之間的繼承層級(jí):
注意: 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ù)(constructor function),其以指定的模式來(lái)創(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 屬性中。
換句話說(shuō),我們可以使用構(gòu)造函數(shù)來(lái)重寫(xiě)上一個(gè)擁有對(duì)象b和對(duì)象c的例子。因此,對(duì)象a(一個(gè)原型對(duì)象)的角色由Foo.prototype來(lái)扮演:
// a constructor function function Foo(y) { // which may create objects // by specified pattern: they have after // creation own "y" property this.y = y; } // also "Foo.prototype" stores reference // to the prototype of newly created objects, // so we may use it to define shared/inherited // properties or methods, so the same as in // previous example we have: // inherited property "x" Foo.prototype.x = 10; // and inherited method "calculate" Foo.prototype.calculate = function (z) { return this.x + this.y + z; }; // now create our "b" and "c" // objects using "pattern" Foo var b = new Foo(20); var c = new Foo(30); // call the inherited method b.calculate(30); // 60 c.calculate(40); // 80 // let"s show that we reference // properties we expect console.log( b.__proto__ === Foo.prototype, // true c.__proto__ === Foo.prototype, // true // also "Foo.prototype" automatically creates // a special property "constructor", which is a // reference to the constructor function itself; // instances "b" and "c" may found it via // delegation and use to check their 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)系:
這張圖又一次說(shuō)明了每個(gè)對(duì)象都有一個(gè)原型。構(gòu)造函數(shù)Foo也有自己的__proto__,值為Function.prototype,Function.prototype也通過(guò)其__proto__屬性關(guān)聯(lián)到Object.prototype。因此,重申一下,Foo.prototype就是Foo的一個(gè)明確的屬性,指向?qū)ο?strong>b和對(duì)象c的原型。
正式來(lái)說(shuō),如果思考一下分類(lèi)的概念(并且我們已經(jīng)對(duì)Foo進(jìn)行了分類(lèi)),那么構(gòu)造函數(shù)和原型對(duì)象合在一起可以叫作「類(lèi)」。實(shí)際上,舉個(gè)例子,Python的第一級(jí)(first-class)動(dòng)態(tài)類(lèi)(dynamic classes)顯然是以同樣的屬性/方法處理方案來(lái)實(shí)現(xiàn)的。從這個(gè)角度來(lái)說(shuō),Python中的類(lèi)就是ECMAScript使用的委托繼承的一個(gè)語(yǔ)法糖。
注意: 在ES6中「類(lèi)」的概念被標(biāo)準(zhǔn)化了,并且實(shí)際上以一種構(gòu)建在構(gòu)造函數(shù)上面的語(yǔ)法糖來(lái)實(shí)現(xiàn),就像上面描述的一樣。從這個(gè)角度來(lái)看原型鏈成為了類(lèi)繼承的一種具體實(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 = new Bar("John"); console.log(bar.getName()); // John Doe
轉(zhuǎn)自 JavaScript. The core.
001------ HTML
002------ CSS
003------ DIV+CSS
004------ HTML5
005------ CSS3
006------ Web響應(yīng)式布局
007------JavaScript視頻教程
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/79119.html
摘要:原型鏈和對(duì)象的原型是對(duì)象實(shí)例和它的構(gòu)造函數(shù)之間建立的鏈接,它的值是構(gòu)造函數(shù)的。對(duì)象的原型根據(jù)上文提到的構(gòu)造調(diào)用函數(shù)的時(shí)候會(huì)創(chuàng)建一個(gè)新對(duì)象,自動(dòng)將的原型指向構(gòu)造函數(shù)的對(duì)象。 showImg(https://segmentfault.com/img/remote/1460000020185197); JS的原型、原型鏈一直是比較難理解的內(nèi)容,不少初學(xué)者甚至有一定經(jīng)驗(yàn)的老鳥(niǎo)都不一定能完全說(shuō)清...
摘要:從實(shí)現(xiàn)角度分析原型鏈歡迎來(lái)我的博客閱讀從實(shí)現(xiàn)角度分析原型鏈網(wǎng)上介紹原型鏈的優(yōu)質(zhì)文章已經(jīng)有很多了,比如說(shuō)作為補(bǔ)充,就讓我們換個(gè)角度,從實(shí)現(xiàn)來(lái)分析一下吧本文假設(shè)你對(duì)原型鏈已經(jīng)有所了解。 從實(shí)現(xiàn)角度分析js原型鏈 歡迎來(lái)我的博客閱讀:《從實(shí)現(xiàn)角度分析js原型鏈》 網(wǎng)上介紹原型鏈的優(yōu)質(zhì)文章已經(jīng)有很多了,比如說(shuō): https://github.com/mqyqingfeng/Blog/issu...
摘要:相當(dāng)于在用原型繼承編寫(xiě)復(fù)雜代碼前理解原型繼承模型十分重要。同時(shí),還要清楚代碼中原型鏈的長(zhǎng)度,并在必要時(shí)結(jié)束原型鏈,以避免可能存在的性能問(wèn)題。 js是一門(mén)動(dòng)態(tài)語(yǔ)言,js沒(méi)有類(lèi)的概念,ES6 新增了class 關(guān)鍵字,但只是語(yǔ)法糖,JavaScript 仍舊是基于原型。 至于繼承,js的繼承與java這種傳統(tǒng)的繼承不一樣.js是基于原型鏈的繼承. 在javascript里面,每個(gè)對(duì)象都有一...
摘要:圖片描述缺點(diǎn)是無(wú)法實(shí)現(xiàn)多繼承可以在構(gòu)造函數(shù)中,為實(shí)例添加實(shí)例屬性。 對(duì)象的方法 Object.assign() 對(duì)象可以簡(jiǎn)寫(xiě) ,如果 key 和 value 相等則可以簡(jiǎn)寫(xiě) let name = xm; let age = 2; let obj = { name, age, fn(){ // 可以省略函數(shù)關(guān)鍵字和冒號(hào): console.log(2...
摘要:構(gòu)造函數(shù),實(shí)例,原型三者的關(guān)系如下圖構(gòu)造函數(shù)是構(gòu)成整個(gè)原型鏈的關(guān)鍵,是他利用將原型傳給了后代。因此,通過(guò)操縱構(gòu)造函數(shù)的,就能夠操縱原型鏈,從而對(duì)原型鏈進(jìn)行自在的拼接。 要理解js的原型鏈主要就是理清楚以下三者的關(guān)系: 構(gòu)造函數(shù)的protitype屬性 對(duì)象的__proto__屬性 對(duì)象的constructor屬性 在js中,函數(shù)作為一等公民,它是一個(gè)對(duì)象,可以擁有自己的屬性,可...
摘要:二構(gòu)造函數(shù)我們先復(fù)習(xí)一下構(gòu)造函數(shù)的知識(shí)上面的例子中和都是的實(shí)例。這兩個(gè)實(shí)例都有一個(gè)構(gòu)造函數(shù)屬性,該屬性是一個(gè)指針指向。原型鏈其中是對(duì)象的實(shí)例。 一. 普通對(duì)象與函數(shù)對(duì)象 JavaScript 中,萬(wàn)物皆對(duì)象!但對(duì)象也是有區(qū)別的。分為普通對(duì)象和函數(shù)對(duì)象,Object 、Function 是 JS 自帶的函數(shù)對(duì)象。下面舉例說(shuō)明 var o1 = {}; var o2 =new Objec...
閱讀 1876·2021-11-25 09:43
閱讀 3705·2021-11-24 10:32
閱讀 1099·2021-10-13 09:39
閱讀 2347·2021-09-10 11:24
閱讀 3363·2021-07-25 21:37
閱讀 3481·2019-08-30 15:56
閱讀 876·2019-08-30 15:44
閱讀 1466·2019-08-30 13:18