六大繼承方案總結(jié)(繼承系列-3)

目錄:
原型鏈繼承
問題:
借用構(gòu)造函數(shù)(不算標(biāo)準(zhǔn)的繼承模式)
組合繼承(偽經(jīng)典繼承)
核心思想:
優(yōu)點
問題:(寄生組合式繼承就是為了解決這一問題)
原型式繼承
實現(xiàn)原理:
Object.create()
優(yōu)點
缺點
寄生式繼承
原理
缺點
寄生組合式繼承
實現(xiàn)思路
核心邏輯
優(yōu)點
圣杯模式
原型鏈繼承
function?SuperType()?{
?this.property?=?true;
}
SuperType.prototype.getSuperValue?=?function?()?{
?return?this.property;
};
function?SubType()?{
?this.subproperty?=?false;
}
//繼承了?SuperType?
SubType.prototype?=?new?SuperType();
SubType.prototype.getSubValue?=?function?()?{
?return?this.subproperty;
};
var?instance?=?new?SubType();
alert(instance.getSuperValue());?//true
問題:
最主要的問題來自包含引用類型值的原型。我們前面介紹過包含引用類型值的原型屬性會被所有實例共享;而這也正是為什么要在構(gòu)造函數(shù)中,而不是在原型對象中定義屬性的原因。在通過原型來實現(xiàn)繼承時,原型實際上會變成另一個類型的實例。于是,原先的實例屬性也就順理成章地變成了現(xiàn)在的原型屬性了。
借用構(gòu)造函數(shù)(不算標(biāo)準(zhǔn)的繼承模式)
又叫“對象偽裝”或“經(jīng)典繼承”
function?SuperType(name)?{
?this.name?=?name;
?this.colors?=?["red",?"blue",?"green"];
}
function?SubType()?{
?//?繼承了?SuperType?
?SuperType.call(this,'同時傳遞參數(shù)');
}
var?instance1?=?new?SubType();
instance1.colors.push("black");
console.log(instance1.colors);?//?"red,blue,green,black"?
var?instance2?=?new?SubType();
console.log(instance2.colors);?//?"red,blue,green"
console.log(instance2.name);?//?'同時傳遞參數(shù)'
解決了“引用類型值的原型屬性會被所有實例共享”的問題。
組合繼承(偽經(jīng)典繼承)
綜合了原型鏈和盜用構(gòu)造函數(shù),將兩者的優(yōu)點集中了起來。
核心思想:
使用借用構(gòu)造函數(shù)的技術(shù)實現(xiàn)“實例屬性”和方法的繼承,使用原型鏈實現(xiàn)“原型屬性”和方法的繼承。
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);?//?(第一次調(diào)用構(gòu)造函數(shù))利用“借用構(gòu)造函數(shù)”的思想,繼承了SuperType實例的name,colors屬性
??
??//?自定義自己的實例屬性
?this.age?=?age;
}
SubType.prototype?=?new?SuperType();?//?(第二次調(diào)用構(gòu)造函數(shù))利用“構(gòu)造原型鏈”的思想,繼承原型屬性和方法
SubType.prototype.constructor?=?SubType;
SubType.prototype.sayAge?=?function?()?{
?console.log(this.age);
};
var?instance1?=?new?SubType("Nicholas",?29);
instance1.colors.push("black");
console.log(instance1.colors);?//"red,blue,green,black"
instance1.sayName();?//"Nicholas";
instance1.sayAge();?//29
var?instance2?=?new?SubType("Greg",?27);
console.log(instance2.colors);?//"red,blue,green"
instance2.sayName();?//"Greg";
instance2.sayAge();?//27
優(yōu)點
?組合繼承彌補了原型鏈和盜用構(gòu)造函數(shù)的不足,是 JavaScript 中使用最多的繼承模式。而且組合繼 承也保留了 instanceof 操作符和 isPrototypeOf()方法識別合成對象的能力。
?
問題:(寄生組合式繼承就是為了解決這一問題)
但依舊擁有原型繼承和借用構(gòu)造函數(shù)的缺點:
組合繼承最大的問題就是效率問題,因為無論在什么情況下,都會調(diào)用兩次構(gòu)造函數(shù): 一次是在創(chuàng)建子類型原型時, 另一次是在子類型構(gòu)造函數(shù)內(nèi)部。 第二個問題是會繼承多余的屬性。
上例中,使用SubType.prototype = new SuperType();,SubType將會繼承SuperType實例對象的name、colors屬性,雖然name沒有被創(chuàng)建。因此造成了一個問題,SubType.prototype上會存在不必要的,多余的屬性。如name,colors屬性同時存在于SubType.prototype和SubType中。
原型式繼承
實現(xiàn)原理:
創(chuàng)建一個臨時構(gòu)造函數(shù),將傳入的對象賦值給這個構(gòu)造函數(shù)的原型,然后返回這個臨時類型的一個實例。
function?object(o)?{
??function?F()?{}
??F.prototype?=?o;
??return?new?F();
}
使用
let?person?=?{
??name:?"Nicholas",
??friends:?["Shelby",?"Court",?"Van"]
};
let?anotherPerson?=?object(person);
anotherPerson.name?=?"Greg";
anotherPerson.friends.push("Rob");
let?yetAnotherPerson?=?object(person);
yetAnotherPerson.name?=?"Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends);?//?"Shelby,Court,Van,Rob,Barbie"
Object.create()
規(guī)范化了原型式繼承的概念,用于實現(xiàn)原型式繼承
let?person?=?{
??name:?"Nicholas",
??friends:?["Shelby",?"Court",?"Van"]
};
let?anotherPerson?=?Object.create(person);
anotherPerson.name?=?"Greg";
anotherPerson.friends.push("Rob");
let?yetAnotherPerson?=?Object.create(person);
yetAnotherPerson.name?=?"Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends);?//?"Shelby,Court,Van,Rob,Barbie"
優(yōu)點
不需要單獨創(chuàng)建構(gòu)造函數(shù)
缺點
屬性中包含的引用值始終會在相關(guān)對象間共享
寄生式繼承
與原型式繼承比較接近,相當(dāng)于把原型式繼承又包裝了一遍。
原理
思路類似于寄生構(gòu)造函數(shù)和工廠模式:創(chuàng)建一個實現(xiàn)繼承的函數(shù),以某種方式增強對象,然后返回這個對象。
基本的寄生繼承模式如下:
function?createAnother(original)?{
??let?clone?=?object(original);?//?通過調(diào)用函數(shù)創(chuàng)建一個新對象?
??clone.sayHi?=?function?()?{?//?以某種方式增強這個對象
????console.log("hi");
??};
??return?clone;?//?返回這個對象?
}
適合主要關(guān)注對象,而不在乎類型和構(gòu)造函數(shù)的場景
缺點
通過寄生式繼承給對象添加函數(shù)會導(dǎo)致函數(shù)難以重用(比如上述代碼的sayHi)
寄生組合式繼承
實現(xiàn)思路
寄生式組合繼承通過盜用構(gòu)造函數(shù)繼承屬性,但使用混合式原型鏈繼承方法。
基本思路是不通過調(diào)用父類構(gòu)造函數(shù)給子類原型賦值,而是取得父類原型的一個副本。說到底就是使用寄生式繼承來繼承父類原型,然后將返回的新對象賦值給子類原型
概括:
通過借用構(gòu)造函數(shù)來繼承屬性; 通過原型鏈來繼承方法(圣杯式繼承)。
核心邏輯
function?object(o)?{
??function?F()?{}
??F.prototype?=?o;
??return?new?F();
}
function?inheritPrototype(subType,?superType)?{
??let?prototype?=?object(superType.prototype);?//?創(chuàng)建對象?
??prototype.constructor?=?subType;?//?增強對象?
??subType.prototype?=?prototype;?//?賦值對象
}
這個函數(shù)接收兩個參數(shù): 子類構(gòu)造函數(shù)和父類構(gòu)造函數(shù)。
函數(shù)內(nèi)部,第一步是創(chuàng)建父類原型的一個副本。
給返回的 prototype 對象設(shè)置 constructor 屬性,為了解決由于重寫原型導(dǎo)致默認(rèn) constructor 丟失的問題。
將新創(chuàng)建的對象賦值給子類型的原型。
優(yōu)點
只調(diào)用了一次構(gòu)造函數(shù),避免了子類原型上不必要也用不到的屬性,因此可以說這個例子的效率更高。
而且,原型鏈仍然保持不變,因此 instanceof 操作符和 isPrototypeOf()方法正常有效。
寄生式組合繼承可以算是引用類型繼承的最佳模式。
圣杯模式
最后發(fā)現(xiàn),這不就是圣杯模式的寫法么!
function?inherit(Target,Origin){
?function?F(){};
?F.prototype?=?Origin.prototype;
?Target.prototype?=?new?F();
?Target.prototype.constuctor?=?Target;
?target.prototype.uber?=?Origin.prototype;
}
用圣杯式原型鏈繼承方法替代原來的new 構(gòu)造原型鏈的方法,這樣兒子身上就沒有父親身上的實例化屬性了!good!

讓我們一起攜手同走前端路!
關(guān)注公眾號回復(fù)【加群】即可

●?三欄響應(yīng)式布局(左右固寬中間自適應(yīng))的5種方法
