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

《编写高质量代码:改善JavaScript程序的188个建议》第4章 面向对象编程

关灯直达底部

JavaScript的许多特性都借鉴自其他语言,如语法借鉴自Java,函数借鉴自Scheme,原型继承借鉴自Self,而正则表达式特性借鉴自Perl。

在面向对象的语言(如C#、Java等)中,类是面向对象的基础,并且具有明显的层次概念和继承关系,每个类都有一个超类,从超类中继承属性和方法,类还可以进一步被扩展(扩展类称为子类),这样就构建了一个多层的、复杂的对象继承关系。但由于JavaScript是基于对象的弱类型语言,它是以对象为基础,以函数为模型,以原型为继承机制的开发模式,因此对于习惯于面向对象开发的用户来说,需要适应JavaScript语言的灵活性和特殊性。

在大多数编程语言中,继承都是重要的主题之一。在那些基于类的语言(如Java)中,继承拥有两个优势。类继承第一个优势是,它是代码重用的一种形式,如果一个新的类与一个已存在的类大部分相似,那么只需要具体说明其不同点即可。代码重用的模式极为重要,因为它们可以显著地减少软件开发的成本。类继承的另一个优势是,它包括了一套类型系统的规范。由于程序员无须编写显式类型转换的代码,所以大大减少了工作量。

建议79:参照Object构造体系分析prototype机制

原型是JavaScript核心特性之一,它通过prototype这个属性表现出来。在JavaScript中,对象(Object)是没有原型的,只有构造函数拥有原型,而构造类的实例对象都能够通过prototype属性访问原型对象。

prototype不仅是JavaScript实现和管理继承的一种机制,更是一种OO(面向对象)的设计思想。从语义角度分析,prototype表示类的原型,就是构造类拥有的原始成员。构造函数的prototype属性存储着一个引用对象的指针,该指针指向一个原型对象,它是一个特殊的对象,相当于一个数据集合,内部存储着构造函数的原始属性和方法。借助prototype属性,可以访问原型对象内部成员。当构造函数实例化后,所有实例对象都可以访问构造函数的原型成员。如果在原型对象中声明一个成员,则所有实例对象都可以共享它。

原型具有普通对象的结构,可以将任何普通对象实例设置为原型对象,在默认状态下原型对象继承于Object抽象类,由JavaScript原生并依附于每个构造函器上,从而实现构造类的原型属性和原型方法能够被所有实例对象继承。原型对象、原型属性在JavaScript对象系统中的位置和关系如图4.1所示。

图 4.1 原型对象、原型属性在JavaScript对象系统中的位置和关系

在JavaScript中,对象应该是类(class)和实例(instance)的关系演化。类是对象的模型化,而实例则是类的特征具体化。类包含很多概念类型,如元类、超类、泛类和类型等。例如:


function Class(type){//构造函数

this.type=type;

}

var instance1=new Class(/"instance1/");//实例对象1

var instance2=new Class(/"instance2/");//实例对象2


使用instanceof运算符可以验证它们的关系:


alert(instance1 instanceof Class);//true,说明instance1对象是Class构造函数的实例

alert(instance2 instanceof Class);//true,说明instance2对象是Class构造函数的实例


instance1和instance2都是对象,但Class构造函数不是它们唯一的类型,Object也是它们的类型:


alert(instance1 instanceof Object);//true,说明instance1对象也是Object构造函数的实例

alert(instance2 instanceof Object);//true,说明instance2对象也是Object构造函数的实例


Object比Class类型更加抽象,它们之间应该属于一种继承关系。


alert(Class instanceof Object);//true,说明Class类是Object对象的实例(或子类)


但instance1和instance2对象却不是Function构造函数的实例,这说明它们之间没有直接关系。


alert(instance1 instanceof Function);//false,说明它们不是类型与实例的关系

alert(instance2 instanceof Function);//false,说明它们不是类型与实例的关系


而Object与Function之间的关系就非常微妙,它们都是高度抽象的类型,互为对方的实例。


alert(Object instanceof Function);//true,说明Object对象是Function函数的实例(即子类)

alert(Function instanceof Object);//true,说明Function函数是Object对象的实例(即子类)


Object与Function同时也是两个不同类型的构造器。下面的代码能够很好地显示它们的差异。


var f=new Function;//实例化Function对象

var o=new Object;//实例化Object对象

alert(f instanceof Function);//true,说明f是Function对象的实例

alert(f instanceof Object);//true,说明f是Object对象的实例

alert(o instanceof Function);//false,说明o不是Function对象的实例

alert(o instanceof Object);//true,说明o是Object对象的实例


instance(对象实例)、Class(类型)、Object(抽象类)和Function(构造类)之间的关系如图4.2所示。

图 4.2 类型、原型和对象实例之间的关系

prototype是属于Function的成员,而prototype对象又是Object的一个实例,构造函数通过点语法访问prototype,再通过prototype访问原型对象成员。原型属性与本地特性之间的关系如图4.3所示。

图 4.3 原型属性与本地特性之间的关系

Object和Function都可以定义原型,Object被视为Function的子类。下面的示例能够很好地说明Object原型和Function原型的异同,这些原型及其属性之间的关系如图4.4所示。


Object.prototype.a=1;//声明Object的原型属性a的值为1

Function.prototype.a=2;//声明Function的原型属性a的值为2

alert(Object.a);//2,说明属性a指向Function构造函数的原型

alert(Function.a);//2,说明属性a指向Function构造函数的原型

var o={}//空的对象直接量

alert(o.a);//1,说明属性a指向Object构造函数的原型

var f=Object;//引用Object构造函数

alert(f.a);//2,说明属性a指向Function构造函数的原型

var f1=new Function;//实例化Function对象

alert(f1.a);//2,说明属性a指向Function构造函数的原型

var o1=new Object;//实例化Object对象

alert(o1.a);//1,说明属性a指向Object构造函数的原型


图 4.4 Function、Object、Prototype及其属性间的关系