【问题标题】:What's the best way to iterate over items in javascript?在javascript中迭代项目的最佳方法是什么?
【发布时间】:2012-09-10 18:30:14
【问题描述】:

我最近遇到了一种在 Javascript 中循环数组的不同方式。

我曾经写过这样的循环:

for (var len = 0; len < testData.length; len++) {
  total = total + testData[len];
}

我读过一些这样的代码:

for (var len = 0; testData[len]; len++) {
  total = total + testData[len];
}

我想知道这些会如何执行,所以我使用了jsPerf 来找出答案。结果非常惊人。我预计第二种方法会比第一种方法快一点,但实际上要快得多。

我在这里有什么缺点吗?或者这是遍历列表项的最佳方式。

更新:

灰色状态即将来临Diode 指出了测试用例中的一个简单缺陷,它似乎更快。

改错后,这个是最快的:

var datalen = testData.length;
for (var len = 0; len <datalen; len++) {
     total = total + testData[len];
}

更新 2:

在更多浏览器中进行测试后,这个测试用例再次采取了不同的方向。 只有在 Chrome 和 Opera 中,正常的 for 循环是最快的。在其他所有其他浏览器中 Shmiddty 的方式只是快一点。

var i = testData.length, sum=0;
while (i--){
    sum += testData[i];
}

【问题讨论】:

  • 如果你想给一个变量添加一个值,你应该使用+=而不是重复这个变量。
  • 当你说“最好”时,你在寻找什么?速度、代码可读性、可靠性?你在循环什么样的数组?
  • 如果testData 中有虚假值,那么第二个循环将提前停止。这可能是您看到的“性能提升”的一部分。
  • 但是您的 JSPerf 测试存在很大缺陷。它似乎是如此之快,因为它在第一次迭代时因为0 值而停止。将初始化更改为以1 开头而不是0,然后进行比较。 for (var i = 1; i &lt; 1000; i++) {
  • 更正的 JSPerf 测试显示了一个非常不同的结果。 jsperf.com/for-until-length-vs-until-undefined/7

标签: javascript for-loop undefined


【解决方案1】:

我认为第一种形式更好。第二种形式有一些问题。如果您有一个包含falsy 值的稀疏数组怎么办?点赞:var testData = [ "text", undefined, false, 0, 5 ];

我也希望第一种形式表现更好。特别是如果你“缓存”testData.length 的值,就像这样:

var i, len = testData.length;
for (i = 0; i < len; i += 1) {
  total = total + testData[i];
}

【讨论】:

  • 缓存变量的好主意,但我仍然认为一个简单的空/假检查,因为 testData[len] 更快。 +1 评论虚假值。
  • 为避免上述问题,您可以使用条件 (testData[len] != void 0)。这对于所有定义的值都是正确的。
  • @ParitoshSingh undefined === void 0 的问题仍然存在。
  • @ParitoshSingh:您可以使用undefined 值定义属性。但是除此之外,如果要进行这样的比较,那么您不妨将索引与长度进行比较。
  • 在 jsPerf 测试用例中,我比较了缓存和未缓存版本的长度。缓存长度确实有利于速度。
【解决方案2】:


更新:我错了,forEach 很慢!似乎循环更好


您应该使用forEach,它在 ECMAScript 第 5 版中定义。

testData.forEach(function(val,i,arr){
    total += val;
});

参数是:

  • val 是当前值
  • i 是当前索引
  • arr 是数组

您不必全部使用:

testData.forEach(function(val){
    total += val;
});

对于不支持它的浏览器,可以使用这个 shim:

if(!Array.prototype.forEach){
    Array.prototype.forEach = function(fn, scope) {
        for(var i = 0, len = this.length; i < len; ++i) {
            fn.call(scope || this, this[i], i, this);
        }
    }
}

更多信息请参见Array.forEach

【讨论】:

  • 我完全忘记了 forEach。我将它添加到 jsPerf。虽然这是一个显而易见的解决方案,但事实证明它是最慢的迭代方式。
  • @Sorskoot Weird... 我认为它应该更快,因为它是本机代码。也许是因为它需要一个函数,所以作用域链增长了。奇怪的是我正在使用forEach开始一个项目,我将其更改为循环,感谢您的jsperf比较
  • @Oriol:与作用域链无关;范围在 JavaScript 中是静态的。 forEach 的问题在于函数调用不是免费的——事实上,正如你所看到的,它非常昂贵。
【解决方案3】:

你也可以这样循环:

var i = testData.length;

while (i--){
    console.log(testData[i]);
}

注意它遍历数组向后

或者,总结一下:

var i = testData.length, sum=0;

while (i--){
    sum += testData[i];
}

【讨论】:

  • 我将它添加到了测试用例中。令人惊讶的是,常规的 for 循环更快。
  • 我尝试了其他一些浏览器,在某些浏览器中这种方式实际上是最快的......
  • 这些测试用例使用长度为 1000 的数组运行。如果列表明显较短,则测试结果会发生变化。我无法解释这些性能结果。
  • 可能是由于每个 javascript 引擎的优化不同。
【解决方案4】:

根据 Shmiddt 的回答,您可以向后使用循环。似乎它在 Firefox 上比倒退快一点,在 Chrome 上它们并列:

var total = 0;
for (var len = testData.length-1; len >=0; len--) {
  total += testData[len];
}

测试:http://jsperf.com/for-until-length-vs-until-undefined/10

【讨论】:

    【解决方案5】:

    关于性能,testData[len] 只检查元素是否已定义,而不是 null/false/“falsish”,这比比较要快,尤其是每次迭代都检索 testData.length。

    因此,第二种方式更快,但并不更可靠——正如 Frits van Campen 所说的那样。

    【讨论】:

      【解决方案6】:

      您的设置代码插入 0,这会导致循环在第一个条件下中断

      var testData = [];
      for (var i = 0; i < 1000; i++) {
        testData.push(i);  // pushes 0 to `testData`
      }
      
      
      for (var len = 0; testData[len]; len++) {   // loop breaks on first condition-check as testData[0] is 0.
        total = total + testData[len];
      }
      

      如下所示更改设置代码并查看差异

      var testData = [];
      for (var i = 0; i < 1000; i++) {
        testData.push(i+1);
      }
      

      http://jsperf.com/for-until-length-vs-until-undefined/6

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-05-30
        • 1970-01-01
        • 2016-05-05
        • 1970-01-01
        • 2017-11-21
        • 1970-01-01
        相关资源
        最近更新 更多