JavaScript繼承理解

??初學JavaScript,一直很好奇這個沒有類定義的語言如何實現(xiàn)了面向?qū)ο蟮某绦蛟O(shè)計模式,以及如何實現(xiàn)類屬性和方法的繼承。經(jīng)過我兩天的冥思苦想,終于有了自己的一些感悟。

首先我們明確幾個重要的概念:

1 什么是原型?

??根據(jù)JavaScript權(quán)威指南上的解釋:每一個JavaScript對象(null除外)都和另外一個對象關(guān)聯(lián)?!傲硪粋€”對象就是我們熟知的原型,每一個對象都從原型繼承屬性。沒有原型的對象不多,Object.prototype就是其中之一。它不繼承任何屬性。其他原型對象都是普通對象,普通對象都具有原型。所有內(nèi)置構(gòu)造函數(shù)(以及大部分自定義的構(gòu)造函數(shù))都具有一個繼承自O(shè)bject.prototype的原型。

2 JavaScript 中Function(函數(shù))和Object(對象)的關(guān)系?

??JavaScript最開始定義一個對象叫做Object.prototype,本質(zhì)上就是一組無序key-value存儲({}),之后再此基礎(chǔ)上,研發(fā)出可以保存一段“指令”并“生產(chǎn)產(chǎn)品”的原型,叫作函數(shù),起名Function.prototype,本質(zhì)上就是[Function:Empty](空函數(shù))。

??為了規(guī)?;a(chǎn)生對象,JS在函數(shù)的基礎(chǔ)上構(gòu)造出了兩個構(gòu)造器:生產(chǎn)函數(shù)對象的構(gòu)造器叫做Function,生產(chǎn)key-value存儲對象的構(gòu)造器叫做Object。

??JavaScript在每個對象上打了個標簽__proto__,以標明這個對象是根據(jù)哪個原型生產(chǎn)的。為原型打了個標簽constructor,標明哪個構(gòu)造器可以依照這個原型生產(chǎn)對象。為構(gòu)造器打了標簽prototype,標明這個構(gòu)造器可以從哪個原型生產(chǎn)對象。

3 什么是原型鏈?

??原型鏈是一種機制,指的是JavaScript的每一個對象包括原型對象都有一個[[proto]](通過__proto__訪問)屬性指向創(chuàng)建它的函數(shù)對象的原型對象,即函數(shù)的prototype屬性。原型對象又可以通過[[proto]]屬性訪問它的上層原型對象,一層一層往上繼承屬性和方法,這就是原型鏈。原型鏈的作用就是用來實現(xiàn)對象的繼承。

原型鏈示意圖:

默認JavaScript中的構(gòu)造函數(shù)當做類,new出來的對象稱為實例,原型對象簡稱原型,具體實現(xiàn)上代碼:

首先我們構(gòu)造父類SuperClass,并創(chuàng)建它的原型:

function SuperClass() {
    if(!(this instanceof SuperClass))
        return new SuperClass();
    else{
        this.id1 = '1';
        this.color1 = ['red'];
    }
}

SuperClass.prototype={
    id2 : '2',
    color2 : ['yellow']
};

然后構(gòu)造一個空的子類:

function SubClass() {
    if(!(this instanceof SubClass))
        return new SubClass();
    else{}
}

定義一個打印對象所有屬性和方法的函數(shù)以便調(diào)試:

function ConsoleDetail(obj) {
    for(var i in obj){
        console.log(i+':'+obj[i]);
    }
}

這里我對構(gòu)造函數(shù)本身的屬性方法和創(chuàng)建出來對象的屬性方法有點迷惑,于是測試:

console.log('-----------------------------------');
SuperClass.name = 'wenjun';
ConsoleDetail(SuperClass);
console.log('-----------------------------------');
var obj = new SuperClass();
ConsoleDetail(obj);

Chrome控制臺輸出:

可以看到實例化的對象構(gòu)造屬性方法和原型屬性方法都符合預期。但是SubClass對象上明明定義了name屬性,卻什么也沒打印出來,出了什么錯?

原來Function.name是一個自帶屬性,它的值就是我們定義Function時取得名字(字符串類型),對于匿名函數(shù),它的值為“anonymous”。詳情可以閱讀文檔

既然name不行,換個age試試:

console.log('-----------------------------------');
SuperClass.age = '20';
ConsoleDetail(SuperClass);
console.log('-----------------------------------');
var obj = new SuperClass();
ConsoleDetail(obj);

結(jié)果:

構(gòu)造函數(shù)本身也是一個對象,它自身可以掛載屬性和方法,然而這和使用它構(gòu)造出來的實例的屬性和方法(通過關(guān)鍵字<this.XXX>定義)沒有任何關(guān)系!

不糾結(jié)這個了,正式開始繼承:

console.log('-----------------------------------');
SubClass.prototype = new SuperClass();
ConsoleDetail(SubClass);
console.log('-----------------------------------');
var obj = SubClass();
ConsoleDetail(obj);

結(jié)果:

子類原型賦值為父類實例,對子類本身沒有任何影響,子類上依舊沒有任何屬性,而子類構(gòu)造的實例卻可以繼承子類構(gòu)造函數(shù)和父類構(gòu)造函數(shù)的屬性和方法(通過關(guān)鍵字<this.XXX>定義)及父類原型上的屬性和方法。

為了對比實例調(diào)用父類繼承屬性和方法的效果,我多構(gòu)建一個obj2同樣繼承SubClass:

var obj2 = SubClass();

先更改SuperClass構(gòu)造函數(shù)中的id1和color1:

obj.id1 = '11';
obj.color1.push('black');
ConsoleDetail(obj2);

結(jié)果:

發(fā)現(xiàn)id1沒變,而color1變了。

繼續(xù)更改SuperClass原型上的id2和color2:

obj.id2 = '22';
obj.color2.push('white');
ConsoleDetail(obj2);

結(jié)果:

發(fā)現(xiàn)id2沒變,而color2變了。

子類實例繼承父類構(gòu)造函數(shù)和原型對象的屬性及方法,只要是值類型的都是賦值(深拷貝),對象類型都是引用(淺拷貝)!

接著放棄之前的修改,單純嘗試重新定義屬性或方法:

obj.color1 = function () {
        console.log("redefine color1.");
};
ConsoleDetail(obj);
console.log('-----------------------------------');
ConsoleDetail(obj2);

結(jié)果:

這里的color1是Function對象,如果按照之前的邏輯,對象類型都是引用,為什么這里obj2沒有任何變化?

仔細思考了很久,我能給出的最合理解釋是:obj.color1 = function(){...}實際上是在實例上掛載了與通過原型鏈繼承的屬性或方法同名的屬性或方法,導致新定義的屬性或方法覆蓋了obj1原型鏈上的同名屬性或方法,只是查詢(從實例本身->實例原型->原型的原型->...,瀏覽器似乎也是按照這個順序打印結(jié)果的)該屬性會忽略使用原型上的同名屬性或方法,但實際上該操作并沒有改變原型對于該屬性或方法的定義!所以obj2繼承過來的colo1沒有任何改變,但是瀏覽器打印obj的順序卻有了變化,這又剛好印證了我的解釋。

??這就是我對于原型鏈在類式繼承模式下的理解,初學JavaScript,水平有限,里面一些解釋還只是自圓其說,沒有找到資料確認,但是我會好好加油的!也希望大家指出我的不足,給我提供一些學習JavaScript的意見和建議,之后我還會分享其他工廠模式下的繼承鏈分析,敬請期待!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ??面向?qū)ο螅∣bject-Oriented,OO)的語言有一個標志,那就是它們都有類的概念,而通過類可以創(chuàng)建任意...
    霜天曉閱讀 2,263評論 0 6
  • 理解 javascript 的原型鏈及繼承 以上所有的運行結(jié)果都是 true; 三種構(gòu)造對象的方法: 通過對象字面...
    你期待的花開閱讀 1,671評論 0 3
  • 在JavaScript中,原型鏈作為一個基礎(chǔ),老生長談,今天我們就來深入的解讀一下原型鏈。 本章主要講的是下面幾點...
    Devinnn閱讀 1,504評論 1 6
  • 《鏡子》 站在鏡子前, 光鮮的人光鮮, 猥瑣的人猥瑣, 端莊的人端莊, 丑惡的人丑惡, 鏡子就是公正的代言。 映遠...
    不語不問閱讀 368評論 1 5
  • 日期:20180116精聽材料:C7T2S1耗時:30分鐘工具:手機小站雅思app 聽錯的單詞和詞組(連讀、同音等...
    精進精進再精進閱讀 299評論 0 0

友情鏈接更多精彩內(nèi)容