首页 » 编写高质量代码:改善JavaScript程序的188个建议 » 编写高质量代码:改善JavaScript程序的188个建议全文在线阅读

《编写高质量代码:改善JavaScript程序的188个建议》建议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


面向对象中并不是所有的事物泛型都是使用继承关系来描述的,继承关系只是泛型关系的一种,除此之外,创建关系、原型关系、聚合关系、组合关系等,都是泛型的一种类型。泛型概念很宽泛,通常使用继承、聚合和组合来描述事物的名词特性,而使用原型、元类等其他概念来描述事物的形容词概念。