原型鏈
ECMAScript中將原型鏈作為實現(xiàn)繼承的主要方法。其基本思想是利用原型讓一個引用類型繼承另一個引用類型的屬性和方法兄一。
- 基本模式
function SuperType() {
this.property = true;
this.colors = ["a", "b"];
}
SuperType.prototype.getSuperValue = function(){
console.log(this.property);
};
function SubType() {
this.subProperty = false;
}
SubType.prototype = new SuperType();
// SubType.prototype = Object.create(SuperType.prototype);
SubType.prototype.getSubValue = function(){
console.log(this.subProperty);
};
var instance = new SubType();
instance.getSuperValue();
結(jié)果:
上面的例子中,instance指向SubType的原型费薄,SubType的原型又指向SuperType的原型楞抡。getSuperValue()方法仍然還在SuperType.prototype中析藕,但property則位于SubType.prototype中账胧。這是因為prototype是一個實例屬性治泥,而getSuperValue()則是一個原型方法居夹。
注意:原型鏈雖然很強大,可以實現(xiàn)繼承檬洞,但存在兩個主要的問題添怔。
(1)包含引用類型值的原型屬性會被所有實例共享广料,這會導(dǎo)致對一個實例的修改會影響另一個實例性昭。
(2)在創(chuàng)建子類型的實例時糜颠,不能向超類型的構(gòu)造函數(shù)中傳遞參數(shù)其兴。由于這兩個問題的存在元旬,實踐中很少單獨使用原型鏈守问。
function SuperType() {
this.property = true;
this.colors = ["a", "b"];
}
SuperType.prototype.getSuperValue = function(){
console.log(this.property);
};
function SubType() {
this.subProperty = false;
}
SubType.prototype = new SuperType();
// SubType.prototype = Object.create(SuperType.prototype);
SubType.prototype.getSubValue = function(){
console.log(this.subProperty);
};
var instance1 = new SubType();
instance1.colors.push("c");
console.log(instance1.colors);
var instance2 = new SubType();
console.log(instance2.colors);
結(jié)果:
會發(fā)現(xiàn)引用類型值的原型屬性會被所有實例共享穆端。
- 借用構(gòu)造函數(shù)
在解決原型中包含引用類型值所帶來的問題中体啰,使用借用構(gòu)造函數(shù)技術(shù)來解決荒勇。借用構(gòu)造函數(shù)的基本思想沽翔,即在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)仅偎。函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對象哨颂,因此通過使用apply()和call()方法可以在新創(chuàng)建的對象上執(zhí)行構(gòu)造函數(shù)威恼。如下例子
function SuperType() {
this.colors = ["a", "b"];
}
function SubType() {
SuperType.call(this);
this.subProperty = false;
}
var instance1 = new SubType();
instance1.colors.push("c");
console.log(instance1.colors);
var instance2 = new SubType();
console.log(instance2.colors);
結(jié)果:
上面例子中,通過使用call()方法(或者apply()方法)斤蔓,在新創(chuàng)建的SubType實例的環(huán)境下調(diào)用了SuperType構(gòu)造函數(shù)弦牡。這樣一來就會在新的SubType對象上執(zhí)行SuperType()函數(shù)中定義的所有對象初始化代碼。結(jié)果椭豫,SubType的每個實例都會有自己的colors屬性副本赏酥。
相對于原型鏈而言裸扶,借用構(gòu)造函數(shù)可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)姓言。如下例子
function SuperType(name) {
this.name = name;
}
function SubType() {
SuperType.call(this, "Lee");
this.age = 28;
}
var instance1 = new SubType();
console.log(instance1.name);
console.log(instance1.age);
結(jié)果:
借用構(gòu)造函數(shù)存在兩個問題:
(1)無法避免構(gòu)造函數(shù)模式存在的問題,方法都在構(gòu)造函數(shù)中定義餐塘,因此無法復(fù)用函數(shù)税手。
(2)在超類型的原型中定義的方法芦倒,對子類型而言是不可見的兵扬。因此這種技術(shù)很少單獨使用器钟。
- 組合繼承
組合繼承,指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一起昙啄。思路是使用原型鏈實現(xiàn)對原型方法的繼承跟衅,而通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承。這樣叭莫,既通過在原型上定義方法實現(xiàn)了函數(shù)的復(fù)用,又能夠保證每個實例都有它自己的屬性靖诗。以下例子充分說明了這一點
function SuperType(name) {
this.name = name;
this.colors = ["a", "b"];
}
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.constructor = SubType;
SubType.prototype.sayAge = function(){
console.log(this.age);
};
var instance1 = new SubType("Lee", 28);
instance1.colors.push("c");
console.log(instance1.colors);
instance1.sayName();
instance1.sayAge();
var instance2 = new SubType("Hui", 27);
console.log(instance2.colors);
instance2.sayName();
instance2.sayAge();
結(jié)果:
這個例子中颂鸿,兩個實例既分別擁有自己的屬性,包括colors屬性,又可以使用相同的方法稳懒。
組合繼承避免了原型鏈和借用構(gòu)造函數(shù)的缺點僚祷,融合了他們的優(yōu)點俺榆,是JavaScript中最常用的繼承模式定嗓。
缺點:實際上子類上會擁有超類的兩份屬性,只是子類的屬性覆蓋了超類的屬性
- 原型式繼承
采用原型式繼承并不需要定義一個類恃逻,傳入?yún)?shù)obj,生成一個繼承obj對象的對象
function objectCreate(obj){
function F(){}
F.prototype = obj
return new F()
}
直接通過對象生成一個繼承該對象的對象,但是不是類式繼承,而是原型式基礎(chǔ),缺少了類的概念找田。
- 寄生式繼承
創(chuàng)建一個僅僅用于封裝繼承過程的函數(shù),然后在內(nèi)部以某種方式增強對象植袍,最后返回對象
function objectCreate(obj){
function F(){}
F.prototype = obj
return new F()
}
function createSubObj(superInstance){
var clone = objectCreate(superInstance)
clone.property = 'Sub Property'
return clone
}
原型式繼承的一種拓展,但是依舊沒有類的概念。
- 寄生組合式繼承
結(jié)合寄生式繼承和組合式繼承,完美實現(xiàn)不帶兩份超類屬性的繼承方式
function inheritPrototype(Super,Sub){
var superProtoClone = Object.Create(Super.prototype)
superProtoClone.constructor = Sub
Sub.prototype = Super
}
function Sub(){
Super.call()
Sub.property = 'Sub Property'
}
inheritPrototype(Super,Sub)
完美實現(xiàn)繼承,解決了組合式繼承帶兩份屬性的問題腕窥,過于繁瑣癞松,故不如組合繼承。