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

《编写高质量代码:改善JavaScript程序的188个建议》建议86:使用面向对象模拟继承

关灯直达底部

JavaScript是一种弱类型解释运行的脚本语言,就语言本身来讲,它不是一门面向对象语言,但我们可以利用一些语言特性来模拟面向对象编程和继承机制,这一切都需要从JavaScript中的function讲起。

函数是一个定义一次但调用或执行无数次的JavaScript代码片段。函数可以有零个或多个输入参数和一个返回值,它通常用于完成一些计算或事务处理等任务。通常可以这样定义一个function:


function distance(x1,y1,x2,y2){

var dx=x2-x1;

var dy=y2-y1;

return Math.sqrt(dx*dx+dy*dy);

}


在JavaScript中,function不仅是一种语法结构,还可以作为一种数据。这意味着它可以被赋值给变量,在对象或数组中作为元素的属性存储,或者作为函数参数传递等。例如,把function作为数据使用:


var d=distance;

d(1,1,2,2);


当在一个object中定义和调用一个function时,这个function被称做该object的一个方法。需要注意的是,当这个function被调用时,这个object会以隐含参数的形式传入到function中,function内部可以通过this关键字来引用这个object的属性。例如,在下面这个例子的运行结果中,calculator.result的值为2。


var calculator={

operand1:1,

operand2:1,

compute:function{

this.result=this.operand1+this.operand2;

}

};

calculator.compute;

alert(calculator.result);


在JavaScript中,对象的创建通常是通过new运算符来完成的,new关键字后面必须是一个可执行的function。例如:


var array=new Array(10);

var today=new Date;


当上面这条创建语句被执行时,首先会创建一个空的对象赋给前面的变量,然后调用后面紧跟的function,并且将这个空的对象作为隐含参数传入到function内部。这样在function内部就可以通过this关键字引用该对象,做一些对象初始化工作。这样的function通常被称为构造方法或简单构造方法。例如:


function Rectangle(w,h){

this.width=w;

this.height=h;

}

var rect1=new Rectangle(2,4);


到这里已经有了对象、函数、作为数据的函数、对象方法和构造函数,于是可以使用这些JavaScript的特性模拟写出类似于Java风格的面向对象的代码。例如,利用下面代码就可以模拟面向对象。


function Rectangle(w,h){

this.width=w;

this.height=h;

this.area=function{

return this.width*this.height;

};

}

var rect1=new Rectangle(2,4);

rect1.area;


上面的代码看起来非常不错,但似乎还缺少一些东西。面向对象的精髓是对事物的抽象和继承,以达到代码重用的目的,在JavaScript中如何做到这一点呢?

在JavaScript中,每一个object都有一个prototype属性,这个属性引用了另一个object对象。当需要读取一个object中的某个属性p时,JavaScript引擎先查找这个object中是否存在属性p,如果存在则返回该属性值,如果不存在则在这个object的prototype对象中查找是否存在属性p。这样做会有两点好处:第一,在每个类中可以抽象出来一些共有的属性和方法定义在prototype对象中,当使用构造函数创建多个类的实例时,多个实例使用同一prototype对象大大减小了对内存的占用量。第二,新创建的对象的属性是在创建对象时由prototype对象带来的,这样就可以实现属性和方法的继承。

在前面对function的介绍中曾提到,当new操作符被调用时会创建一个空的对象赋给变量并调用后面的构造函数。实际上在利用new操作符创建一个空对象后,还会为这个对象设置它的prototype属性,这个属性值等于它的构造函数的prototype属性值。所有的函数在其定义时就已经自动创建和初始化好了prototype属性,这个初始化好的prototype属性指向一个只包含一个constructor属性的对象,并且这个constructor属性指向这个function自身。这就解释了为什么每一个object都会有一个constructor属性。运行下面的代码,将打印出一个名为Object的简版的函数体。


var obj=new Object;

alert(obj.constructor);


了解了JavaScript模拟面向对象编程基础知识,就可以通过将一些共有的属性和方法定义在构造方法中来实现属性的继承。


function Rectangle(w,h){

this.width=w;

this.height=h;

}

Rectangle.prototype.area=function{

return this.width*this.height;

};


另外,要继承另一个类并重写和添加一些方法,可能需要复制这个类的构造方法的prototype对象中的属性和方法,然后再根据需要重写和添加一些方法和属性。