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

《编写高质量代码:改善JavaScript程序的188个建议》建议80:合理使用原型

关灯直达底部

1.使用原型设置默认值

JavaScript是一种动态语言,它的对象系统也是动态的。在程序中,可以根据需要设置原型值,从而影响所有实例对象。如果为构造函数定义了与原型属性同名的本地属性,则本地属性会覆盖原型属性,例如:


function p(x){//构造函数

this.x=x;

}

p.prototype.x=1;

var p1=new p(10);

alert(p1.x);//10,本地属性覆盖原型属性


但原型属性并没有被删除,它依然存在,仅是被同名的本地属性覆盖了,如果使用delete运算符删除本地属性,那么原型属性依然会显示出来。因此,当原型属性与本地属性同时存在时,它们之间可以出现交流现象。利用这种现象为对象初始化默认值,例如:


function p(x){//构造函数

if(x)//如果参数存在,则使用该参数设置属性,此条件是关键

this.x=x;

}

p.prototype.x=0;//利用原型属性,设置本地属性x的默认值

var p1=new p;

alert(p1.x);//0,显示本地属性的默认值

var p2=new p(1);

alert(p2.x);//1,显示本地属性的初始化值


2.使用原型实现数据备份

把本地对象的数据完全赋值给原型对象,相当于为该对象定义一个副本,这就是备份对象。当对象属性被修改时,可以通过原型对象来恢复本地对象的初始值。下面示例演示了如何使用原型备份数据。


function p(x){//构造函数

this.x=x;

}

p.prototype.backup=function{//原型方法,备份本地对象的数据到原型对象中

for(var i in this){

p.prototype[i]=this[i];

}

}

var p1=new p(1);

p1.backup;//备份实例对象中的数据

p1.x=10;//改写本地对象的属性值

alert(p1.x);//10,说明属性值已经被改写

p1=p.prototype;;//恢复备份

alert(p1.x);//1,说明对象的属性值已经被恢复到原始值


3.使用原型设置只读属性

利用原型还可以为对象属性设置“只读”特性,这样可以避免对象内部数据被任意篡改。这里的“只读”只是一个表象,并不是真正禁止对象属性进行修改,而是借助闭包体存储属性值,这样就可以避免属性值被动态修改。

下面示例演示了如何根据平面上两点坐标来计算它们之间的距离。构造函数p用来设置定位点坐标,如果传递了两个参数值,会返回以参数为坐标值的点;如果省略参数,则默认点为原点(0,0)。而在构造函数l中,通过传递的两点坐标对象来计算它们的距离。

如果无意间修改了构造函数的方法b或e的值,则构造函数中length方法的计算值也随之发生变化。这种动态效果对于动态跟踪两点坐标变化来说,是非常必要的。但是,我们并不需要在初始化实例之后随意地改动坐标值,毕竟方法b和f与参数a和b是没有多大联系的,但它们的参数值却同时指向同一个对象的引用指针。


function p(x,y){//求坐标点构造函数

if(x)this.x=x;//初始x轴值

if(y)this.y=y;//初始y轴值

p.prototype.x=0;//默认x轴值

p.prototype.y=0;//默认y轴值

}

function l(a,b){//求两点间距离的构造函数

var a=a;

var b=b;

var w=function{//计算x轴距离,返回对函数引用

return Math.abs(a.x-b.x);

}

var h=function{//计算y轴距离,返回对函数引用

return Math.abs(a.y-b.y);

}

this.length=function{//计算两点间距离,使用小括号调用私有方法w和h

return Math.sqrt(w*w+h*h);

}

this.b=function{//获取起点坐标对象

return a;

}

this.e=function{//获取终点坐标对象

return b;

}

}

var p1=new p(1,2);//声明一个点

var p2=new p(10,20);//声明另一个点

var l1=new l(p1,p2);//实例化构造函数,传递两点对象

alert(l1.length);//20.12461179749811,调用length方法计算两点间距离

l1.b.x=50;//不经意地改动方法b的一个属性为50

alert(l1.length);//43.86342439892262,说明上面改动影响到两点间距离


为了避免因为改动方法b的属性x的值而影响两点间距离,可以在方法b和e中新建一个临时性的构造类,设置该类的原型为a,然后实例化构造类并返回,这样就阻断了方法b与私有变量a的直接联系,它们之间仅是值的传递,而不是对对象a的引用,从而避免因方法b的属性值变化而影响私有对象a的属性值。


this.b=function{//方法b

function temp{};//临时构造类

temp.prototype=a;//把私有对象传递给临时构造类的原型对象

return new temp;//实例化后的对象,阻断直接返回a所出现的引用关系

}

this.e=function{//方法f

function temp{};//临时构造类

temp.prototype=a;//把私有对象传递给临时构造类的原型对象

return new temp;//实例化后的对象,阻断直接返回a所出现的引用关系

}


还有一种方法,这种方法是在为私有变量w和h赋值时,不是向函数赋值,而是函数调用表达式,这样私有变量w和h存储的是值类型数据,而不是对函数结构的引用,从而不再受后期相关属性值的影响。


function l(a,b){//求两点间距离的构造函数

var a=a;

var b=b;

var w=function{//计算x轴距离,返回函数表达式的计算值

return Math.abs(a.x-b.x);

}

var h=function{//计算y轴距离,返回函数表达式的计算值

return Math.abs(a.y-b.y);

}

this.length=function{//计算两点间距离,直接使用私有变量w和h来计算

return Math.sqrt(w*w+h*h);

}

this.b=function{//获取起点坐标对象

return a;

}

this.e=function{//获取终点坐标对象

return b;

}

}


4.使用原型进行批量复制

先看下面一个示例:


function f(x){//构造函数

this.x=x;

}

var a=;

for(var i=0;i<100;i++){//使用for循环结构批量复制构造类f的同一个实例

a[i]=new f(10);//把实例分别存入数组

}


上面的代码演示了如何复制同一个实例对象100次。如果后期需要修改数组中每个实例对象,就会非常麻烦。现在可以尝试使用原型来进行批量复制操作,如下所示。


function f(x){//构造函数

this.x=x;

}

var a=;

function temp{};//定义一个临时的空构造类temp

temp.prototype=new f(10);//把构造类f实例化,并把该实例传递给构造类temp的原型对象

for(var i=0;i<100;i++){;//使用for循环结构批量复制临时构造类temp的同一个实例

a[i]=new temp;//把实例分别存入数组

}


把构造类f的实例存储在临时构造类的原型对象中,然后通过临时构造类temp实例来传递复制的值。因此,要想修改数组的值,只需要修改类f的原型即可,从而避免逐一修改数组中每个元素。