[翻译]High Performance JavaScript(006)

Identifier Resolution Performance  标识符识别性能

 

    Identifier resolution isn't free, as in fact no computer operation really is without some sort of performance overhead. The deeper into the execution context's scope chain an identifier exists, the slower it is to access for both reads and writes. Consequently, local variables are always the fastest to access inside of a function, whereas global variables will generally be the slowest (optimizing JavaScript engines are capable of tuning this in certain situations). Keep in mind that global variables always exist in the last variable object of the execution context's scope chain, so they are always the furthest away to resolve. Figures 2-4 and 2-5 show the speed of identifier resolution based on their depth in the scope chain. A depth of 1 indicates a local variable.

    标识符识别不是免费的,事实上没有哪种电脑操作可以不产生性能开销。在运行期上下文的作用域链中,一个标识符所处的位置越深,它的读写速度就越慢。所以,函数中局部变量的访问速度总是最快的,而全局变量通常是最慢的(优化的JavaScript引擎在某些情况下可以改变这种状况)。请记住,全局变量总是处于运行期上下文作用域链的最后一个位置,所以总是最远才能触及的。图2-4和2-5显示了作用域链上不同深度标识符的识别速度,深度为1表示一个局部变量。

 

Figure 2-4. Identifier resolution for write operations

图2-4  写操作的标识符识别速度

Figure 2-5. Identifier resolution for read operations

图2-5  读操作的标识符识别速度

 

    The general trend across all browsers is that the deeper into the scope chain an identifier exists, the slower it will be read from or written to. Browsers with optimizing JavaScript engines, such as Chrome and Safari 4, don't have this sort of performance penalty for accessing out-of-scope identifiers, whereas Internet Explorer, Safari 3.2, and others show a more drastic effect. It's worth noting that earlier browsers, such as Internet Explorer 6 and Firefox 2, had incredibly steep slopes and would not even appear within the bounds of this graph at the high point if their data had been included.

    总的趋势是,对所有浏览器来说,一个标识符所处的位置越深,读写它的速度就越慢。采用优化的JavaScript引擎的浏览器,如Safari 4,访问域外标识符时没有这种性能损失,而Internet Explorer,Safari 3.2,和其他浏览器则有较大幅度的影响。值得注意的是,早期浏览器如Internet Explorer 6和Firefox 2,有令人难以置信的陡峭斜坡,如果此图包含它们的数据,曲线高点将超出图表边界。

 

    Given this information, it's advisable to use local variables whenever possible to improve performance in browsers without optimizing JavaScript engines. A good rule of thumb is to always store out-of-scope values in local variables if they are used more than once within a function. Consider the following example:

    通过以上信息,在没有优化JavaScript引擎的浏览器中,最好尽可能使用局部变量。一个好的经验法则是:用局部变量存储本地范围之外的变量值,如果它们在函数中的使用多于一次。考虑下面的例子:

 

function initUI(){
  var bd = document.body,
  links = document.getElementsByTagName("a"),
  i = 0,
  len = links.length;
  while(i < len){
    update(links[i++]);
  }
  document.getElementById("go-btn").onclick = function(){
    start();
  };
  bd.className = "active";
}

 

    This function contains three references to document, which is a global object. The search for this variable must go all the way through the scope chain before finally being resolved in the global variable object. You can mitigate the performance impact of repeated global variable access by first storing the reference in a local variable and then using the local variable instead of the global. For example, the previous code can be rewritten as follows:

    此函数包含三个对document的引用,document是一个全局对象。搜索此变量,必须遍历整个作用域链,直到最后在全局变量对象中找到它。你可以通过这种方法减轻重复的全局变量访问对性能的影响:首先将全局变量的引用存储在一个局部变量中,然后使用这个局部变量代替全局变量。例如,上面的代码可以重写如下:

 

function initUI(){
  var doc = document,
  bd = doc.body,
  links = doc.getElementsByTagName("a"),
  i = 0,
  len = links.length;
  while(i < len){
    update(links[i++]);
  }
  doc.getElementById("go-btn").onclick = function(){
    start();
  };
  bd.className = "active";
}

 

    The updated version of initUI() first stores a reference to document in the local doc variable. Instead of accessing a global variables three times, that number is cut down to one. Accessing doc instead of document is faster because it's a local variable. Of course, this simplistic function won't show a huge performance improvement, because it's not doing that much, but imagine larger functions with dozens of global variables being accessed repeatedly; that is where the more impressive performance improvements will be found.

    initUI()的新版本首先将document的引用存入局部变量doc中。现在访问全局变量的次数是1次,而不是3次。用doc替代document更快,因为它是一个局部变量。当然,这个简单的函数不会显示出巨大的性能改进,因为数量的原因,不过可以想象一下,如果几十个全局变量被反复访问,那么性能改进将显得多么出色。

 

Scope Chain Augmentation  改变作用域链

 

    Generally speaking, an execution context's scope chain doesn't change. There are, however, two statements that temporarily augment the execution context's scope chain while it is being executed. The first of these is with.

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

 

    The with statement is used to create variables for all of an object's properties. This mimics other languages with similar features and is usually seen as a convenience to avoid writing the same code repeatedly. The initUI() function can be written as the following:

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

 

function initUI(){
  with (document){ //avoid!
    var bd = body,
    links = getElementsByTagName("a"),
    i = 0,
    len = links.length;
    while(i < len){
      update(links[i++]);
    }
    getElementById("go-btn").onclick = function(){
      start();
    };
    bd.className = "active";
  }
}

 

    This rewritten version of initUI() uses a with statement to avoid writing document elsewhere. Though this may seem more efficient, it actually creates a performance problem.

    此重写的initUI()版本使用了一个with表达式,避免多次书写“document”。这看起来似乎更有效率,而实际上却产生了一个性能问题。

 

    When code execution flows into a with statement, the execution context's scope chain is temporarily augmented. A new variable object is created containing all of the properties of the specified object. That object is then pushed to the front of the scope chain, meaning that all of the function's local variables are now in the second scope chain object and are therefore more expensive to access (see Figure 2-6).

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

Figure 2-6. Augmented scope chain in a with statement

图2-6  with表达式改变作用域链

 

    By passing the document object into the with statement, a new variable object containing all of the document object's properties is pushed to the front of the scope chain. This makes it very fast to access document properties but slower to access the local variables such as bd. For this reason, it's best to avoid using the with statement. As shown previously, it's just as easy to store document in a local variable and get the performance improvement that way.

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

 

    The with statement isn't the only part of JavaScript that artificially augments the execution context's scope chain; the catch clause of the try-catch statement has the same effect. When an error occurs in the try block, execution automatically flows to the catch and the exception object is pushed into a variable object that is then placed at the front of the scope chain. Inside of the catch block, all variables local to the function are now in the second scope chain object. For example:

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

 

try {
  methodThatMightCauseAnError();
} catch (ex){
  alert(ex.message); //scope chain is augmented here
}

 

    Note that as soon as the catch clause is finished executing, the scope chain returns to its previous state.

    请注意,只要catch子句执行完毕,作用域链就会返回到原来的状态。

 

    The try-catch statement is very useful when applied appropriately, and so it doesn't make sense to suggest complete avoidance. If you do plan on using a try-catch, make sure that you understand the likelihood of error. A try-catch should never be used as the solution to a JavaScript error. If you know an error will occur frequently, then that indicates a problem with the code itself that should be fixed.

    如果使用得当,try-catch表达式是非常有用的语句,所以不建议完全避免。如果你计划使用一个try-catch语句,请确保你了解可能发生的错误。一个try-catch语句不应该作为JavaScript错误的解决办法。如果你知道一个错误会经常发生,那说明应当修正代码本身的问题。

 

    You can minimize the performance impact of the catch clause by executing as little code as necessary within it. A good pattern is to have a method for handling errors that the catch clause can delegate to, as in this example:

    你可以通过精缩代码的办法最小化catch子句对性能的影响。一个很好的模式是将错误交给一个专用函数来处理。例子如下:

 

try {
  methodThatMightCauseAnError();
} catch (ex){
  handleError(ex); //delegate to handler method
}

 

    Here a handleError() method is the only code that is executed in the catch clause. This method is free to handle the error in an appropriate way and is passed the exception object generated from the error. Since there is just one statement executed and no local variables accessed, the temporary scope chain augmentation does not affect the performance of the code.

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值