8

http://jsperf.com/loops/67

如果你看,下面的循环管理一些疯狂的基准:

var i=0;
var v;
for (i, v; v = arr[i++];) {
   v;
}

它在 FF 中的得分约为 7 亿次/秒,在 Chrome 中约为 2000 万次,在 IE10 中约为 5000 万次。下一个最快的循环在 FF 中管理约 100k,在 IE10 中管理约 6k,在 Chrome 中仅管理约 2k。

为什么这么快?我可以看到其他循环之间的明显差异以及一个比另一个更快的差异,但我无法想出任何可以解释这个循环绝对令人兴奋的性能差异的东西,7 亿到 100k 是一个疯狂的差距。


回答后编辑:

根据@Michael Gary 的回答,我回过头来编辑设置以包含一个实际的真实数组,结果又回到了现实:http: //jsperf.com/loops/70

4

2 回答 2

7

原因很简单。arr使用以下代码创建数组:

var arr = new Array(10000);

所以它的长度为 10000,但所有元素都是undefined. 此循环不适用于数组长度,但会在遇到“虚假”值时终止 - 假设由于尝试读取数组末尾而v接收到值时循环将停止。undefined

但是在这个特定的数组中,所有一万个元素都有值undefined。因此,循环在测试数组的第一个元素时停止。换句话说,它根本不循环!难怪它很快。

但是更真实的案例呢?这种循环如何处理冗长的 JSON 对象数组:

[
    { "id": 507674, "name": "Kolink" },
    { "id": 997356, "name": "DarkLord7854" },
    { "id": 1202830, "name": "Michael Geary" },
    /* and thousands more */
]

在这里,您不会遇到循环立即终止的问题,因为数组元素都是“真实的”。

使用现代 JavaScript 引擎,这被证明是编写循环的一种相当糟糕的方式,因为我最近发现让我非常尴尬。

我是jQuery Cookbook的作者之一:我编写了第 5 章“更快、更简单、更有趣”的大部分内容。好吧,“更快”的部分并没有那么好。我推荐了一个非常像您的循环来迭代大量对象,例如上面的对象:

for( var item, i = -1;  item = array[++i]; ) {
    // do stuff with item
}

事实证明,在现代浏览器中,这比像这样的传统循环要慢得多:

for( var i = 0, n = array.length;  i < n;  i++ ) {
    var item = array[i];
    // do stuff with item
}

部分原因是因为试图读取数组末尾的内容会使一些 JavaScript 引擎重新回到未优化的数组表示方式中,正如 V8 作者之一在去年的 Google I/O 上向我解释的那样。部分原因可能是浏览器优化了更常见的循环类型,而不是优化了这种不太常见的方法。

无论哪种方式,更传统的循环在现代浏览器中变得更快:

http://jsperf.com/mikes-loops/2

但这与您的循环不同。在你的情况下,疯狂的性能提升直接是因为它根本不运行循环。:-)

于 2013-04-07T03:24:52.917 回答
3

arr被初始化为一个包含 10000 批臭虫的数组。Array(10000)准备数组的长度,但不以任何方式填充它。

因此,arr[0]will beundefined是假的,所以 for 循环立即终止。

本质上,代码归结为:

var i=0;
var v;
i,v; // doesn't do anything but access the variables
v = undefined;
i++;
于 2013-04-07T03:24:31.017 回答