讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議98:比較使用JavaScript多態、重載和覆蓋 >

建議98:比較使用JavaScript多態、重載和覆蓋

在JavaScript中,加號是一個多態運算符,它能夠根據傳入值的類型進行不同的計算。從某種意義上來說,多態是面向對像中重要的一部分,也是實施繼承的主要目的。一個實例可以擁有多種類型,既可以是這種類型,也可以是那種類型,這種多類型稱為類的多態。

多態表現為兩個方面:類型的模糊和類型的識別。JavaScript是一種弱類型語言,通過typeof運算符來判斷值的類型,但通過typeof無法確定對象的類型,所有類型的實例對像對於typeof運算符來說都是基本的object,因此JavaScript的類型是比較模糊的。由於沒有嚴格的類型檢測,因此可以為任何對像調用任何方法,無須考慮它是否被設計為擁有該方法。使用JavaScript的原型可以設計類的多態特性。


function A{//超類A

this.get=function{

alert("A");

}

}

function B{//子類B

this.get=function{

alert("B");

}

}

B.prototype=new A;//設置B類繼承A類

function C{//子類C

this.get=function{

alert("C");

}

}

C.prototype=new A;//設置C類繼承A類

function F(x){//多態類F

this.x=x;

}

F.prototype.get=function{

if(this.x instanceof A)//判斷是否為超類的實例,然後調用不同類的方法

this.x.get

}

var b=new B;

var c=new C;

var f1=new F(b);

var f2=new F(c);

f1.get;//B,此時該方法指向的是B類中的方法get

f2.get;//C,此時該方法指向的是C類中的方法get


重載和覆蓋是兩個不同的類型概念,重載(overload)就是指同名方法有多個實現,依靠參數的類型或參數的個數來區分和識別它們。在JavaScript中,函數的參數是沒有類型的,並且參數個數也是任意的。看下面的示例:


function f(x,y){

return x+y;

}


示例中的函數f雖然指定了兩個形參,但是仍然可以在調用時傳遞任意多個實參,參數的類型也可以是任意的。由於JavaScript語言是弱類型語言,不會根據傳遞的參數個數和類型來決定要執行的行為,因此,要定義重載方法,只能夠通過arguments來實現。


function f{

var sum=0;

for(var i=0;i<arguments.length;i++){

if(typeof arguments[i]=="number")

sum+=arguments[i];

}

return sum;

}


上面的函數實現了重載對任意多個參數求和的函數。不管函數f中包含多少個參數,也不管參數類型如何,該函數將會自動把其中的數值類型的參數相加並返回總數。


alert(f(3,4,6,7,8,9));//重載函數f,返回37

alert(f(3,4));//重載函數f,返回7


結合instanceof運算符和constructor屬性來判斷參數類型,並且根據參數個數和類型執行不同的操作,可以實現複雜的方法重載。

覆蓋(overrid)是指在子類中定義的方法與超類中的方法同名,並且參數類型和個數也相同,當子類被實例化後,從超類中繼承的同名方法將被隱藏。下面是一個簡單的示例。


function A{//超類A

this.m=function{

alert("A");

}

}

function B{//子類B

this.m=function{

alert("B");

};

}

B.prototype=new A;//類B繼承類A

B.prototype.constructor=B;//恢復B類的原型對象的構造器

var b=new B;b.m;//字符B,說明子類B的方法m將覆蓋類A的方法m


在強類型語言中,在覆蓋的方法中可以調用被覆蓋的方法(超類的方法),不過可以通過臨時私有變量先保存超類的同名方法,然後在子類同名方法中調用即可。實現的代碼如下:


function A{

this.m=function{

alert("A");

}

}

function B{

var m=this.m;//先使用私有變量保存超類繼承的同名方法

this.m=function{

m.call(this);

alert("B");

};

}

B.prototype=new A;//類B繼承類A

B.prototype.constructor=B;

var b=new B;

b.m;//字符B,說明子類B的方法m將覆蓋類A的方法m


在覆蓋方法中調用超類的同名方法時,需要使用call或apply方法來改變執行上下文為this,如果直接調用該方法,執行上下文就會變成全局對象,在特殊語境中可能會發生歧義。