JavaScript之對象繼承

原型鏈繼承

function SuperType(){
    this.property = true;
}

SuperType.prototype.getSuperValue = function(){
    return this.property;
};

function SubType(){
    this.subproperty = false;
}

// 繼承了 SuperType
SubType.prototype = new SuperType();

var subType = new SubType();
console.log(subType.getSuperValue());   // 繼承了 SuperType 的 getSuperValue 方法,打印 true

缺點

  1. 若是 SuperType 存在一個引用類型的屬性,而 SubType 的原型對象變爲 SuperType 的一個實例,這樣每一個 SubType 的實例都會共用這個引用類型的屬性,不一樣的 SubType 實例對該屬性的操做都將會在其它 SubType 實例中體現出來,這跟每一個實例擁有本身的屬性是違背的。
function SuperType(){
    this.colors = ["red", "blue", "green"];
}

function SubType(){            
}

// 繼承了 SuperType
SubType.prototype = new SuperType();

var subType1 = new SubType();
subType1.colors.push("black"); // subType1 修改 colors
console.log(subType1.colors);    // "red,blue,green,black"

var subType2 = new SubType();
console.log(subType2.colors);    // "red,blue,green,black",subType2 的 colors 值爲 subType1 修改以後的值
  1. 在建立子類型(SubType)的實例時,不能向超類型(SuperType)的構造函數中傳遞參數。實際上,應該說是沒有辦法在不影響全部對象實例(緣由如缺點1)的狀況下,給超類型的構造函數傳遞參數。

借用構造函數繼承

function SuperType(){
    this.colors = ["red", "blue", "green"];
}

function SubType(){  
    // 繼承了 SuperType
    SuperType.call(this); // 經過 apply() 或 call() 執行 SuperType 的構造函數
}

var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors);    // "red,blue,green,black"

var instance2 = new SubType();
alert(instance2.colors);    // "red,blue,green"

該方法解決了 原型鏈繼承 的引用型屬性共享的問題。javascript

還有能夠在子類型構造函數中向超類型構造函數傳遞參數,以下例子:java

function SuperType(name){
    this.name = name;
}

function SubType(){  
    // 繼承 SuperType,並傳遞參數
    SuperType.call(this, "Nicholas");
    
    // 實例屬性
    this.age = 29;
}

var subType = new SubType();
console.log(subType.name);   //"Nicholas";
console.log(subType.age);    //29

缺點

  1. 方法都在構造函數中定義,所以函數複用就無從談起了。並且,在超類型的原型中定義的方法,對子類型而言也是不可見的,結果全部類型都只能使用構造函數模式。

原型鏈/構造函數組合繼承

function SuperType(name){
    this.name = name;
    this.colors = ["red", "blue", "green"];
}

SuperType.prototype.sayName = function(){
    console.log(this.name);
};

function SubType(name, age){
    // 執行 SuperType 的構造函數,繼承 SuperType 的屬性
    SuperType.call(this, name);
    
    this.age = age;
}

// 將 SuperType 的實例賦給 SubType 的原型對象,這樣 SubType 可繼承 SuperType 原型中的方法
SubType.prototype = new SuperType();

SubType.prototype.sayAge = function(){
    console.log(this.age);
};

var  subType1 = new SubType("Nicholas", 29);
subType1.colors.push("black");
console.log(subType1.colors);  // "red,blue,green,black"
subType1.sayName();      // "Nicholas";
subType1.sayAge();       // 29


var subType2 = new SubType("Greg", 27);
console.log(subType2.colors);  // "red,blue,green"
subType2.sayName();      // "Greg";
subType2.sayAge();       // 27

缺點

  1. 不管什麼狀況,都會調用兩次超類型構造函數,一次是在建立子類型原型的時候,一次是在執行子類型構造函數的時候。這樣一個 SubType 實例會包含兩組 SuperType 的屬性,一組在 SubType 實例上,一組在 SubType 原型中。

原型式繼承

該方法基於已有的對象建立新對象,同時還沒必要所以建立自定義類型。數組

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = Object.create(person); // 使用 person 做爲新對象(anotherPerson)的原型
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");

var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");

console.log(person.friends);   // "Shelby,Court,Van,Rob,Barbie"

寄生式繼承

該方法建立一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來加強對象,最後再像真地是它作了全部工做同樣返回對象。app

function creatAnother(original) {
    var clone = Object.create(original); // 建立一個新對象
    clone.sayHi = function() { // 以某種方式來加強這個對象
        console.log("hi");
    }
    return clone; // 返回該對象
}

// 使用示例
var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};

var anotherPerson = creatAnother(person);
anotherPerson.sayHi(); // "hi"

寄生/原型鏈/構造函數組合繼承

該方法解決原型鏈/構造函數組合繼承調用兩次超類型構造函數的問題。函數

經過借用構造函數來繼承屬性,經過原型鏈的混成形式來繼承方法。其背後的基本思路是:沒必要爲了指定子類型的原型而調用超類型的構造函數,咱們所須要的無非就是超類型原型的一個副本而已。本質上,就是使用寄生式繼承來繼承超類型的原型,而後再將結果指定給子類型的原型。this

function object(o){
    function F(){}
    F.prototype = o;
    return new F();
}

function inheritPrototype(subType, superType){
    var prototype = object(superType.prototype);   // 根據超類型原型建立新對象
    prototype.constructor = subType;               // 將新對象的 constructor 設置爲子類型
    subType.prototype = prototype;                 // 將新對象賦給子類型的原型
}
                        
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;
}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge = function(){
    console.log(this.age);
};

var subType1 = new SubType("Nicholas", 29);
subType1.colors.push("black");
console.log(subType1.colors);  // "red,blue,green,black"
subType1.sayName();      // "Nicholas";
subType1.sayAge();       // 29


var subType2 = new SubType("Greg", 27);
alert(subType2.colors);  // "red,blue,green"
subType2.sayName();      // "Greg";
subType2.sayAge();       // 27

參考資料:《JavaScript高級程序設計(第3版)》第6.3節 繼承prototype

相關文章
相關標籤/搜索