为什么在nodejs的for循环中比var慢? [英] Why is let slower than var in a for loop in nodejs?

查看:245
本文介绍了为什么在nodejs的for循环中比var慢?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我写了一个非常简单的基准:

  console.time('var'); $ var $)


console.time('let );
for(let i = 0; i< 100000000; i ++){}
console.timeEnd('let')

如果您正在运行Chrome,可以在此处尝试(由于NodeJS和Chrome使用相同的JavaScript引擎,尽管通常略有不同):



  //由于Node在函数包装器中运行与全局代码不同的//`this`的代码,所以:( function(){console.time('var'); for(var i = 0; i< 100000000; i ++){} console.timeEnd('var')console.time('let'); for = 0; i <100000000; i ++){} console.timeEnd('let')})。call({});  

>



结果令我惊叹:

  var:89.162ms 
let:320.473ms

我已经在Node 4.0.0&& 5.0.0&& 6.0.0, var let 之间的比例对于每个节点版本是相同的。



有人可以向我解释这个看似奇怪的行为是什么原因?

解决方案

根据 var let 之间的差异,这与 var 存在于匿名函数的整个块范围内,而 let 只存在于循环中,并且必须为每次迭代重新声明。 sup> 1 下面是一个演示这一点的例子:



 (function() i = 0; i <5; i ++){setTimeout(function(){console.log(`i:$ {i} seconds`);},i * 1000);} // 5,5,5,5 ,5为(让j = 0; j <5; j ++){setTimeout(funct ion(){console.log(`j:$ {j} seconds`); },5000 + j * 1000); } // 0,1,2,3,4}());  



请注意, i 在循环的所有迭代中共享,而 let 不是。基于您的基准测试,似乎node.js没有优化 let 的优化范围规则,因为它比 var 是。



详细说明



这是一个小的外行人解释中为循环,对于那些不关心公认的密集规范的人来说,好奇如何重新声明为每次迭代,同时仍保持连续性。


但是 let 不能为每次迭代重新声明,因为如果在循环中更改它,它会传播到下一个迭代!


首先这个例子几乎似乎验证了这个潜在的反驳:



 (()的函数{for(let j = 0; j< 5; j ++){j ++; //看看它如何跳过0,2和4!? setTimeout(function(){console.log(`j:$ {j} seconds`);},j * 1000); }}());  



这些变化尊重 j 的连续性。但是,如Babel所示,它仍然被重新声明:



  function strict(function(){var _loop = function _loop(_j){_j ++; //这里是新范围内的更改setTimeout(function(){console.log(j:+ _j +seconds );},_j * 1000); j = _j; //这里是传播的变化以维持连续性}; for(var j = 0; j< 5; j ++){_loop(j);}}) );  



复杂的规则。毫无疑问,基准测试表现出如此巨大的差距(现在)。希望将来会进一步优化。






1:见这个折叠版本在Babel的REPL上看到这一点。当您在中声明变量循环时,会发生什么情况,就是创建一个新的声明性环境来保存该变量(详细信息),然后对于每个循环迭代 另一个声明环境被创建为保存变量的每个迭代副本;每个迭代的副本都是从上一个值初始化的(详细信息),但它们是单独的变量,如链接中由闭包输出的值所示。


I have written a very simple benchmark:

console.time('var');
for (var i = 0; i < 100000000; i++) {}
console.timeEnd('var')


console.time('let');
for (let i = 0; i < 100000000; i++) {}
console.timeEnd('let')

If you're running Chrome, you can try it here (since NodeJS and Chrome use the same JavaScript engine, albeit usually slightly different versions):

// Since Node runs code in a function wrapper with a different
// `this` than global code, do that:
(function() {
  console.time('var');
  for (var i = 0; i < 100000000; i++) {}
  console.timeEnd('var')


  console.time('let');
  for (let i = 0; i < 100000000; i++) {}
  console.timeEnd('let')
}).call({});

And the results amaze me:

var: 89.162ms
let: 320.473ms

I have tested it in Node 4.0.0 && 5.0.0 && 6.0.0 and the proportion between var and let is the same for each node version.

Could someone please explain to me what is the reason behid this seemingly odd behaviour?

解决方案

Based on the difference between the mechanics of var vs. let, it's related to the fact that var exists in the entire block scope of the anonymous function while let exists only within the loop and must be re-declared for each iteration.1 Here's an example demonstrating this point:

(function() {
  for (var i = 0; i < 5; i++) {
    setTimeout(function() {
      console.log(`i: ${i} seconds`);
    }, i * 1000);
  }
  // 5, 5, 5, 5, 5


  for (let j = 0; j < 5; j++) {
    setTimeout(function() {
      console.log(`j: ${j} seconds`);
    }, 5000 + j * 1000);
  }
  // 0, 1, 2, 3, 4
}());

Notice that the i is shared across all iterations of the loop while let is not. Based on your benchmark, it appears that node.js just hasn't optimized scoping rules for let since it's much more recent and complicated than var is.

Elaboration

Here's a little layman explanation of let in for loops, for those who don't care to look into the admittedly dense specs, but are curious how let is re-declared for each iteration while still maintaining continuity.

But let can't possibly be re-declared for each iteration, because if you change it inside the loop, it propagates to the next iteration!

First here's an example that almost appears to validate this potential counter-argument:

(function() {
  for (let j = 0; j < 5; j++) {
    j++; // see how it skips 0, 2, and 4!?!?
    setTimeout(function() {
      console.log(`j: ${j} seconds`);
    }, j * 1000);
  }
}());

You are partially right, in that the changes respect the continuity of j. However, it is still re-declared for each iteration, as demonstrated by Babel:

"use strict";

(function () {
  var _loop = function _loop(_j) {
    _j++; // here's the change inside the new scope
    setTimeout(function () {
      console.log("j: " + _j + " seconds");
    }, _j * 1000);
    j = _j; // here's the change being propagated back to maintain continuity
  };

  for (var j = 0; j < 5; j++) {
    _loop(j);
  }
})();

Like it was said. Complicated rules. It's no wonder that a benchmark shows such a large discrepancy in performance (for now). Hopefully it will be further optimized in the future.


1: See this transpiled version on Babel's REPL to see this demonstrated. What happens when you declare a let variable in a for loop like that is that a new declarative environment is created to hold that variable (details here), and then for each loop iteration another declarative environment is created to hold a per-iteration copy of the variable; each iteration's copy is initialized from the previous one's value (details here), but they're separate variables, as demonstrated in the link by the values output by the closures.

这篇关于为什么在nodejs的for循环中比var慢?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆