JS繼承原理剖析

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()

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章