Javascript繼承深入詳解

一、原型鏈繼承方式

1、原形鏈繼承做法

示例:

function SuperType(){

this.property=true;

}

Super.prototype.getSuperValue=function(){

return this.property;

}

function SubType(){

this.subProperty=false;

}

SubType.prototype=new SuperType();

SubType.prototype.getSubValue=function(){

return this.subProperty;

}

var s=new SubType();

console.log(s. getSuperValue());

2、優勢

原形鏈的優勢來自於共享,如getSuperValue,無論new多少個SubType,都只共享同一個getSuperValue

3、缺點

只能實現單一繼承。

例如SubType.prototype=new SuperType1();如果還想讓SubType.prototype再繼承SuperType2,那麼SubType.prototype=new SuperType2 ();結果是SubType將不再繼承SuperType1而只繼承SuperType2

沒有很好地解決屬性共享問題

這是原形鏈最大的問題。例如:

function SuperType(){

this.names={「Leo」,」Linda」};

}

SuperType.prototype.getName=function(){return this.name};

function SubType(){}

SubType.prototype=new SuperType();//此時SubType.prototype會具有name屬性,

//這會引起屬性共享問題

var s1=new SubType();

var s2=new SubType();

s2.names.push(「cici」);//此時s1的names也改變了

沒有很好地解決類型識別問題

function SuperType(){}

function SubType(){}

SubType.prototype=new SuperType();

var instance=new SubType();

此時instance的constructor指向的是SuperType,而不是SubType。

4、點評

原形鏈繼承,成也共享敗也共享

二、借用構造函數繼承

通過call跟apply借用超類的構造函數,原理跟使用構造函數創建對象一樣。

1、借用構造函數繼承做法

示例:

function SuperType(name){

this.name=name;

this.sayName=function(){alert(name);}

}

SuperType.prototype.sayHi=function(){alert(「Hi」);}

function SubType(){

SuperType.call(this,」Leo」);

this.age=18;

}

var obj=new SubType();//new調用SubType構造函數,於是SubType函數裡面的this

//指向obj。

2、優勢

借用構造函數解決了原形鏈繼承的共享問題

借用構造函數的問題:

3、缺點

屬性無法共享

如sayName是一個函數,理應所有對象共享,而不是沒創建一個對象,就要給這個對象添加一次sayName方法。

超類原型中的屬性對子類不可見

例如上面在SuperType的原型中定義了sayHi方法,但是子類是不會有sayHi方法的。

4、點評

借用構造函數解決了原形鏈的共享問題,但是也導致該共享的屬性如:函數,無法共享。所以借用構造函數繼承解決了共享問題,又敗在共享

三、組合繼承(也叫偽經典繼承)

1、組合繼承做法

示例:

function SuperType(){

this.color=["red","blue"];

}

SuperType.prototype.getColor=function(){console.log(this.color);};

function SubType(){

SuperType.call(this);

}

SubType.prototype=new SuperType();

var obj1=new SubType();

var obj2=new SubType();

obj1.color.push("green");

console.log(obj1.color);//輸出red,blue,green

console.log(obj2.color);//輸出red,blue

console.log(SubType.prototype.color);//輸出red,blue

2、優勢

組合繼承融合了原形鏈和借用構造函數之所長,讓該共享的屬性共享,不該共享的屬性不共享。可以說是一種比較理想的實現繼承的手段。

3、缺點

雖然組合繼承已經是很好的一種繼承方式了,但是組合繼承還有個小小的遺憾,就是使子類的原型跟子類的實例各自存放一份屬性。如上面示例的SubType的原型就有一個color,而obj1和obj2實例本身也有color,於是會有這樣的結果,如果delete obj2.color,然後console.log(obj2.color);仍然會輸出red,blue,delete後的color來自於SubType原型

4、點評

組合繼承集原形鏈繼承及借用構造函數繼承之所長,解決了共享問題,讓該共享的屬性共享,不該共享的屬性不共享。雖然還有個小缺點,但是已經算是很理想的繼承方式了

四、原型式繼承與寄生式繼承

所謂原型式繼承就是以一個對象A作為另一個對象B的原型,並創建B。所謂寄生式繼承,其實就是對原型式繼承做了一層簡單的封裝。

1、原型式與寄生式繼承做法

示例:

function extend(obj){

function F(){}

F.prototype=obj;

return new F();

}

var Person = {

name : '名稱',  //名稱

friends:["Simen","Van"],//引用此屬性需要注意原型共享問題

getName : function(){

return this.name;

},

};

function createAuthor(name,book){

var author=extend(Person);//原型式繼承Person

author.name=name;

author.book=book;       //擴展Person

return author;

}

function createCoder(name,language){

var coder=extend(Person); //原型式繼承Person

coder.name=name;

coder.language=language;//擴展Person

return coder;

}

var author=createAuthor("莎士比亞", "哈姆雷特");//整個createAuthor的過程就是寄生

//式繼承

console.log("作者:"+author.getName()+" 代表作:"+author.book);//getName來自

//Person

var coder=createCoder("Leo", "C++");//整個createCoder的過程就是寄生式繼承

console.log("碼農:"+coder.getName()+"  語言:"+coder.language); //getName函數來

//自Person

console.log(author.friends);//注意friend是author的原型Person的屬性,會有原型共//享問題

console.log(coder.friends);

author.friends.push("Lemon");

console.log(author.friends);

console.log(coder.friends);

2、優勢

可以直接利用現有對象進行擴展,而不必要去興師動眾地聲明類

3、缺點

作為原型的對象,最好只有方法,沒有屬性。如果有屬性,最好不要使用屬性,否則需要注意原型屬性的共享問題。如果共享正好就是你想要的,那就不會有什麼問題。

上面的示例代碼中,author和coder都以Person對象為原型實現了繼承,並各自進行了擴展,author增加了book屬性,coder則增加了language屬性。由於author和coder的原型都是Person,Person有getName,所以author和coder都可以調用getName。也就是說getName是放在Person中作為原型屬性被author和coder共享的,這很好,將函數作為原型屬性共享通常是好事。但是注意Person有friends屬性,這可能會引起原型共享的問題。所以結論很明顯,被繼承的對象,這裡是Person,最好是只有方法,如果有屬性,例如friends,如果需要使用friends的話,需要注意原型共享的問題。

五、寄生組合式繼承

前面說過,組合繼承有個缺點,就是組合繼承調用了兩次構造函數,即在子類內部借用構造函數,以及子類的原型以new形式調用超類構造函數,導致子類存有兩份超類的實例屬性。

1、組合繼承的缺點:

function SuperType(){

this.name=」Leo」;

}

SuperType.prototype.sayName=function(){

alert(this.name);

}

function SubType(){

SuperType.call(this);//繼承實例屬性

}

SubType.prototype=new SuperType();//這裡超類的實例與原型屬性都繼承了,需要注//意的是,上面已經繼承過實例屬性

2、組合繼承的改進方案:寄生組合式繼承

function createObjectWithPrototype(obj){

function F(){}

F.prototype=obj;

return new F();

}

function inheritPrototype(SubType,SuperType){

var prototype=createObjectWithPrototype(SuperType.prototype);

prototype.constructor=SubType;

SubType.prototype=prototype;

}

function SuperType(){

this.name="Leo";

}

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

function SubType(){

SuperType.call(this);

this.age=18;

}

inheritPrototype(SubType, SuperType);

SubType.prototype.sayAge=function(){

console.log(this.age);

};

var s=new SubType();

s.sayAge();

s.sayName();

3、對寄生組合式繼承封裝

function createObjectWithPrototype(obj){

function F(){}

F.prototype=obj;

return new F();

}

function inheritPrototype(SubType,SuperType){

var prototype=createObjectWithPrototype(SuperType.prototype);

prototype.constructor=SubType;

SubType.prototype=prototype;

}

/**

*若超類有參數,需要指定第三個參數。影響性能主要因素有超類的參

*數個數,超類參數越多性能越低。如果要傳參,最好超類與子類都采

*用參數對象進行傳參。另一個影響性能的因素就是在調用extend之

*對子類的原型進行了擴展,擴展越多,性能越低。最好是在extend之

*後再對子類原型進行擴展

*/

function extend(SubType,SuperType,subTypeArgsNum){

if(arguments.length<2)

throw new Error("參數錯誤,至少要兩個參數");

if(typeof SubType!="function" || typeof SuperType!="function")

throw new Error("參數類型錯誤,第一個參數需要傳入function類型");

if(typeof subTypeArgsNum!="number" && subTypeArgsNum!=null)

throw new Error("參數類型錯誤,第三個參數需要傳入數值類型或者為空");

function F(SubTypeArgs){//構造函數繼承

SubType.apply(this,arguments);

if(subTypeArgsNum!=null){

var argLength=arguments.length;

var superArgs=[];

for(var i=subTypeArgsNum;i<argLength;i++){

superArgs.push(arguments[i]);

}

SuperType.apply(this,superArgs);

}

else SuperType.apply(this);

};

inheritPrototype(F, SuperType);//原型繼承

var subTypePrototype=SubType.prototype;

var prototype=F.prototype;

for(property in subTypePrototype){

prototype[property]=subTypePrototype[property];

}

prototype.constructor=SubType;

return F;

}

function SuperType(name){

this.name=name;

}

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

function SubType(ageObj){

this.age=ageObj.age;

}

SubType.prototype.sayAge=function(){

console.log(this.age);

};

SubType=extend(SubType,SuperType,1);

var s=new SubType({age:18},"Leo");

s.sayAge();

s.sayName();

將繼承過程封裝起來後,實現了子類與超類繼承的解耦,提高了代碼的簡潔性。不

過這是以較大的性能損耗作為代價的。還有待改進

4、優勢

寄生組合繼承,在組合繼承的基礎上對其進行改進,解決了組合繼承的缺點。

可以說是一種近乎完美的繼承方案

5、缺點

寄生組合繼承的缺點就是比組合繼承略微複雜點,不過這不是什麼問題。

6、組合、組合寄生與組合寄生封裝後的性能比較

組合繼承,組合寄生繼承與組合寄生封裝後的繼承性能比較:組合繼承與組

合寄生繼承性能相當。但是函數封裝後實現繼承所需時間是則是組合繼承或

組合寄生繼承所花時間的3~10倍,性能損失很大。但是封裝函數實現繼承比

較簡單,而且由於統一使用繼承函數,代碼更加簡潔可維護,可根據實際情

況選用繼承方式

相關文章

  1. 《古劍奇譚2》關於二周目繼承問題詳解

    <古劍奇譚2>關於二周目繼承問題詳解 一.繼承情況 1.確認不能繼承的物品:除武器外的所有裝備,即衣服鞋子帽子裝飾靈石全部不能繼承. 2.確認可以繼承的物品: (1)武器 (2)錢 (3) ...
  2. javascript中this詳解

    this this是javascript的一個關鍵字,隨著函數使用場合不同,this的值會發生變化.但是總有一個原則,那就是this指的是調用函數的那個對象. 純粹函數調用 function test ...
  3. javascript console 函數詳解 js開發調試的利器

    Console 是用於顯示 JS和 DOM 對象信息的單獨窗口.並且向 JS 中注入1個 console 對象,使用該對象 可以輸出信息到 Console 窗口中. 使用 alert 不是一樣可以顯示 ...
  4. 經濟適用房繼承問題詳解

    經濟適用房能繼承嗎?成都市房管局<保障性住房申請人死亡後相關問題的處理暫行辦法>在資格審核.簽訂合同.權屬登記等階段死亡時,其所涉房屋權屬問題做了規定. 成都市房管局出台新規<保障性 ...
  5. 《真三國無雙聯合突襲2》繼承要素詳解

    光榮於近日公布了將於3月11日在psp平台上發售的動作遊戲<真三國無雙 聯合突襲2>的最新情報,本次公開的情報是聯機通信模式和遊戲中都市新機能以及參考並吸收了帝國的一些遊戲要素的介紹.那麼 ...
  6. Javascript繼承詳解

    一.原型鏈繼承方式 1.原形鏈繼承做法 示例: function SuperType(){ this.property=true; } Super.prototype.getSuperValue=fu ...
  7. javascript 詳解document.write()含義及用法

    1. 所有主流瀏覽器都支持JavaScript. 2. 目前,全世界大部分網頁都使用JavaScript. 3. 它可以讓網頁呈現各種動態效果. 4. 做為一個Web開發師,如果你想提供漂亮的網頁.令 ...
  8. Javascript apply與call函數詳解

    Javascript apply與call函數詳解 方法/步驟 前言:鑑於網上各種關於apply和call的帖子,多如牛毛,又講的很囉嗦,沒一篇講到本質上,於是就有了這篇帖子. 一.apply 1.a ...
  9. javascript 詳解document.write()方法

    javascript 詳解document.write()方法 在javascript中,document.write()方法:常用來網頁向文檔中輸出內容. 示例:通過document.write() ...
  10. 詳解用javascript寫一個簡單計算器(二)

    上一篇已經寫好計算器的界面,和最簡單的計算功能,這篇主要是繼續完善計算器的機能,增強其功能,和提高其使用的容錯率. 方法/步驟 var results=""; var calres ...
  11. javascript事件委託案例詳解

    在javascript中 onclick,onmouseover,onmouseout等這些稱著事件,那麼委託呢? 委託->大家都聽過法律上有委託人,代理人之類的名詞.  顧名思義,委託就是在事 ...
  12. 詳解JavaScript中循環控制語句的用法

    JavaScript提供完全控制來處理循環和switch語句.可能有一種情況,當你需要退出一個循環,但未達到其底部.也可能有一種情況,當要跳過的碼塊的一部分,並直接開始下一個疊代. 為了處理這些情況下 ...
  13. 房產繼承過戶費用及程序詳解

    繼承人繼承了被繼承人的房產後,有一道非常重要的程序,那就是進行過戶. 工具/原料 繼承權公證費用. 房地產價值評估費用. 房產繼承過戶程序. 方法/步驟 1.繼承權公證費用: 繼承權公證費按照繼承人所 ...
  14. javascript的冒泡和捕獲功能詳解

    前言:雖然精通jquery,但對它的原型javascript卻不是很了解,最近在學習javascript中遇到了一些困難,比如冒泡和捕獲,很多次被提到,但又不知究竟應用在何處.找到了一些好文章解惑,在 ...
  15. java中子類繼承父類程序各成員的執行順序詳解

    java的面向對象包括封裝.繼承.多態3個主要要點,那么子類繼承父類後程序中各成員的執行順序是怎樣的呢?下面教大家怎麼自己測試其執行順序. 工具/原料 java運行環境 Notepad++ 方法/步驟 ...
  16. 詳解用javascript寫一個簡單計算器(一)

    本文將詳細講解如果用HTML,css,javascript與正則表達寫一個簡單計算器,由於篇幅較長,我們分開幾篇來講. 方法/步驟 首先我們要做好一個計算器的界面,主要用到html與css的知識,下面 ...
  17. javascript 詳解 window.open()方法

    javascript 詳解 window.open()方法    上網細心的朋友,有可能注意到,打開網站時,一個小窗口浮在當前窗口的左上方.上面寫著一些網站公告之類的內容. javascript wi ...
  18. js(JavaScript)數組使用方法詳解

    實際上所有的程式語言都有一個內置的數組數據類型. JavaScript沒有什麼不同. 它們存儲了應用程式所需的數據,從開發人員的角度來看,這意味著您將編寫較少的代碼,從而提高生產力.下面開始介紹它的使 ...
  19. JavaScript使用詳解

    在html或jsp等開發頁面中通常會使用JavaScript進行頁面的調整,以及有些與後台進行的數據交互也是通過js實現的,下面就簡單的介紹一下js. 工具/原料 JavaScript 頁面 方法/步 ...
  20. 行屍走肉第二季存檔繼承失敗問題解決辦法詳解

    行屍走肉第二季將延續上一作的內容,主角由大叔轉變成了小蘿莉,第二部作品也吸引了許多玩家去玩,最近有童鞋發現了遊戲的問題,行屍走肉第二季存檔繼承失敗問題,造成這個的原因可能就是存檔的名字,下面是解決的方 ...