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

《编写高质量代码:改善JavaScript程序的188个建议》建议91:推荐使用构造函数原型模式定义类

关灯直达底部

JavaScript中定义类型的方式有多种,这也形成了不同的类型模式。

(1)工厂模式

工厂模式是指通过函数把一个类型实例包装起来,这样可以通过调用函数来实现类型的实例化。


function wrap(title,pages){

var book=new Object;

book.title=title;

book.pages=pages;

book.what=function{

alert(this.title+this.pages)

}

return book;//初始化后的对象

}


也可以对该模式进行优化,进而消除重复创建相同函数的弊端,节约大量资源。


what=function{

alert(this.title+this.pages)

}

function wrap(title,pages){

var book=new Object;

book.title=title;

book.pages=pages;

book.what=what;

return book;

}


工厂模式只是一种伪装的构造函数,不推荐使用。

(2)构造函数模式

在JavaScript中,构造函数具有如下特性:

❑构造函数使用new运算符进行调用。

❑在构造函数内部,this关键字指代当前实例对象。

❑在构造函数内部,必须通过点运算符来声明和引用成员。构造函数的结构体内可以包含一般函数的执行语句。

例如,对于下面这个构造函数Box,传递给Box的是一个新创建的空对象,该对象是高度抽象但未知的,通过this关键字来代称,this的值就是这个新创建的空对象引用。当使用new运算符实例化构造函数时,可以通过传递参数来初始化这个对象的属性值。


function Box(w,h){//构造函数

this.w=w;

this.h=h;

}

var box1=new Box(4,5);//实例并初始化构造函数


由于每一个构造函数代表一种类型,为了与普通函数进行区分,函数名应该很直观,并且首字母要大写(非强制的)。如果构造函数返回对象,那么被返回的对象将覆盖this的值。例如:


function Box(w,h){//构造函数

this.w=w;

this.h=h;

return this;

}


(3)原型模式

先声明一个构造函数,并且利用构造函数的prototype属性为该构造函数定义原型属性title和pages,以及原型方法what。构造函数的原型成员将会被所有实例对象继承,这样当使用new运算符实例化对象时,所有对象都拥有原型属性中定义的成员。


function Book{//空类

}

Book.prototype.title=/"Javascript设计方法/";

Book.prototype.pages=200;

Book.prototype.what=function{

alert(this.title+this.pages);

};


从语义的角度分析,通过原型继承的方式,实现了在前面两种模式中将对象与其方法分离的设计思想。使用instanceof运算符能够方便地检测对象实例的类型。原型模式存在以下两个问题:

❑由于构造函数已被事先声明,而原型属性在类结构声明之后才被定义,因此无法通过构造函数参数向原型属性动态传递值。这样所带来的后果:由该类实例化的所有对象都是一个“模样”,没有“个性”。如果改变原型属性值,那么所有实例都受到干扰。这是非常严重的问题,如果无法解决,该项研究将无果而终。

❑当原型属性的值为引用类型数据时,如果在一个对象实例中修改该属性值,将会影响所有的实例。由于原型属性x的值为一个引用类型数据,因此所有对象实例的属性x的值都是指向该对象的引用指针。一旦某个对象的属性值被改动,其他实例对象的属性值也会随着发生变化。

(4)构造函数原型模式

构造函数原型模式是建立在原型模式基础上的一种混合设计模式,将构造函数模式与原型模式混合使用。对于可能会相互影响且希望动态传递参数的属性,将其拆分出来使用构造函数模式进行设计。而对于不需要个性、希望共享,并且又不会相互影响的方法或属性,单独使用原型模式来设计。


function Book(title,pages){//构造函数模式设计

this.title=title;

this.pages=pages;

}

Book.prototype.what=function{//原型模式设计

alert(this.title+this.pages);

};


在混合使用构造函数与原型模式时,可以不使用构造函数来定义对象的所有非函数属性(即对象属性),而使用原型模式来定义对象的函数属性(即对象方法)。这样所有方法都只创建一次,而每个对象都能够根据需要自定义属性值。这种混合型模式成为ECMAScript定义类的推荐标准,这也是使用最广的一种设计模式,它具有前面3种设计模式的所有优点,而且去除了它们的副作用。

遵循面向对象的设计原则,类的所有成员都应该封装在类结构体内,因此可以进一步优化构造函数原型模式,从而产生动态原型模式。优化的思路:使用条件结构封装该原型方法,判断原型方法是否存在,如果存在,则不再创建该方法,否则就创建该方法。


function Book(title,pages){

this.title=title;

this.pages=pages;

if(typeof Book.isLock==/"undefined/"){//创建原型方法的锁,如果不存在该方法则创建

Book.prototype.what=function{

alert(this.title+this.pages);

};

Book.isLock=true;//创建原型方法后,把锁锁上,避免重复创建

}

}


“typeof Book.isLock”表达式能够检测该属性值的类型,如果返回undefined字符串,则表示不存在该属性值,说明还没有创建原型方法,允许进入分支结构创建原型方法,并设置该属性的值为true,这样它的类型返回值就是boolean字符串。

动态原型模式与构造函数原型模式在性能上是等价的,不过目前使用最广泛的是构造函数原型混合模式。