寫在前面
推薦大家先看一下這篇文章的姊妹篇JavaScript創(chuàng)建對象的四種常見模式
這個抄書筆記在介紹四個模式時挤聘,也或多或少的解釋了一下原型鏈的概念,話不多說鞍陨,開始本篇文章从隆。
原型鏈
基于原型鏈的繼承
JavaScript將原型鏈作為實現(xiàn)繼承的主要方式缭裆±裕基本思想是利用原型讓一個引用類型,繼承另外一個引用類型的屬性和方法盅藻。關(guān)于構(gòu)造函數(shù)畅铭、實例和原型的關(guān)系就不在贅述了,參見下圖(這個圖是目前對三者關(guān)系總結(jié)最棒的圖了~):
那么想一下假残,如果讓原型對象等于另一個類型的實例呢辉懒?結(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
上面代碼演示的是SubType
對SuperType
的繼承〔肝兀基本原理是通過創(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.prototype
是SuperType.prototype
的實例,所以享有這個屬性)躬充。
搜索的經(jīng)歷三個步驟:
1)搜索實例對象Instance
2)搜索SubType.prototype
3)搜索SuperType.prototype
注意此時
instance
的constructor
屬性現(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.prototype
(SuperType
的實例)都將擁有一個引用類型屬性礁鲁,那么每個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ì)量的博文渠退,希望大家能有所收獲