讀古今文學網 > 編寫高質量代碼:改善JavaScript程序的188個建議 > 建議103:使用摻元類 >

建議103:使用摻元類

摻元類就是共享通用的方法和屬性,將差異比較大的類中相同功能的方法集中到一個類中聲明,這樣需要這些方法的類就可以直接從摻元類中進行擴展,通用的方法只需要聲明一遍就行了。從代碼的大小、質量方面來說,這還是有一定效益的。如圖4.7所示,摻元類就是讓一個類被多個類繼承,這種繼承關係被形象地稱為多親繼承,它是一種比較特殊的類形式。

圖 4.7 多親繼承示意圖

如果希望某個函數被多個類調用,那麼可以通過擴充的方式讓這些類共享該函數。具體的設計思路:先創建包含通用函數的超類,然後利用這個超類擴充子類,這種包含通用方法的類可以稱為摻元類。例如,先設計一個摻元類F,設想兩個子類A和B能夠繼承摻元類F的通用方法getx和gety。代碼如下:


var F=function(x,y){//構造函數F,摻元類

this.x=x;

this.y=y;

}

F.prototype={

getx:function{

return this.x;

},

gety:function(y){

return this.y;

}

}


然後,定義兩個子類A和B,利用類繼承方法先繼承摻元類中的本地屬性,以方便繼承的方法正確獲取值。實際應用中不使用類繼承來繼承摻元類的本地屬性和方法。


A=function(x,y){//子類A

F.call(this,x,y);//繼承摻元類F

};

B=function(x,y){//子類B

F.call(this,x,y);//繼承摻元類F

};


要讓A類和B類都繼承F類,可以使用原型繼承方法來實現,但原型繼承需要實例化F類。我們可以模仿複製繼承方法設計一個專門函數來實現這種繼承關係,具體代碼如下:


//摻元類繼承封裝函數,其中參數Sub表示子類,參數Sup表示摻元類

function extend(Sub,Sup){

for(m in Sup.prototype){//遍歷摻元類的原型對像

if(!Sub.prototype[m]){//如果子類不存在同名成員,則複製摻元類原型成員給子類原型對像

Sub.prototype[m]=Sup.prototype[m];

}

}

}


該函數很簡單,使用for in循環遍歷摻元類的原型對像中的每一個成員,並將其添加到子類的原型對像中。如果子類中已存在同名成員,則跳過該成員,轉而處理下一個,這樣能夠確保子類原型對像中的成員不會被改寫。有了這個封裝函數,就可以直接調用它來快速生成多個相同的子類。傳遞子類參數必須事先聲明,並且應通過類繼承方法來繼承F的本地屬性和方法。


extend(A,F);//繼承F的子類A

extend(B,F);//繼承F的子類B


最後,實例化A類和B類,這樣就可以調用F定義的通用方法了。


var a=new A(1,2);

var b=new B(10,20);

alert(a.getx);//1

alert(a.gety);//2

alert(b.getx);//10

alert(b.gety);//20


也可以把多個子類合併到一個類中來實現多重繼承。例如,下面的示例定義了兩個類A和B,並分別為它們定義兩個原型方法。


var A=function{}//類A

A.prototype={

x:function{

return"x";

}

}

var B=function{}//類B

B.prototype={

y:function{

return"y";

}

}

C=function{};//空類C

extend(C,A);//把類A繼承給類C

extend(C,B);//把類B繼承給類C

var c=new C;//實例化類C

alert(c.x)//字符x

alert(c.y)//字符y


面向對像中並不是所有的事物泛型都是使用繼承關係來描述的,繼承關係只是泛型關係的一種,除此之外,創建關係、原型關係、聚合關係、組合關係等,都是泛型的一種類型。泛型概念很寬泛,通常使用繼承、聚合和組合來描述事物的名詞特性,而使用原型、元類等其他概念來描述事物的形容詞概念。