掺元类就是共享通用的方法和属性,将差异比较大的类中相同功能的方法集中到一个类中声明,这样需要这些方法的类就可以直接从掺元类中进行扩展,通用的方法只需要声明一遍就行了。从代码的大小、质量方面来说,这还是有一定效益的。如图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
面向对象中并不是所有的事物泛型都是使用继承关系来描述的,继承关系只是泛型关系的一种,除此之外,创建关系、原型关系、聚合关系、组合关系等,都是泛型的一种类型。泛型概念很宽泛,通常使用继承、聚合和组合来描述事物的名词特性,而使用原型、元类等其他概念来描述事物的形容词概念。