Javascript基礎(chǔ)系列之繼承

繼承

許多 OO 語言都支持兩種繼承方式:接口繼承實現(xiàn)繼承。但是ECMAScript中只支持實現(xiàn)繼承,下面就是實現(xiàn)繼承的方式

借用構(gòu)造函數(shù)

這種技術(shù)的基本思想相當簡單,即在子類型構(gòu)造函數(shù)的內(nèi)部調(diào)用超類型構(gòu)造函數(shù)。函數(shù)只不過是在特定環(huán)境中執(zhí)行代碼的對象,因此通過使用apply()call()方法也可以在(將來)新創(chuàng)建的對象上執(zhí)行構(gòu)造函數(shù)

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" 

這樣可以解決原型中包含引用類型值所帶來問題的屬性共享的問題

參數(shù)傳遞

相對于原型鏈而言,借用構(gòu)造函數(shù)有一個很大的優(yōu)勢,即可以在子類型構(gòu)造函數(shù)中向超類型構(gòu)造函數(shù)傳遞參數(shù)

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

借用構(gòu)造函數(shù)的問題

如果僅僅是借用構(gòu)造函數(shù),那么也將無法避免構(gòu)造函數(shù)模式存在的問題——方法都在構(gòu)造函數(shù)中定義,因此函數(shù)復(fù)用就無從談起了

組合繼承

組合繼承,有時候也叫做偽經(jīng)典繼承,指的是將原型鏈和借用構(gòu)造函數(shù)的技術(shù)組合到一塊,從而發(fā)揮二者之長的一種繼承模式。其背后的思路是使用原型鏈實現(xiàn)對原型屬性和方法的繼承,而通過借用構(gòu)造函數(shù)來實現(xiàn)對實例屬性的繼承。這樣,既通過在原型上定義方法實現(xiàn)了函數(shù)復(fù)用,又能夠保證每個實例都有它自己的屬性

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

組合繼承避免了原型鏈和借用構(gòu)造函數(shù)的缺陷,融合了它們的優(yōu)點,成為JavaScript中最常用的繼承模式。而且,instanceofisPrototypeOf()也能夠用于識別基于組合繼承創(chuàng)建的對象

原型式繼承

借助原型可以基于已有的對象創(chuàng)建新對象,同時還不必因此創(chuàng)建自定義類型。

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

在 object()函數(shù)內(nèi)部,先創(chuàng)建了一個臨時性的構(gòu)造函數(shù),然后將傳入的對象作為這個構(gòu)造函數(shù)的原型,最后返回了這個臨時類型的一個新實例。從本質(zhì)上講,object()對傳入其中的對象執(zhí)行了一次淺復(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"

這種實現(xiàn)你必須有一個對象可以作為另一個對象的基礎(chǔ)。如果有這么一個對象的話,可以把它傳遞給 object()函數(shù),然后再根據(jù)具體需求對得到的對象加以修改即可。在這個例子中,可以作為另一個對象基礎(chǔ)的是 person 對象,于是我們把它傳入到 object()函數(shù)中,然后該函數(shù)就會返回一個新對象。這個新對象將person作為原型,所以它的原型中就包含一個基本類型值屬性和一個引用類型值屬性.這意味著 person.friends 不僅屬于 person 所有,而且也會被anotherPerson以及yetAnotherPerson共享。實際上,這就相當于又創(chuàng)建了 person 對象的兩個副本

ECMAScript 5 通過新增 Object.create()方法規(guī)范化了原型式繼承。這個方法接收兩個參數(shù):一個用作新對象原型的對象和(可選的)一個為新對象定義額外屬性的對象。在傳入一個參數(shù)的情況下,Object.create()與 object()方法的行為相同

在沒有必要興師動眾地創(chuàng)建構(gòu)造函數(shù),而只想讓一個對象與另一個對象保持類似的情況下,原型式繼承是完全可以勝任的。不過別忘了,包含引用類型值的屬性始終都會共享相應(yīng)的值,就像使用原型模式一樣

寄生式繼承

寄生式繼承的思路與寄生構(gòu)造函數(shù)和工廠模式類似,即創(chuàng)建一個僅用于封裝繼承過程的函數(shù),該函數(shù)在內(nèi)部以某種方式來增強對象,最后再像真地是它做了所有工作一樣返回對象

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

在這個例子中,createAnother()函數(shù)接收了一個參數(shù),也就是將要作為新對象基礎(chǔ)的對象。然后,把這個對象(original)傳遞給 object()函數(shù),將返回的結(jié)果賦值給clone。再為 clone 對象添加一個新方法 sayHi(),最后返回 clone 對象

var person = {
    name: "Nicholas",
    friends: ["Shelby", "Court", "Van"]
};
var anotherPerson = createAnother(person);
anotherPerson.sayHi(); //"hi" 

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

使用寄生式繼承來為對象添加函數(shù),會由于不能做到函數(shù)復(fù)用而降低效率;這一
點與構(gòu)造函數(shù)模式類似

寄生組合式繼承

組合繼承最大的問題就是無論什么情況下,都會調(diào)用兩次超類型構(gòu)造函數(shù):一次是在創(chuàng)建子類型原型的時候,另一次是在子類型構(gòu)造函數(shù)內(nèi)部,子類型最終會包含超類型對象的全部實例屬性,但我們不得不在調(diào)用子類型構(gòu)造函數(shù)時重寫這些屬性

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); //第二次調(diào)用 SuperType()
    this.age = age;
}
SubType.prototype = new SuperType(); //第一次調(diào)用 SuperType()
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function(){
    alert(this.age);
}; 

[圖片上傳失敗...(image-6385c9-1533106102279)]

所謂寄生組合式繼承,即通過借用構(gòu)造函數(shù)來繼承屬性,通過原型鏈的混成形式來繼承方法。其背后的基本思路是:不必為了指定子類型的原型而調(diào)用超類型的構(gòu)造函數(shù),我們所需要的無非就是超類型原型的一個副本而已。本質(zhì)上,就是使用寄生式繼承來繼承超類型的原型,然后再將結(jié)果指定給子類型的原型

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

function inheritPrototype(subType, superType){
    var 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)建超類型原型的一個副本。第二步是為創(chuàng)建的副本添加constructor屬性,從而彌補因重寫原型而失去的默認的constructor屬性。最后一步,將新創(chuàng)建的對象(即副本)賦值給子類型的原型

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

這個例子的高效率體現(xiàn)在它只調(diào)用了一次SuperType構(gòu)造函數(shù),并且因此避免了在SubType.
prototype 上面創(chuàng)建不必要的、多余的屬性。與此同時,原型鏈還能保持不變;因此,還能夠正常使用instanceof 和 isPrototypeOf()

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • ??面向?qū)ο螅∣bject-Oriented,OO)的語言有一個標志,那就是它們都有類的概念,而通過類可以創(chuàng)建任意...
    霜天曉閱讀 2,256評論 0 6
  • 第3章 基本概念 3.1 語法 3.2 關(guān)鍵字和保留字 3.3 變量 3.4 數(shù)據(jù)類型 5種簡單數(shù)據(jù)類型:Unde...
    RickCole閱讀 5,513評論 0 21
  • 繼承是 OO 語言中的一個最為人津津樂道的概念。許多 OO 語言都支持兩種繼承方式:接口繼承 和 實現(xiàn)繼承。接口繼...
    threetowns閱讀 491評論 0 0
  • 博客內(nèi)容:什么是面向?qū)ο鬄槭裁匆嫦驅(qū)ο竺嫦驅(qū)ο缶幊痰奶匦院驮瓌t理解對象屬性創(chuàng)建對象繼承 什么是面向?qū)ο?面向?qū)ο?..
    _Dot912閱讀 1,536評論 3 12
  • 今天下午第一節(jié)課我同學(xué)都去了多功能廳去聽講座,主題是:關(guān)注食品安全,共創(chuàng)健康美好家園。我們看了辣條是怎樣制作的,我...
    清新浪閱讀 252評論 0 0

友情鏈接更多精彩內(nèi)容