1、原型鏈繼承
構造函數、原型和實例之間的關係:每個構造函數都有一個原型對象,原型對象都包含一個指向構造函數的指針,而實例都包含一個原型對象的指針。
繼承的本質就是複製,即重寫原型對象,代之以一個新類型的實例。
function SuperType() {
this.property = true;
}
SuperType.prototype.getSuperValue = function() {
return this.property;
}
function SubType() {
this.subproperty = false;
}
// 這裏是關鍵,創建SuperType的實例,並將該實例賦值給SubType.prototype
SubType.prototype = new SuperType();
SubType.prototype.getSubValue = function() {
return this.subproperty;
}
var instance = new SubType();
console.log(instance.getSuperValue()); // true
原型鏈方案存在的缺點:多個實例對引用類型的操作會被篡改。
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function SubType(){}
SubType.prototype = new SuperType();
//SubType.prototype ->實例對象的賦值,SuperType->構造函數,new SuperType()._proto_ = SuperType.prototype
var Subinstance = new SubType();
Subinstance.colors.push("black");
console(Subinstance.colors); //"red,blue,green,black"
var Sub2instance = new SubType();
console.log(Sub2instance.colors); //"red,blue,green,black"
2、借用構造函數繼承
使用父類的構造函數來增強子類實例,等同於複製父類的實例給子類(不使用原型)
function SuperType(){
this.color=["red","green","blue"];
}
function SubType(){
//繼承自SuperType
SuperType.call(this);
}
var instance1 = new SubType();
instance1.color.push("black");
alert(instance1.color);//"red,green,blue,black"
var instance2 = new SubType();
alert(instance2.color);//"red,green,blue"
//缺點:只能繼承父類的實例屬性和方法,不能繼承原型屬性/方法;無法實現複用,每個子類都有父類實例函數的副本,影響性能
3、組合繼承
組合上述兩種方法就是組合繼承。用原型鏈實現對原型屬性和方法的繼承,用借用構造函數技術來實現實例屬性的繼承。
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
function SubType(name, age){
// 繼承屬性
// 第二次調用SuperType()
SuperType.call(this, name);
this.age = age;
}
// 繼承方法, 構建原型鏈, 第一次調用SuperType()
SubType.prototype = new SuperType();
// 重寫SubType.prototype的constructor屬性,指向自己的構造函數SubType
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
alert(this.age);
};
var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
4、原型式繼承
利用一個空對象作爲中介,將某個對象直接賦值給空對象構造函數的原型。
function object(obj){
funciton F() {}
F.prototype = obj;
return new F();
}
object()對傳入其中的對象執行了一次淺複製,將構造函數F的原型直接指向傳入的對象
var person = {
name: "Nicholas",
friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
缺點:
原型鏈繼承多個實例的引用類型屬性指向相同,存在篡改的可能。
無法傳遞參數
另外,ES5中存在Object.create()的方法,能夠代替上面的object方法。
5、寄生組合式繼承
結合借用構造函數傳遞參數和寄生模式實現繼承
function createAnother(original){
var clone = object(original); // 通過調用 object() 函數創建一個新對象
clone.add = function(){
console.log('增強對象')
};
return clone;
}
function inheritPrototype(subType, superType){
var prototype = Object.create(superType.prototype); // 創建對象,創建父類原型的一個副本
prototype.constructor = subType; // 增強對象,彌補因重寫原型而失去的默認的constructor 屬性
subType.prototype = prototype; // 指定對象,將新創建的對象賦值給子類的原型
}
// 父類初始化實例屬性和原型屬性
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
alert(this.name);
};
// 借用構造函數傳遞增強子類實例屬性(支持傳參和避免篡改)
function SubType(name, age){
SuperType.call(this, name);
this.age = age;
}
// 將父類原型指向子類
inheritPrototype(SubType, SuperType);
// 新增子類原型屬性
SubType.prototype.sayAge = function(){
alert(this.age);
}
var instance1 = new SubType("xyc", 23);
var instance2 = new SubType("lxy", 23);
instance1.colors.push("2"); // ["red", "blue", "green", "2"]
instance1.colors.push("3"); // ["red", "blue", "green", "3"]
這個例子的高效率體現在它只調用了一次SuperType 構造函數,並且因此避免了在SubType.prototype 上創建不必要的、多餘的屬性。於此同時,原型鏈還能保持不變;因此,還能夠正常使用instanceof 和isPrototypeOf()