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

《编写高质量代码:改善JavaScript程序的188个建议》建议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,如果直接调用该方法,执行上下文就会变成全局对象,在特殊语境中可能会发生歧义。