这话我听过好几次了。JavaScript循环在向后计数时真的更快吗?如果有,为什么?我已经看到了一些测试套件示例,它们显示反向循环更快,但我找不到任何解释!
我假设这是因为循环不再需要在每次检查属性时评估它是否已经完成,它只检查最终的数值。
即。
for (var i = count - 1; i >= 0; i--)
{
// count is only evaluated once and then the comparison is always on 0.
}
这话我听过好几次了。JavaScript循环在向后计数时真的更快吗?如果有,为什么?我已经看到了一些测试套件示例,它们显示反向循环更快,但我找不到任何解释!
我假设这是因为循环不再需要在每次检查属性时评估它是否已经完成,它只检查最终的数值。
即。
for (var i = count - 1; i >= 0; i--)
{
// count is only evaluated once and then the comparison is always on 0.
}
当前回答
回答这类问题的最好方法就是亲自去尝试。建立一个计算百万次迭代的循环,并以两种方式进行。对两个循环进行计时,并比较结果。
答案可能取决于您使用的浏览器。有些会有不同的结果。
其他回答
在许多情况下,这本质上与处理器比其他比较更快地比较0这一事实无关。
这是因为只有少数Javascript引擎(JIT列表中的那些)真正生成机器语言代码。
大多数Javascript引擎构建源代码的内部表示,然后对其进行解释(要了解这是什么样子,请查看Firefox的SpiderMonkey页面底部附近)。一般来说,如果一段代码实际上做同样的事情,但导致更简单的内部表示,它将运行得更快。
请记住,对于简单的任务,如从一个变量中添加/减去一个,或将一个变量与某个东西进行比较,解释器从一个内部“指令”移动到下一个“指令”的开销是相当高的,因此JS引擎内部使用的“指令”越少越好。
并不是说i比i++快。实际上,它们都一样快。
在升序循环中需要花费时间的是,对于每个i,计算数组的大小。在这个循环中:
for(var i = array.length; i--;)
当你声明i时,你只对.length求值一次,而对于这个循环
for(var i = 1; i <= array.length; i++)
当你检查i是否<= array.length时,你每次增加i就计算.length。
在大多数情况下,您甚至不应该担心这种优化。
我不认为在JavaScript中说I比i++更快是有意义的。
首先,它完全依赖于JavaScript引擎实现。
其次,如果最简单的构造被JIT化并转换为本机指令,那么i++ vs i——将完全依赖于执行它的CPU。也就是说,在arm(移动电话)上,下降到0更快,因为递减和比较0是在一条指令中执行的。
也许,你认为一个比另一个更浪费,因为建议的方式是
for(var i = array.length; i--; )
但建议的方式不是因为一个比另一个快,而只是因为如果你写
for(var i = 0; i < array.length; i++)
然后是每个迭代数组。length必须被计算(更聪明的JavaScript引擎可能会发现循环不会改变数组的长度)。尽管它看起来像一个简单的语句,但它实际上是JavaScript引擎在底层调用的某个函数。
The other reason, why i-- could be considered "faster" is because JavaScript engine needs to allocate only one internal variable to control the loop (variable to the var i). If you compared to array.length or to some other variable then there had to be more than one internal variable to control the loop, and the number of internal variables are limited asset of a JavaScript engine. The less variables are used in a loop the more chance JIT has for optimization. That's why i-- could be considered faster...
既然你对这个主题感兴趣,可以看看Greg Reimer关于JavaScript循环基准的博客文章,用JavaScript编写循环的最快方法是什么?:
我为JavaScript中不同的循环编码方式构建了一个循环基准测试套件。现在已经有一些这样的工具了,但是我还没有找到任何一个能够区分原生数组和HTML集合的工具。
您还可以通过打开https://blogs.oracle.com/greimer/resource/loop-test.html对循环进行性能测试(如果JavaScript在浏览器中被NoScript阻塞,则无法工作)。
编辑:
Milan Adamovsky最近创建的基准测试可以在运行时针对不同的浏览器执行。
在Mac OS X 10.6上的Firefox 17.0测试中,我得到了以下循环:
有时,对代码的编写方式做一些非常小的更改,可能会对代码的实际运行速度产生很大的影响。一个微小的代码更改可能会对执行时间产生很大影响的地方是我们有一个处理数组的for循环。当数组是网页上的元素(如单选按钮)时,这种改变的影响最大,但即使数组是Javascript代码的内部,也值得应用这种改变。
编写for循环来处理数组的常规方法如下:
for (var i = 0; i < myArray.length; i++) {...
The problem with this is that evaluating the length of the array using myArray.length takes time and the way that we have coded the loop means that this evaluation has to be performed every time around the loop. If the array contains 1000 elements then the length of the array will be evaluated 1001 times. If we were looking at radio buttons and had myForm.myButtons.length then it will take even longer to evaluate since the appropriate group of buttons within the specified form must first be located before the length can be evaluated each time around the loop.
显然,我们不希望数组的长度在处理时发生变化,因此所有这些重新计算长度的操作只是不必要地增加了处理时间。(当然,如果你在循环中有添加或删除数组条目的代码,那么数组大小可以在迭代之间改变,所以我们不能改变测试它的代码)
对于一个大小固定的循环,我们能做的就是在循环开始时计算长度,并将其保存在一个变量中。然后,我们可以测试变量以决定何时终止循环。这比每次计算数组长度要快得多,特别是当数组包含多个条目或是网页的一部分时。
这样做的代码是:
for (var i = 0, var j = myArray.length; i < j; i++) {...
因此,现在我们只计算数组的大小一次,并在每次循环中测试循环计数器是否包含该值的变量。访问这个额外的变量比计算数组的大小要快得多,因此我们的代码将比以前运行得快得多。我们的脚本中只有一个额外的变量。
通常情况下,处理数组的顺序并不重要,只要数组中的所有条目都得到了处理。在这种情况下,我们可以通过去掉刚刚添加的额外变量并以相反的顺序处理数组来使代码稍微快一些。
以最有效的方式处理数组的最终代码是:
for (var i = myArray.length-1; i > -1; i--) {...
这段代码仍然只在开始时计算一次数组的大小,但不是将循环计数器与变量进行比较,而是将其与常量进行比较。由于访问常量比访问变量更有效,并且由于我们的赋值语句比以前少了一个,所以第三个版本的代码现在比第二个版本的效率略高,比第一个版本的效率要高得多。