【问题标题】:Do loops check the array.length every time when comparing i against array.length?每次将 i 与 array.length 进行比较时,循环是否都会检查 array.length?
【发布时间】:2026-01-16 21:20:04
【问题描述】:

我正在浏览,我found this

var i, len;
for(i = 0, len = array.length; i < len; i++) {  
   //...
}

我的第一个想法是:

  • 他为什么这样做? (由于某种原因它必须更好)
  • 值得吗? (我想是的,否则他为什么会这样做?)

正常循环(不缓存长度的循环)是否每次都检查array.length

【问题讨论】:

  • 在不编译代码的旧浏览器中缓存长度可能更重要。如果我记得的话,Chrome 很好地优化了这一点。
  • @RightSaidFred - Chrome 不会优化它,因为它不能(长度可以在循环内改变),但它足够快,两种方式都没有很大的速度差异。
  • @jfriend00:因为是编译后的代码,看来Chrome可以知道Array是否在循环中被修改,如果没有则进行优化。但我当然同意,对于 Chrome,这基本上是一个有争议的问题。
  • @RightSaidFred - 也许如果循环中没有函数调用,循环中没有带有访问器的属性,并且循环中没有调用数组上的方法,等等......但是,这很难JS 知道什么可能会或可能不会间接修改数组的长度。我认为 Chrome 的速度已经足够快,以至于这些类型的优化不会像以前那样产生太大的影响。
  • @jfriend00:是的,我完全听到你在说什么。我经常想知道 Chrome 能够在多大程度上遵循代码并进行优化。有几次我通过 Google Closure Compiler 中的高级优化运行代码,我对它解开的代码感到非常惊讶。不知道它是如何发挥作用的。

标签: javascript arrays


【解决方案1】:

是的 它每次都检查array.length,但是如果我们不想为增加的数组运行循环,那么在这种情况下,我们可以使用forEach方法。

forEach 是一个 javascript in build 方法,它确实类似于 for 循环,但 forEach 只迭代循环开始之前的数组中的元素。

让我们从下面的sn-p来理解这个:

array = [1,2,3,4]

for(i=0;i<=array.length;i++){
console.log(array[i]);
array.push(array[i]+1);
}

输出喜欢 1 2 3 4 5 6 ...等等(无限),而它每次检查array.length

让我们检查一下 forEach 方法

array = [1,2,3,4]
array.forEach((element) => {
console.log(element);
array.push(element+1);
})
console.log("array elements after loop",array);

它只处理迭代开始之前存在于数组中的 4 个元素。

**但是在forEach的情况下,如果我们从数组中弹出元素,它会影响array.length。

让我们通过一个例子来看看:

array = [1,2,3,4]
array.forEach((element) => {
console.log(element);
array.pop()
})
console.log("array elements after loop",array);

【讨论】:

    【解决方案2】:

    我一直认为在 JavaScript 中长度只是数组对象的一个​​属性,由先前的数组操作(创建、添加、删除)预先计算出来,或者被用户覆盖,所以你只是在查找一个变量吗?我必须承认,由于缺少括号,我刚刚假设,但是查看 array.length 的 MDN 页面,它似乎说了同样的话。

    在长度是一种方法或长度由标准库函数计算的语言中,您应该在运行循环之前预先计算长度,这样就不会每次迭代都计算数组,尤其是对于大型数据集。即便如此,在 Python 等现代高级语言中,len() 仍然只是返回数组对象的长度属性。

    因此,除非我弄错了,否则复杂度只是 O(1),从这个角度来看,即使变量比每次查找的属性稍快,也不值得为潜在的麻烦创建/重用保护性 for 循环范围之外的其他变量。

    但是,我怀疑在这种情况下,示例的程序员选择这种方法的原因仅仅是他们在另一种语言中养成并继承了 JavaScript 的习惯。

    【讨论】:

      【解决方案3】:

      这样做的一个原因是,如果您在循环期间向数组添加元素,但不想迭代它们。假设你想把 [1, 2, 3] 变成 [1, 2, 3, 1, 2, 3]。你可以这样做:

      var initialLength = items.length;
      for(var i=0; i<initialLength; ++i) {
          items.push(items[i]);
      }
      

      如果你在循环之前不保存长度,那么 array.length 将不断增加并且循环将一直运行直到浏览器崩溃/杀死它。

      除此之外,正如其他人所说,它会轻微影响性能。我不会养成这样做的习惯,因为“过早的优化是万恶之源”。另外,如果您在循环期间更改数组的大小,这样做可能会破坏您的代码。例如,如果您在循环期间从数组中删除元素,但不断将 i 与之前的数组大小进行比较,则循环将尝试访问超出新大小的元素。

      【讨论】:

        【解决方案4】:

        这里有一些针对不同方法的性能测试

        http://www.websiteoptimization.com/speed/10/

        【讨论】:

          【解决方案5】:
          值得吗? (显然是的,不然他为什么会这样?)

          绝对是的。因为,正如您所说,循环每次都会计算数组长度。所以这将导致巨大的开销。在你的 firebug 或 chrome 开发工具中运行以下代码 sn-ps vs.

          // 创建一个包含 50.000 个项目的数组 (功能(){ window.items = []; for (var i = 0; i

          这是在各种浏览器中的大致结果

          浏览器优化未优化 火狐 14 26 铬 15 32 IE9 22 40 IE8 82 157 IE7 76 148

          所以再考虑一下,并使用优化的方式 :)
          注意: 我尝试在 jsPerf 上使用此代码,但我现在无法访问 jsPerf。我想,我尝试的时候它已经关闭了。

          【讨论】:

          【解决方案6】:

          一个由三部分组成的循环执行如下:

          for (A; B; C)
          
          A - Executed before the enumeration
          B - condition to test
          C - expression after each enumeration (so, not if B evaluated to false)
          

          所以,是的:如果数组的.length 构造为for(var i=0; i&lt;array.length; i++),则在每次枚举时检查它的属性。对于微优化,将数组的长度存储在临时变量中是有效的(另请参阅:What's the fastest way to loop through an array in JavaScript?)。

          相当于for (var i=0; i&lt;array.length; i++) { ... }:

          var i = 0;
          while (i < array.length) {
              ...
              i++;
          }
          

          【讨论】:

          • 问题是属性查找。为了计算表达式B,每次迭代都需要对对象/数组进行一次查找,这比使用本地范围查找要慢。如果可以在本地范围内找到某些内容,它始终是查找内容的最快方法,这就是为什么您将使用 var foo = 将长度存储在当前范围内的原因。
          • 差别很大,取决于浏览器。在较旧的浏览器中,缓存更有效。如需比较,请查看:jsperf.com/caching-array-length/4
          • @RobW:当然可以。 new'ish js 引擎将优化对象查找。引擎将具有内部查找哈希来访问属性。所以是的,这种微优化将对旧版浏览器产生更大的影响(=== IE
          最近更新 更多