一般来说,一个运行期上下文的作用域链不会被改变,但是,有两种表达式可以在运行时临时改变运行期上下文作用域链。
(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子句中运行的唯一代码。此函数以适当方法自由地处理错误,并接收由错误产生的异常对象。由于只有一条语句,没有局部变量访问,作用域链临时改变就不会影响代码的性能。