JS繼承及原型鏈

一、原型鏈

實現方法:讓一個原型對象等於另一個類型的實例

function SuperType(){
    this.prototype=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

上面的例子中,調用instance.getSuperValue()會經歷三個搜索步驟:
1.搜索實例
2.搜索SubType.prototype
3.搜索SuperType.prototype

注意:
在通過原型鏈實現繼承時,不能使用對象字面量創建原型方法,因爲這樣做會重寫原型鏈。

原型鏈的問題:
1.包含引用類型值的原型屬性會被所有實例共享
2.在創建子類型的實例時,不能向超類型的構造函數中傳遞參數

二、借用構造函數

這種技術的基本思想是:在子類型構造函數的內部調用超類型構造函數

function SuperType(){
    this.colors=["red","blue","green"];
}
function SubType(){
    //繼承了SuperType
    SuperType.call(this);
}
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"

以上代碼在(未來將要)新創建的SubType實例的環境下調用了SuperType構造函數。這樣一來,就會在新SubType對象上執行SuperType()函數中定義的所有對象初始化代碼。結果,SubType的每個實例就會具有自己的colors屬性的副本了。

借用構造函數還可以傳遞參數:

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

function SubType(){
    //繼承了SuperType,同時還傳遞了參數
    SuperType.call(this,"Nicholas");
    //實例屬性
    this.age=29;
}
var instance =new SubType();
alert(instance.name);   //"Nicholas"
alert(this.age);    //29

借用構造函數中的問題:
1.如果僅僅借用構造函數,那麼將無法避免構造函數模式存在的問題——方法都在構造函數中定義,函數複用無從談起
2.在超類型的原型中定義的方法,對子類型而言是不可見的,結果所有類型都只能使用構造函數模式。所以這種方法很少單獨使用。

三、組合繼承

思路是使用原型鏈實現對原型屬性和方法的繼承,而通過借用構造函數來實現對實例屬性的繼承

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;
}

//繼承方法
SubType.prototype=new SuperType();
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

組合繼承避免了原型鏈和借用構造函數的缺陷,融合了它們的優點,成爲javascript中最常用的繼承模式。而且,instanceof和isPrototypeof()也能夠用於識別基於組合繼承創建的對象。

四、原型式繼承

藉助原型可以基於已有的對象創建新對象,同時還不必因此創建自定義類型:

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

ECMAScript 5 通過新增Object.create()方法規範化了原型式繼承。這個方法接受兩個參數:一個用作新對象原型的對象和(可選的)一個爲新對象定義額外屬性的對象。

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

var anotherPerson=Object.create(person);
anotherPerson.name="Greg";
anotherPerson.friends.push("Rob");

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

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

Object.create()方法的第二個參數與Object.defineProperties()方法的第二個參數格式相同:每個屬性都是通過自己的描述符定義的。以這種方式指定的任何屬性都會覆蓋原型對象上的同名屬性。例如:

var person={
    name:"Nicholas",
    friends:["Shelby","Court","Van"]
};
var anotherPerson=Object.create(person,{
        name:{
            value:"Greg"
        }
    });

alert(anotherPerson.name);  //"Greg"

缺點:
包含引用類型值的屬性始終都會共享相應的值,就像使用原型模式一樣。

五、寄生式繼承

寄生式繼承的思路與寄生構造函數和工廠模式類似,即創建一個僅用於封裝繼承過程的函數,該函數在內部以某種方式來增強對象,最後再像真的是它做了所有工作一樣返回對象。以下代碼示範了寄生式繼承模式。

function createAnother(original){
    var clone=object(original);   //通過調用函數創建一個新函數
    clone.sayHi=function(){     //以某種方式來增強這個對象
        alert("hi");
    }
    return clone;
}

可以像下面這樣來使用createAnother()函數:

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

var antherPerson=createAnother(person);
antherPerson.sayHi; //"hi"

優點:
在主要考慮對象而不是自定義類型和構造函數的情況下,寄生式繼承也是一種有用的模式。前面示範繼承模式時使用的object()函數不是必須的;任何能夠返回新對象的函數都適用於此模式。

缺點:
使用寄生式繼承來爲對象添加函數,由於不能做到函數複用而降低效率;這一點與構造函數模式類似。

六、寄生組合式繼承

寄生組合式繼承的基本模式如下:

function inheritPrototype(subType,superType){
    var prototype=object(superType.prototype); //創建對象
    prototype.constructor=subType; //增強對象
    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);
};

開發人員普遍認爲寄生式組合繼承是引用類型最理想的繼承範式。

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