Js中的繼承

參考自:[http://jingyan.baidu.com/article/cd4c2979f2f664756e6e600b.html]

原型鏈繼承

利用prototype實現繼承,prototype對象是個模板,要實例的對象都是以這個模板爲基礎,它的任何屬性和方法都被傳遞給那個類的所有實例,原型鏈利用這種功能來實現繼承機制。

例子:
function ClassA(){}

ClassA.prototype.color="red";

ClassA.prototype.sayColor=function(){
    alert(this.color);
}
function ClassB(){}
ClassB.prototype=newClassA();

通過原型鏈,ClassA的所有屬性和方法傳遞給了ClassB,用prototype的確是方便很多。

注意的是調用ClassA的構造函數是,沒有給它傳遞參數,這是在原型鏈中的標準做法。要確保構造函數沒有任何參數。如果構造函數中有參數的話會怎樣呢?那樣便不能完全的繼承,只能繼承父類通過prototype初始的屬性和方法,在構造函數中初始的屬性和方法便不會繼承。

與對象冒充相似,子類的所有屬性和方法都必須出現在prototype屬性被賦值之後,因爲在它之前賦值的所有方法都會被刪除。爲什麼呢?因爲prototype屬性被替換成了新的對象,原始對象的prototype屬性以不復存在了


對象冒充(構造函數繼承)

感覺這種方式利用了js中類和函數的模糊性,同是function關鍵字申明方法,既可以說他是函數,也可以說他是類,js太靈活了

例子:
function ClassA(sColor){

     this.color=sColor;

     this.sayColor=function(){

        alert(this.color);

      } 

}

function ClassB(sColor){

     this.newMethod=ClassA;//把ClassA方法賦給newMethod.

     this.newMethod();//調用newMethod.

     delete  this.newMethod;
     }

ClassB執行ClassA方法便相當於繼承了ClassA,在調用完畢後要刪除newMethod,因爲後續添加的屬性和方法如果和超類同名,就會覆蓋超類的相關屬性和方法。

利用這種繼承方式可以實現多重繼承,例如:

 function ClassD(sColor){

     this.newMethod=ClassA;//把ClassA方法賦給newMethod,

     this.newMethod();//調用newMethod

     delete  this.newMethod;

     this.newMethod=ClassB;

     this.newMethod();

     delete  this.newMethod;}

利用這種多重繼承方法有個弊端,如果ClassA和ClassB具有相同的屬性和方法,ClassB有較高的優先級。


call方法和apply方法

ECMAScript在function對象加入兩個新方法,call()和apply(),這兩中方法很相似,只有在傳參方面有所不同,call()方法的第一個參數用作this的對象,例如:

function ClassB(sColor,sName){

     ClassA.call(this,sColor);

    this.name=sName;

    this.sayName=function(){

         alert(this.name);

   }

}

call方法還是調用了ClassA()方法,利用this傳遞進去ClassB,爲ClassB初始化,他僅僅是調用了ClassA()方法而已,如果你在ClassA之外爲ClassA添加了方法(例如利用原型法),是不能繼承到ClassB的。call()方法的第一個參數必須是this,可以有第二個,第三個,第四個….參數。

apply()方法與call()方法不同的是,將二個,第三個,第四個….參數用一個數組傳遞。例如:

 function ClassB(sColor,sName,sSex){

    var arr=new Arry(sColor,sName,sSex);

     ClassA.apply(this,arr);//傳遞數組

    this.name=sName;

    this.sayName=function(){

         alert(this.name);

     }

}

可以將arguments作爲參數傳遞給apply,但是ClassB的參數順序必須和ClassA一致。
另外,Function.apply()在提升程序性能方面有着突出的作用:
我們先從Math.max()函數說起,Math.max後面可以接任意個參數,最後返回所有參數中的最大值。

alert(Math.max(5,7,9,3,1,6));   //9   

//但是在很多情況下,我們需要找出數組中最大的元素。   
var arr=[5,7,9,1];  
//alert(Math.max(arr));    // 這樣卻是不行的。NaN   

//要這樣寫   
    function getMax(arr){  
        var arrLen=arr.length;  
        for(var i=0,ret=arr[0];i<arrLen;i++){  
            ret=Math.max(ret,arr[i]);  
        }  
        return ret;  
    }  

    alert(getMax(arr)); //9  

 //換用apply,可以這樣寫   
    function getMax2(arr){  
        return Math.max.apply(null,arr);  
    }  

    alert(getMax2(arr)); //9  

//兩段代碼達到了同樣的目的,但是getMax2卻優雅,高效,簡潔得多。   
//再比如數組的push方法。   
    var arr1=[1,3,4];  
    var arr2=[3,4,5];  
    //如果我們要把 arr2展開,然後一個一個追加到arr1中去,最後讓arr1=[1,3,4,3,4,5]   
    //arr1.push(arr2)顯然是不行的。 因爲這樣做會得到[1,3,4,[3,4,5]]   

    //我們只能用一個循環去一個一個的push(當然也可以用arr1.concat(arr2),但是concat方法並不改變arr1本身)   

    var arrLen=arr2.length;  
    for(var i=0;i<arrLen;i++){  
        arr1.push(arr2[i]);  
    }  

    //自從有了Apply,事情就變得如此簡單   

    Array.prototype.push.apply(arr1,arr2); //現在arr1就是想要的結果

混合方式

這種繼承方式使用構造函數定義類,並未使用任何原型,對象冒充的主要問題是必須使用構造函數方式,這不是最好的選擇。不過如果使用原型鏈,就無法使用帶參的構造函數了。開發者該如何選擇呢?答案很簡單,兩者都使用。創建類的最好方式是用構造函數方式定義屬性,用原型方式定義方法。這種方式同樣適用於繼承機制,用對象冒充繼承函數的屬性,用原型鏈繼承prototype對象的方法,用這兩種方式重寫前面的例子,代碼如下:

function ClassA(sColor){
     this.color=sColor;
     }
ClassA.prototype.sayColor=function(){
 alert(this.color);
 };

function ClassB(sColor,sName){
    callA.call(this,sColor);
    this.name=sName;
    ClassB.prototype=new ClassA();
    ClassB.prototype.sayName=function(){ 
    alert(this.name); 
};

在此例子中,繼承機制有兩行突出顯示的代碼實現。在第一行中,在ClassB的構造函數中,用對象冒充繼承ClassA類的sColor屬性。在第二行,用原型鏈繼承ClassA類的方法。由於這種混合方式使用了原型鏈,所以instanceof運算符仍能正確運行。

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