摘要:構(gòu)造函數(shù)除了以指定模式創(chuàng)建對象之外,構(gòu)造函數(shù)也做了另一個有用的事情它自動地為新創(chuàng)建的對象設(shè)置一個原型對象。正式來說,如果思考一下分類的概念并且我們已經(jīng)對進行了分類,那么構(gòu)造函數(shù)和原型對象合在一起可以叫作類。
這篇文章是「深入ECMA-262-3」系列的一個概覽和摘要。每個部分都包含了對應(yīng)章節(jié)的鏈接,所以你可以閱讀它們以便對其有更深的理解。
對象ECMAScript做為一個高度抽象的面向?qū)ο笳Z言,是通過對象來交互的。即使ECMAScript里邊也有基本類型,但是,當(dāng)需要的時候,它們也會被轉(zhuǎn)換成對象。
一個對象就是一個屬性集合,并擁有一個獨立的prototype(原型)對象。這個prototype可以是一個對象或者null。
讓我們看一個關(guān)于對象的基本例子。一個對象的prototype是以內(nèi)部的[[Prototype]]屬性來引用的。但是,在示意圖里邊我們將會使用__
對于以下代碼:
var foo = { x: 10, y: 20 };
我們擁有一個這樣的結(jié)構(gòu),兩個明顯的自身屬性和一個隱含的__proto__屬性,這個屬性是對foo原型對象的引用:
這些prototype有什么用?讓我們以原型鏈(prototype chain)的概念來回答這個問題。
原型對象也是簡單的對象并且可以擁有它們自己的原型。如果一個原型對象的原型是一個非null的引用,那么以此類推,這就叫作原型鏈。
原型鏈是一個用來實現(xiàn)繼承和共享屬性的有限對象鏈。
考慮這么一個情況,我們擁有兩個對象,它們之間只有一小部分不同,其他部分都相同。顯然,對于一個設(shè)計良好的系統(tǒng),我們將會重用相似的功能/代碼,而不是在每個多帶帶的對象中重復(fù)它。在基于類的系統(tǒng)中,這個代碼重用風(fēng)格叫作類繼承-你把相似的功能放入類A中,然后類B和類C繼承類A,并且擁有它們自己的一些小的額外變動。
ECMAScript中沒有類的概念。但是,代碼重用的風(fēng)格并沒有太多不同(盡管從某些方面來說比基于類(class-based)的方式要更加靈活)并且通過原型鏈來實現(xiàn)。這種繼承方式叫作委托繼承(delegation based inheritance)(或者,更貼近ECMAScript一些,叫作原型繼承(prototype based inheritance))。
跟例子中的類A,B,C相似,在ECMAScript中你創(chuàng)建對象:a,b,c。于是,對象a中存儲對象b和c中通用的部分。然后b和c只存儲它們自身的額外屬性或者方法。
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
足夠簡單,是不是?我們看到b和c訪問到了在對象a中定義的calculate方法。這是通過原型鏈實現(xiàn)的。
規(guī)則很簡單:如果一個屬性或者一個方法在對象自身中無法找到(也就是對象自身沒有一個那樣的屬性),然后它會嘗試在原型鏈中尋找這個屬性/方法。如果這個屬性在原型中沒有查找到,那么將會查找這個原型的原型,以此類推,遍歷整個原型鏈(當(dāng)然這在類繼承中也是一樣的,當(dāng)解析一個繼承的方法的時候-我們遍歷class鏈( class chain))。第一個被查找到的同名屬性/方法會被使用。因此,一個被查找到的屬性叫作繼承屬性。如果在遍歷了整個原型鏈之后還是沒有查找到這個屬性的話,返回undefined值。
注意,繼承方法中所使用的this的值被設(shè)置為原始對象,而并不是在其中查找到這個方法的(原型)對象。也就是,在上面的例子中this.y取的是b和c中的值,而不是a中的值。但是,this.x是取的是a中的值,并且又一次通過原型鏈機制完成。
如果沒有明確為一個對象指定原型,那么它將會使用__proto__的默認值-Object.prototype。Object.prototype對象自身也有一個__proto__屬性,這是原型鏈的終點并且值為null。
下一張圖展示了對象a,b,c之間的繼承層級:
注意: ES5標準化了一個實現(xiàn)原型繼承的可選方法,即使用 Object.create 函數(shù):
var b = Object.create(a, {y: {value: 20}}); var c = Object.create(a, {y: {value: 30}});
你可以在對應(yīng)的章節(jié)獲取到更多關(guān)于ES5新API的信息。 ES6標準化了 __proto__屬性,并且可以在對象初始化的時候使用它。
通常情況下需要對象擁有相同或者相似的狀態(tài)結(jié)構(gòu)(也就是相同的屬性集合),賦以不同的狀態(tài)值。在這個情況下我們可能需要使用構(gòu)造函數(shù)(constructor function),其以指定的模式來創(chuàng)造對象。
構(gòu)造函數(shù)除了以指定模式創(chuàng)建對象之外,構(gòu)造函數(shù)也做了另一個有用的事情-它自動地為新創(chuàng)建的對象設(shè)置一個原型對象。這個原型對象存儲在ConstructorFunction.prototype 屬性中。
換句話說,我們可以使用構(gòu)造函數(shù)來重寫上一個擁有對象b和對象c的例子。因此,對象a(一個原型對象)的角色由Foo.prototype來扮演:
// 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 );
這個代碼可以表示為如下關(guān)系:
這張圖又一次說明了每個對象都有一個原型。構(gòu)造函數(shù)Foo也有自己的__proto__,值為Function.prototype,Function.prototype也通過其__proto__屬性關(guān)聯(lián)到Object.prototype。因此,重申一下,Foo.prototype就是Foo的一個明確的屬性,指向?qū)ο?strong>b和對象c的原型。
正式來說,如果思考一下分類的概念(并且我們已經(jīng)對Foo進行了分類),那么構(gòu)造函數(shù)和原型對象合在一起可以叫作「類」。實際上,舉個例子,Python的第一級(first-class)動態(tài)類(dynamic classes)顯然是以同樣的屬性/方法處理方案來實現(xiàn)的。從這個角度來說,Python中的類就是ECMAScript使用的委托繼承的一個語法糖。
注意: 在ES6中「類」的概念被標準化了,并且實際上以一種構(gòu)建在構(gòu)造函數(shù)上面的語法糖來實現(xiàn),就像上面描述的一樣。從這個角度來看原型鏈成為了類繼承的一種具體實現(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)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/79119.html
摘要:原型鏈和對象的原型是對象實例和它的構(gòu)造函數(shù)之間建立的鏈接,它的值是構(gòu)造函數(shù)的。對象的原型根據(jù)上文提到的構(gòu)造調(diào)用函數(shù)的時候會創(chuàng)建一個新對象,自動將的原型指向構(gòu)造函數(shù)的對象。 showImg(https://segmentfault.com/img/remote/1460000020185197); JS的原型、原型鏈一直是比較難理解的內(nèi)容,不少初學(xué)者甚至有一定經(jīng)驗的老鳥都不一定能完全說清...
摘要:從實現(xiàn)角度分析原型鏈歡迎來我的博客閱讀從實現(xiàn)角度分析原型鏈網(wǎng)上介紹原型鏈的優(yōu)質(zhì)文章已經(jīng)有很多了,比如說作為補充,就讓我們換個角度,從實現(xiàn)來分析一下吧本文假設(shè)你對原型鏈已經(jīng)有所了解。 從實現(xiàn)角度分析js原型鏈 歡迎來我的博客閱讀:《從實現(xiàn)角度分析js原型鏈》 網(wǎng)上介紹原型鏈的優(yōu)質(zhì)文章已經(jīng)有很多了,比如說: https://github.com/mqyqingfeng/Blog/issu...
摘要:相當(dāng)于在用原型繼承編寫復(fù)雜代碼前理解原型繼承模型十分重要。同時,還要清楚代碼中原型鏈的長度,并在必要時結(jié)束原型鏈,以避免可能存在的性能問題。 js是一門動態(tài)語言,js沒有類的概念,ES6 新增了class 關(guān)鍵字,但只是語法糖,JavaScript 仍舊是基于原型。 至于繼承,js的繼承與java這種傳統(tǒng)的繼承不一樣.js是基于原型鏈的繼承. 在javascript里面,每個對象都有一...
摘要:圖片描述缺點是無法實現(xiàn)多繼承可以在構(gòu)造函數(shù)中,為實例添加實例屬性。 對象的方法 Object.assign() 對象可以簡寫 ,如果 key 和 value 相等則可以簡寫 let name = xm; let age = 2; let obj = { name, age, fn(){ // 可以省略函數(shù)關(guān)鍵字和冒號: console.log(2...
摘要:構(gòu)造函數(shù),實例,原型三者的關(guān)系如下圖構(gòu)造函數(shù)是構(gòu)成整個原型鏈的關(guān)鍵,是他利用將原型傳給了后代。因此,通過操縱構(gòu)造函數(shù)的,就能夠操縱原型鏈,從而對原型鏈進行自在的拼接。 要理解js的原型鏈主要就是理清楚以下三者的關(guān)系: 構(gòu)造函數(shù)的protitype屬性 對象的__proto__屬性 對象的constructor屬性 在js中,函數(shù)作為一等公民,它是一個對象,可以擁有自己的屬性,可...
摘要:二構(gòu)造函數(shù)我們先復(fù)習(xí)一下構(gòu)造函數(shù)的知識上面的例子中和都是的實例。這兩個實例都有一個構(gòu)造函數(shù)屬性,該屬性是一個指針指向。原型鏈其中是對象的實例。 一. 普通對象與函數(shù)對象 JavaScript 中,萬物皆對象!但對象也是有區(qū)別的。分為普通對象和函數(shù)對象,Object 、Function 是 JS 自帶的函數(shù)對象。下面舉例說明 var o1 = {}; var o2 =new Objec...
閱讀 1986·2021-11-25 09:43
閱讀 3763·2021-11-24 10:32
閱讀 1269·2021-10-13 09:39
閱讀 2449·2021-09-10 11:24
閱讀 3424·2021-07-25 21:37
閱讀 3547·2019-08-30 15:56
閱讀 931·2019-08-30 15:44
閱讀 1517·2019-08-30 13:18