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

《编写高质量代码:改善JavaScript程序的188个建议》建议90:预防this误用的策略

关灯直达底部

this虽然用法灵活,但容易出错。好的使用习惯可以很好地预防this误用。事实上,this的复杂性在很大程度上取决于用户的使用方式。由于this指代灵活,如果把它放在复杂的应用环境中,它也会变得很不确定,因此必须牢记:确保在同一作用域中操作this,避免把包含有this的全局函数动态用于局部作用域中,同时应避免在不同作用域的对象之间相互引用包含this的方法。

如果把this作为参数值来调用函数,那么可以避免了this多变的问题,因为this始终与当前对象保持一致。例如,下面的做法是错误的,因为this在这里始终指向Window对象,而不是所期望的当前按钮对象:


<input type="button"onclick="f"/>

<input type="button"onclick="f"/>

<input type="button"onclick="f"/>

<script language="javascript"type="text/javascript">

function f{

alert(this.value);

}

</script>


但是,如果把this作为参数值进行传递,那么它就会代表当前对象:


<input type="button"onclick="f(this)"/>

<input type="button"onclick="f(this)"/>

<input type="button"onclick="f(this)"/>

<script language="javascript"type="text/javascript">

function f(o){

alert(o.value);

}

</script>


如果要确保构造函数的方法在初始化之后其中所包含的this指针不再发生变化,一个很简单的方法就是:在构造函数中把this指针存储在私有变量中,然后在方法中使用私有变量来引用this指针,这样所引用的对象始终都是初始化的实例对象,而不会在类型继承中发生变化。例如:


function Base{//基类

var_this=this;//存储初始化时对象的引用指针

this.m=function{

return_this;//初始化时对象的引用指针

};

this.name="Base";

}

function F{//子类

this.name="F";

}

F.prototype=new Base;//继承基类

var f=new F;

var n=f.m;

alert(n.name);//this始终指向原型对象,而不再是子类的实例对象


对于对象直接量来说,如果希望使用this代表当前对象直接量,则可以直接调用对象直接量的名称,而不用this关键字。

当然,作为一个动态指针,this也是可以被转换为静态指针的,实现的方法是主要利用Function对象的call或apply方法。这两个方法都可以强制指定this的指代对象。例如,为Function对象扩展一个原型方法pointTo,具体代码如下:


//把this转换为静态指针,参数o表示预设置this所指代的对象,返回一个闭包函数

Function.prototype.pointTo=function(o){

var_this=this;//存储当前函数对象

return function{//一个闭包函数

return_this.apply(o,arguments);/*执行当前函数并把当前函数的作用域强制设置为指定对象*/

}

}


这个方法将调用当前函数,并在由参数指定的对象上执行,从而把this绑定到该对象上。

然后应用这个函数扩展方法,以实现强制指定对象o的方法b中的this始终指向定义对象o。


var o={

name:"this=o"

}

o.b=(function{

return this;

}).pointTo(o);//把this绑定到对象o身上

var o1={

name:"this=o1",

b:o.b

}

var a=o1.b;

alert(a.name);//字符串"this=o",说明this的值没有发生变化


还可以扩展new运算符的替代方法,从而间接使用自定义函数实例化类。


//把构造函数转换为实例对象,参数f表示构造函数,返回构造函数f的实例对象

function instanceFrom(f){

var a=.slice.call(arguments,1);//获取构造函数的参数

f.prototype.constructor=f;//手工设置构造函数的原型构造器

f.apply(f.prototype,a);//在原型对象上强制执行构造函数

return f.prototype;//返回原型对象

}


例如,下面的示例演示了如何使用这个自定义的实例化类方法把一个简单的构造函数转换为具体的实例对象。


function F{

this.name="F";

}

var f=instanceFrom(F);

alert(f.name);


通过这个示例也进一步说明,call和apply方法具有强大的功能,它不仅能够执行普通函数,也能够实例化构造函数,具备new运算符的运算功能。