JavaScript 繼承機制小節(jié)

寫在前面

推薦大家先看一下這篇文章的姊妹篇JavaScript創(chuàng)建對象的四種常見模式

這個抄書筆記在介紹四個模式時挤聘,也或多或少的解釋了一下原型鏈的概念,話不多說鞍陨,開始本篇文章从隆。

原型鏈

基于原型鏈的繼承

JavaScript將原型鏈作為實現(xiàn)繼承的主要方式缭裆±裕基本思想是利用原型讓一個引用類型,繼承另外一個引用類型的屬性和方法盅藻。關(guān)于構(gòu)造函數(shù)畅铭、實例和原型的關(guān)系就不在贅述了,參見下圖(這個圖是目前對三者關(guān)系總結(jié)最棒的圖了~):

原型鏈?zhǔn)疽鈭D

那么想一下假残,如果讓原型對象等于另一個類型的實例呢辉懒?結(jié)果會怎樣谍失,顯然,此時的原型對象將會包含一個指向另一個原型的指針颠印,相應(yīng)的抹竹,另一個原型中也包含了一個指向另一個構(gòu)造函數(shù)的指針。假如另一個原型又指向了第三個原型的實例钞楼,那么上述關(guān)系依然成立袄琳,層層推進,就構(gòu)成了實例與原型的鏈條雳殊,這就是所謂原型鏈的基本概念窗轩,上面那張關(guān)系圖的基礎(chǔ)上無限延展~

實現(xiàn)原型鏈有一個基本的模式,代碼如下:

function SuperType(){  
    this.property = true;  
}  
  
SuperType.prototype.getSuperValue = function(){  
    return this.property;  
};  
  
function SubType(){  
    this.subProperty = false;  
}  
  
//讓SubType的原型指向SuperType的原型仓洼,從而繼承了SuperType
SubType.prototype = new SuperType();
  
SubType.prototype.getSubValue=function(){  
    return this.subProperty;  
}  

var instance = new SubType();
console.log(instance.getSuperValue());//true

上面代碼演示的是SubTypeSuperType的繼承〔肝兀基本原理是通過創(chuàng)建SubType的實例箕戳,并將該實例賦值為SuperType.prototype陵吸,本質(zhì)是重寫原型對象。下面這張圖壮虫,描述了他們之間的關(guān)系

在上文中囚似,我們沒有使用SubType默認提供的原型,而是換成了SuperType的實例徐伐。于是新原型不僅具有作為一個SuperType實例所擁有的全部屬性和方法搬素,而且其內(nèi)部還有一個指針魏保,指向SuperType的原型谓罗。最終結(jié)果就是這個樣子:instance指向SubType的原型,SubType的原型又指向了SuperType的原型檩咱。getSuperValue()方法還在SuperType.prototype中刻蚯,但是property屬性則位于SubType.prototype中(因為SubType.prototypeSuperType.prototype的實例,所以享有這個屬性)躬充。

搜索的經(jīng)歷三個步驟:
1)搜索實例對象Instance
2)搜索SubType.prototype
3)搜索SuperType.prototype

注意此時instanceconstructor屬性現(xiàn)在指向了SuperType(正常應(yīng)該指向構(gòu)造函數(shù)SubType,但是在實例化SubType之前原來的SubType.prototype中的指向了SuperType的原型以政,而SuperType原型的constructor屬性又指向SuperType伴找,,抖誉,好繞啊穆役。。梳杏。)

注意事項:

修改原型對象的屬性時淹接,那么所有的實例通過原型鏈都會訪問到更改后的屬性;但是如果將原型對象設(shè)置為其他值Person.prototype = {},那么原來的實例和原來的原型對象的關(guān)系沒有變化劲适,原有實例還擁有原來原型對象的屬性厢蒜。也就是說:直接修改prototype屬性不會影響已經(jīng)創(chuàng)建的屬性斑鸦,影響的將是新創(chuàng)建的實例,這里經(jīng)常會出筆試題巷屿。

子類型有時候要重寫超類型中的某個方法嘱巾,或者需要添加超類型中不存在的方法時。不管怎樣篙螟,給原型添加方法的代碼一定要放在替換原型代碼之后问拘。

通過原型鏈實現(xiàn)繼承慢味,不能使用對象字面量來創(chuàng)建原型墅冷,因為這樣會重寫原型鏈

首先我們先來看看正常情況下的代碼結(jié)構(gòu)

function SuperType(){  
    this.property = true;  
}  
  
SuperType.prototype.getSuperValue = function(){  
    return this.property;  
};  
  
function SubType(){  
    this.subProperty = false;  
}  
  
//讓SubType的原型指向SuperType的原型寞忿,從而繼承了SuperType
SubType.prototype = new SuperType();
  
SubType.prototype.getSubValue = function(){
    return this.subProperty;  
},
SubType.prototype.someOtherMehtod = function() {
    return false;     
}

var instance = new SubType();
console.log(instance)
console.log(instance.getSuperValue());//true

這段代碼我們打印一下Instance來看一下繼承關(guān)系

然后我們將prototype設(shè)置成字面量形式,比如下面的示例

function SuperType(){  
    this.property = true;  
}  
  
SuperType.prototype.getSuperValue = function(){  
    return this.property;  
};  
  
function SubType(){  
    this.subProperty = false;  
}  
  
//讓SubType的原型指向SuperType的原型叫编,從而繼承了SuperType
SubType.prototype = new SuperType();
  
SubType.prototype = {
    getSubValue: function(){  
        return this.subProperty;  
    },
    someOtherMehtod: function() {
        return false;
    }      
}

var instance = new SubType();
console.log(instance.getSuperValue());//error

這段代碼我們打印一下Instance的繼承關(guān)系

原型鏈繼承的問題

最主要的問題還是來自包含引用類型值的問題搓逾。上一篇文章中介紹過包含引用類型值的原型屬性會被所有實例共享霞篡,而這也是為什么在構(gòu)造函數(shù)中而不是在原型中定義屬性的原因端逼。在上面的例子中如果在SuperType中定義一個引用類型,如數(shù)組屬性顶滩,那么每個SubType.prototypeSuperType的實例)都將擁有一個引用類型屬性礁鲁,那么每個instance都會共享這個引用屬性,一個實例修改了這個屬性冗美,會影響到其他事例着憨。

function SuperType(){  
     this.property = true;  
     this.colors=['red','blue','green'];  
 }  
  
 SuperType.prototype.getSuperValue = function(){  
     return this.property;  
 };  
  
 function SubType(){  
     this.subProperty = false;  
 }  
  
 SubType.prototype = new SuperType();  
  
 SubType.prototype.getSubValue = function(){  
     return this.subProperty;  
 }  
  
 var sub1 = new SubType();  
 sub1.colors.push('yellow');  
 var sub2 = new SubType();  
 console.log(sub2.colors);//["red", "blue", "green", "yellow"] 

其次甲抖,創(chuàng)建子類型實例的時候心铃,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)

所以,很少有人單獨使用原型鏈柱衔,所以就有了下面的方式。

借用構(gòu)造函數(shù)

借用構(gòu)造函數(shù)完善原型鏈繼承

為了解決原型中包含引用類型值所帶來的問題唆铐,開發(fā)人員開始使用一種叫做借用構(gòu)造函數(shù)的技術(shù)(或叫經(jīng)典繼承)主要思路是:子類型構(gòu)造函數(shù)中調(diào)用超類型的構(gòu)造函數(shù)艾岂,使實例擁有自己獨有的屬性

function SuperType(){  
    this.colors = ['red','blue','green'];  
}  
  
function SubType(){  
    //繼承了SuperType
    SuperType.call(this); 
}  
  
var sub1 = new SubType();  
sub1.colors.push('yellow');  
console.log(sub2.colors);//["red", "blue", "green", "yellow"]  

var sub2 = new SubType();  
console.log(sub2.colors);//["red", "blue", "green"]  

這樣在子類的構(gòu)造函數(shù)中調(diào)用父類的構(gòu)造函數(shù),這樣的寫法實際上是在(未來將要)新創(chuàng)建的SubType實例環(huán)境下調(diào)用了SuperType構(gòu)造函數(shù)脆炎,這樣一來氓辣,就會在新SubType對象上執(zhí)行SuperType()函數(shù)中定義的對象初始化代碼钞啸,Subtype的每個實例就會有自己的colors副本了。

這樣做還多了個好處体斩,可以傳遞參數(shù)了

function SuperType(name){  
    this.property = name;  
}  
  
function SubType(name){  
    SuperType.call(this,'Nichllas');  
    //實例屬性
    this.age = 29
}  
  
var sub1 = new SubType();  
console.log(sub1.name);//Nichllas  
console.log(sub1.age);//29  

存在的問題

無法避免構(gòu)造函數(shù)模式存在的問題--方法都在構(gòu)造函數(shù)中定義硕勿,因此函數(shù)服用無從談起,下面介紹一種最常用的繼承方式

組合繼承

又稱偽經(jīng)典繼承扼褪,指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一起粱栖。其背后的思路是使用原型鏈實現(xiàn)對原型方法和屬性的繼承,而通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承幔崖。這樣渣淤,既通過在原型上定義方法實現(xiàn)了函數(shù)的復(fù)用,又能保證每個實例都有自己的方屬性嗅定,看下面的例子:

function SuperType(name){  
    this.name = name;  
    this.colors = ['red','blue','green'];  
}  
  
SuperType.prototype.sayName = function(){  
    console.log(this.name)
};  
  
function SubType(name,age){  
    SuperType.call(this,name); 
    this.age = age; 
}  
//繼承方法
SubType.prototype = new SuperType();   
  
SubType.prototype.sayAge = function(){  
    console.log(this.age); 
}  
  
var instance1 = new SubType('Nichloas', 29);
instance1.colors.push('black');  
console.log(instance1.colors);//red,blue,green,black
instance1.sayName();//Nichloas
instance1.sayAge();//29

var instance2 = new SubType('Grep', 27);
console.log(instance2.colors);//red,blue,green
instance2.sayName();//Grep
instance2.sayAge();//27

推薦文章

這里為大家搜集了一些高質(zhì)量的博文渠退,希望大家能有所收獲

  • 序言:七十年代末碎乃,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子旱眯,更是在濱河造成了極大的恐慌证九,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,002評論 6 519
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件呀页,死亡現(xiàn)場離奇詭異拥坛,居然都是意外死亡猜惋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,357評論 3 400
  • 文/潘曉璐 我一進店門缓窜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谍咆,“玉大人,你說我怎么就攤上這事恩掷」┖浚” “怎么了?”我有些...
    開封第一講書人閱讀 169,787評論 0 365
  • 文/不壞的土叔 我叫張陵逼争,是天一觀的道長氮凝。 經(jīng)常有香客問我望忆,道長,這世上最難降的妖魔是什么稿壁? 我笑而不...
    開封第一講書人閱讀 60,237評論 1 300
  • 正文 為了忘掉前任歉备,我火速辦了婚禮,結(jié)果婚禮上喧笔,老公的妹妹穿的比我還像新娘龟再。我一直安慰自己,他們只是感情好浆劲,可當(dāng)我...
    茶點故事閱讀 69,237評論 6 398
  • 文/花漫 我一把揭開白布牌借。 她就那樣靜靜地躺著割按,像睡著了一般。 火紅的嫁衣襯著肌膚如雪丙躏。 梳的紋絲不亂的頭發(fā)上束凑,一...
    開封第一講書人閱讀 52,821評論 1 314
  • 那天汪诉,我揣著相機與錄音,去河邊找鬼扒寄。 笑死该编,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的嘉赎。 我是一名探鬼主播,決...
    沈念sama閱讀 41,236評論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼拇囊,長吁一口氣:“原來是場噩夢啊……” “哼靶橱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起传黄,我...
    開封第一講書人閱讀 40,196評論 0 277
  • 序言:老撾萬榮一對情侶失蹤尝江,失蹤者是張志新(化名)和其女友劉穎英上,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體惭聂,經(jīng)...
    沈念sama閱讀 46,716評論 1 320
  • 正文 獨居荒郊野嶺守林人離奇死亡相恃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,794評論 3 343
  • 正文 我和宋清朗相戀三年拦耐,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扫俺。...
    茶點故事閱讀 40,928評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡固翰,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出疗琉,到底是詐尸還是另有隱情歉铝,我是刑警寧澤,帶...
    沈念sama閱讀 36,583評論 5 351
  • 正文 年R本政府宣布香浩,位于F島的核電站种吸,受9級特大地震影響呀非,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜猖败,卻給世界環(huán)境...
    茶點故事閱讀 42,264評論 3 336
  • 文/蒙蒙 一降允、第九天 我趴在偏房一處隱蔽的房頂上張望恩闻。 院中可真熱鬧,春花似錦剧董、人聲如沸幢尚。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,755評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽尉剩。三九已至,卻和暖如春毅臊,著一層夾襖步出監(jiān)牢的瞬間理茎,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,869評論 1 274
  • 我被黑心中介騙來泰國打工管嬉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留皂林,地道東北人蚯撩。 一個月前我還...
    沈念sama閱讀 49,378評論 3 379
  • 正文 我出身青樓础倍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親胎挎。 傳聞我的和親對象是個殘疾皇子著隆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,937評論 2 361

推薦閱讀更多精彩內(nèi)容