是否真的需要优化JavaScript for循环?

fab*_*abb 17 javascript optimization performance loops for-loop

我读到建议通过在循环头中每次迭代读取数组的length属性来优化JavaScript 中的循环.

所以,我们应该这样做:

var names = ['George','Ringo','Paul','John'];
for(var i=0,j=names.length;i<j;i++){// Read array length once and assign it to a variable
    doSomeThingWith(names[i]);
}
Run Code Online (Sandbox Code Playgroud)

而不是这个:

var names = ['George','Ringo','Paul','John'];
for(var i=0;i<names.length;i++){
    doSomeThingWith(names[i]);
}
Run Code Online (Sandbox Code Playgroud)

但是,我创建了一个小的测试用例来比较这两种技术,但有时第一种情况更快,有时第二种情况更快.

你会推荐哪个版本?

jfr*_*d00 22

首先,我应该说这个答案是在2011年写的,随着时间的推移这些事情会发生变化(因为浏览器解释器会优化越来越多的东西)所以如果你真的想知道世界的当前状态,你必须在当前的浏览器上运行测试.

在任何版本的IE上运行您自己的jsperf测试.在那里,您将看到两种方法或许多其他旧浏览器之间的一致差异.你显然只在Chrome上运行它,它非常快速且经过优化,两种方法之间的差异可以忽略不计.在IE9上(这可能比IE7和IE8更好),预先缓存长度的方法快31%.

针对该问题设计的jsperf测试给出了该问题的定量结果.在这样的问题中,我们应该去jsperf看看真正的区别是什么,而不是那么多的猜测.

它显示了我尝试的浏览器的差异,从几乎没有差异到相当大的差异,具体取决于浏览器. 在Chrome中,几乎没有区别.在IE9中,首先存储长度几乎快50%.

现在,这种速度差异对您的脚本是否重要取决于具体的代码.如果你有一个庞大的阵列,你经常循环,它可以在一些浏览器中使用这种形式有一个有意义的区别:

for (var i = 0, len = list.length; i < len; i++) {
    // do code here
} 
Run Code Online (Sandbox Code Playgroud)

在使用某些DOM函数返回的实时伪数组的测试用例略有不同的情况下,速度仍有差异,但不是放大(我预计DOM伪实时数组的差异会更大,但事实并非如此).

在实践中,我倾向于使用短版本(更少打字)当我不认为我的代码部分是速度关键和/或数组不大而我会使用预先缓存长度的较长版本如果我我有意识地考虑速度或阵列是巨大的,或者我在同一阵列上进行了大量的迭代.

预缓存长度有几个其他编程原因.如果您将在循环期间向元素的末尾添加元素,并且您不希望循环迭代这些新添加的元素,那么您将需要预加载长度并且仅迭代最初存在的元素.

for (var i = 0, len = list.length; i < len; i++) {
    if (list[i] == "whatever") {
        list.push("something");
    }
} 
Run Code Online (Sandbox Code Playgroud)

请记住,浏览器不断发展并添加越来越多的优化,因此在2011年显示出巨大优势的优化可能在未来基本上构建到更现代的浏览器中,因此不再需要手动编码优化.所以,如果你想为今天的表现优化某些东西,你必须在今天的浏览器中进行测试,你不能只依赖你读过的可能有几年的东西.


Ned*_*der 13

这个建议总是微观优化,并且所有的工作都是在Javascript引擎的速度上完成的,它不再是一个可衡量的差异.也许在一个非常长的非常紧凑的循环中它可能会有所作为,但我怀疑它.

出于某种原因,程序员倾向于专注于速度高于一切,即使它是无根据的.考虑正确性,然后考虑可读性.

  • 50%的是什么?你在循环中有什么真实的事吗?我仍然声称这种差异对于实际的Javascript程序来说很重要. (2认同)

Pet*_*son 5

我会推荐第二个:

var names = ['George','Ringo','Paul','John'];
for (var i = 0; i < names.length; i++) {
  doSomeThingWith(names[i]);
}
Run Code Online (Sandbox Code Playgroud)

因为它更简洁,更具惯用性.除非你做了一些荒谬的微优化,否则你将不需要使用第一个.

  • 请参阅下面的答案中的jsperf测试.在某些浏览器中,这不是一个荒谬的微优化(IE9可以达到50%的速度差异),因此它取决于确切的上下文.我不会认为它从来没有相关性.这取决于速度差异的相关程度. (4认同)

DwB*_*DwB 5

作为一般规则,缓存循环的"停止值"(在您的情况下为names.length)只有在计算值时才有价值.对于有问题的数组,它只是一个查找,所以你通过缓存它将获得很少.