摘要:上圖中的在原型繼承稱作構(gòu)造器。構(gòu)造器就是一個普通的函數(shù),但是將操作符用到構(gòu)造器上時,它會執(zhí)行一個叫的過程。從第條可以看到,構(gòu)造器生成的對象的屬性會指向構(gòu)造器的值,這就是我們構(gòu)造原型鏈的關鍵。
基于類的繼承是大多數(shù)人所熟悉的,也是比較容易理解的。當我們形成類型繼承的思維定勢后,再次接觸原型繼承可能會覺得有些奇怪并難以理解。你更可能會吐槽,原型繼承根本就不能叫做繼承,一點都不面向?qū)ο蟆1救俗畛跻彩沁@樣認為的,但深入仔細的對比后發(fā)現(xiàn),兩者其實并沒有本質(zhì)的差別,只是表面有點不一樣而已。且看下面的分析。
類型繼承先看一個類型繼承的例子,代碼如下:
public class A { //... } public class B extends A { //... } public class C extends B { //... } C c = new C();
A、B、C為三個繼承關系的類,最后將類C實例化。下面這張圖描述了類和實例的對應關系。左邊為類,右邊為其對應實例。
我們看到,類C實例化后,內(nèi)存中不僅存在c對象,同時還有a、b兩個對象。因為在java中,當我們在執(zhí)行new C()操作時,jvm中會發(fā)生如下過程:
創(chuàng)建A的實例a。
創(chuàng)建B的實例b,并將實例b的super指針指向a。
創(chuàng)建C的實例c,并將實例c的super指針指向b。
過程1和過程2對用戶是透明的,不需要人工干預,引擎會按照“藍圖”把這兩個過程完成。通過上圖右半部分我們可以看到,super指針將a、b、c三個實例串起來了,這里是實現(xiàn)繼承的關鍵。當我們在使用實例c的某個屬性或方法時,若實例c中不存在則會沿著super指針向父類對象查找,直到找到,找不到則出錯。這就是繼承能夠達到復用目的內(nèi)部機制。看到這里大家或許已經(jīng)聯(lián)想到原型鏈了,super所串起來的這個鏈幾乎和原型鏈一樣,只是叫法不一樣而已。下面我們就來看看原型繼承。
原型繼承上面是原型繼承的示意圖。先看圖的右半部分,__proto__指針形成的對象鏈就是原型鏈。__proto__是一個私有屬性,只能看不準訪問(某些瀏覽器看也不給看)。__proto__的作用和前面的super是一樣的,原型鏈實現(xiàn)復用的機制和類型繼承也幾乎是一樣的,這里不再重復。有一點不一樣就是原型繼承中的屬性寫操作只會改變當前對象并不會影響原型鏈上的對象。
如何去構(gòu)造原型鏈呢?看上去要稍微麻煩一些。原型繼承里面沒有類的概念,我們需要通過代碼,手動完成這個過程。上圖中的A、B、C在原型繼承稱作構(gòu)造器。構(gòu)造器就是一個普通的函數(shù),但是將new操作符用到構(gòu)造器上時,它會執(zhí)行一個叫[[construct]]的過程。大致如下:
創(chuàng)建一個空對象obj。
設置obj的內(nèi)部屬性[[Class]]為Object。
設置obj的內(nèi)部屬性[[Extensible]]為true。
設置obj的[[__proto__]]屬性:如果函數(shù)對象prototype的值為對象則直接賦給obj,否則賦予Object的prototype值。
調(diào)用函數(shù)對象的[[Call]]方法并將結(jié)果賦給result。
如果result為對象則返回result,否則返回obj。
從第4條可以看到,構(gòu)造器生成的對象的__proto__屬性會指向構(gòu)造器的prototype值,這就是我們構(gòu)造原型鏈的關鍵。下面的代碼是上圖原型鏈的構(gòu)造過程。
function A(){ //... } function B(){ //... } function C(){ //... } var a = new A(); B.prototype = a; var b = new B(); C.prototype = b; var c = new C();
上述代碼雖然能達到目的,但有點繁瑣,我們可以將這個過程封裝一下。backbone的實現(xiàn)是這樣的:
var extend = function(protoProps, staticProps) { var parent = this; var child; if (protoProps && _.has(protoProps, "constructor")) { child = protoProps.constructor; } else { child = function(){ return parent.apply(this, arguments); }; } _.extend(child, parent, staticProps); child.prototype = _.create(parent.prototype, protoProps); child.prototype.constructor = child; child.__super__ = parent.prototype; return child; }
其中_.extend(child, parent, staticProps)是將staticProps和parent對象的屬性復制給child。_.create方法的實現(xiàn)大概如下。
_.create = function(prototype, protoProps){ var F = function(){}; F.prototype = prototype; var result = new F(); return _.extend(result, protoProps); }
有了extend方法,我們的代碼就可以寫成:
A.extend = extend; var B = A.extend({ //... ); var C = B.extend({ //... ); var c = new C();
這段代碼和類型繼承的代碼十分相似,通過原型繼承我們也可以達到類型繼承的效果。但是通過前面的比較我們發(fā)現(xiàn),繼承的本質(zhì)就其實就是對象的復用。原型繼承本身就是以對象為出發(fā)點考慮的,所以大多時候我們并不一定要按照類型繼承的思維考慮問題。而且js是弱類型,對象的操作也極其自由,上述的_.create方法可能是js里面實現(xiàn)繼承的一個更簡單有效的方法。
總結(jié)前面討論了兩種繼承方式,可以看到,繼承的本質(zhì)其實就是對象的復用。本人覺得原型繼承更加的簡單和明確,它直接就是從對象的角度考慮問題。當然,如果你需要一個非常強大的繼承體系,你也可以構(gòu)造出一個類似類型繼承的模式。相對來說,本人覺得原型繼承更靈活和自由些,也是非常巧妙和獨特的。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/86087.html
摘要:上圖中的在原型繼承稱作構(gòu)造器。構(gòu)造器就是一個普通的函數(shù),但是將操作符用到構(gòu)造器上時,它會執(zhí)行一個叫的過程。從第條可以看到,構(gòu)造器生成的對象的屬性會指向構(gòu)造器的值,這就是我們構(gòu)造原型鏈的關鍵。 基于類的繼承是大多數(shù)人所熟悉的,也是比較容易理解的。當我們形成類型繼承的思維定勢后,再次接觸原型繼承可能會覺得有些奇怪并難以理解。你更可能會吐槽,原型繼承根本就不能叫做繼承,一點都不面向?qū)ο蟆1救?..
摘要:通常有這兩種繼承方式接口繼承和實現(xiàn)繼承。理解繼承的工作是通過調(diào)用函數(shù)實現(xiàn)的,所以是寄生,將繼承工作寄托給別人做,自己只是做增強工作。適用基于某個對象或某些信息來創(chuàng)建對象,而不考慮自定義類型和構(gòu)造函數(shù)。 一、繼承的概念 繼承,是面向?qū)ο笳Z言的一個重要概念。通常有這兩種繼承方式:接口繼承和實現(xiàn)繼承。接口繼承只繼承方法簽名,而實現(xiàn)繼承則繼承實際的方法。 《JS高程》里提到:由于函數(shù)沒有簽名,...
摘要:如下所示在規(guī)范中,已經(jīng)正式把屬性添加到規(guī)范中也可以通過設置和獲取對象的原型對象對象之間的關系可以用下圖來表示但規(guī)范主要介紹了如何利用構(gòu)造函數(shù)去構(gòu)建原型關系。 前言 在軟件工程中,代碼重用的模式極為重要,因為他們可以顯著地減少軟件開發(fā)的成本。在那些主流的基于類的語言(比如Java,C++)中都是通過繼承(extend)來實現(xiàn)代碼復用,同時類繼承引入了一套類型規(guī)范。而JavaScript是...
摘要:深入之繼承的多種方式和優(yōu)缺點深入系列第十五篇,講解各種繼承方式和優(yōu)缺點。對于解釋型語言例如來說,通過詞法分析語法分析語法樹,就可以開始解釋執(zhí)行了。 JavaScript深入之繼承的多種方式和優(yōu)缺點 JavaScript深入系列第十五篇,講解JavaScript各種繼承方式和優(yōu)缺點。 寫在前面 本文講解JavaScript各種繼承方式和優(yōu)缺點。 但是注意: 這篇文章更像是筆記,哎,再讓我...
摘要:在規(guī)范中,引入了的概念。使用中的聲明一個類,是非常簡單的事。中面向?qū)ο髮嵗谋澈笤恚瑢嶋H上就是原型對象。與區(qū)別理解上述原理后,還需要注意與屬性的區(qū)別。實際上,在中,類繼承的本質(zhì)依舊是原型對象。 在 ES6 規(guī)范中,引入了 class 的概念。使得 JS 開發(fā)者終于告別了,直接使用原型對象模仿面向?qū)ο笾械念惡皖惱^承時代。 但是JS 中并沒有一個真正的 class 原始類型, clas...
閱讀 3408·2021-09-22 16:00
閱讀 3452·2021-09-07 10:26
閱讀 2989·2019-08-30 15:55
閱讀 2858·2019-08-30 13:48
閱讀 1366·2019-08-30 12:58
閱讀 2162·2019-08-30 11:15
閱讀 945·2019-08-30 11:08
閱讀 525·2019-08-29 18:41