为什么在nodejs的for循环中放慢速度?

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

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') 

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

 // 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({}); 

结果令我惊叹:

 var: 89.162ms let: 320.473ms 

我已经在Node 4.0.0 && 5.0.0 && 6.0.0中testing了它, varlet之间的比例对于每个节点版本都是一样的。

请问有人可以解释一下这个看似奇怪的行为是什么原因?

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

 (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 }()); 

对于这个问题。 我试图find一些线索forms的铬V8源代码。 这里是V8循环剥离代码:

https://github.com/v8/v8/blob/5.4.156/src/compiler/loop-peeling.cc

我试着去理解它,我认为for循环在实现中有一个中间层。 for循环将在中间层保存增量值。

如果循环使用let声明“i”,V8会为每个循环迭代声明一个新的variablesi,将中间层增量variables的值复制到新声明的“i”中,然后将其放到循环体的范围中;

如果循环使用var来声明“i”,V8只会把中间层增量值引用到循环体的范围。 这会降低循环迭代的性能开销。

对不起,我的游泳池英语。 在v8源代码中有一个图表,它会告诉你这个机制。