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

《编写高质量代码:改善JavaScript程序的188个建议》建议153:警惕人为改变作用域链

关灯直达底部

一般来说,一个运行期上下文的作用域链不会被改变,但是,有两种表达式可以在运行时临时改变运行期上下文作用域链。

(1)with

with表达式为所有对象属性创建一个默认操作变量。在其他语言中,类似的功能通常用来避免书写一些重复的代码。initUI函数可以重写成如下形式:


function initUI{

with(document){

var bd=body,links=getElementsByTagName_r("a"),i=0,len=links.length;

while(i<len){

update(links[i++]);

}

getElementById("go-btn").onclick=function{

start;

};

bd.className="active";

}

}


在上面代码中,重写的initUI函数使用了一个with表达式,避免了多次书写document。这样似乎更有效率,实际上却产生了一个性能问题。

当代码流执行到一个with表达式时,运行期上下文的作用域链被临时改变了。一个新的可变对象将被创建,它包含指定对象的所有属性。此对象被插入到作用域链的前端,这意味着现在函数的所有局部变量都被推入第二个作用域链对象中,所以访问代价更高了。

通过将document对象传递给with表达式,一个新的可变对象容纳了document对象的所有属性,被插入到作用域链的前端。这使得访问document对象的速度非常快,但访问局部变量的速度却变慢了,如bd变量。正因为如此,最好不要使用with表达式,只要简单地将document存储在一个局部变量中,就可以获得性能上的提升。

(2)catch

在JavaScript中,不只是with表达式人为地改变运行期上下文的作用域链,try catch表达式的catch子句也具有相同效果。当try块发生错误时,程序流程自动转入catch块,并将异常对象推入作用域链前端的一个可变对象中。在catch块中,函数的所有局部变量现在被放在第二个作用域链对象中,例如:


try{

methodThatMightCauseAnError;

}catch(ex){

alert(ex.message);

}


注意,只要catch子句执行完毕,作用域链就会返回到原来的状态。如果使用得当,那么try catch表达式将是非常有用的语句,所以不建议完全避免使用try catch语句。如果计划使用一个try catch语句,那么一定要确保了解可能发生的错误。一个try catch语句不应该作为解决JavaScript错误的办法。如果一个错误会经常发生,那说明应当修正代码本身的问题。

可以通过精减代码的办法将catch子句对性能的影响降至最低。一个好的模式是将错误交给一个专用函数来处理,例如:


try{

methodThatMightCauseAnError;

}catch(ex){

handleError(ex);

}


handleError函数是catch子句中运行的唯一代码。此函数以适当方法自由地处理错误,并接收由错误产生的异常对象。由于只有一条语句,没有局部变量访问,作用域链临时改变就不会影响代码的性能。