每次将 i 与 array.length 进行比较时,循环都会检查 array.length 吗?

IT技术 javascript arrays
2021-03-19 02:32:43

我四处浏览,发现了这个

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

我的第一个想法是:

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

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

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<array.length; i++). 对于微优化,将数组的长度存储在临时变量中是有效的(另请参阅:在 JavaScript 中循环遍历数组的最快方法是什么?)。

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

var i = 0;
while (i < array.length) {
    ...
    i++;
}
但是为了存储长度而创建一个 var 值得吗?
2021-04-26 02:32:43
差异非常依赖于浏览器。在较旧的浏览器中,缓存更有效。要进行比较,请查看:jsperf.com/caching-array-length/4
2021-04-30 02:32:43
问题是属性查找。为了计算表达式B,每次迭代都需要在对象/数组上进行一次查找,这比在本地范围内查找要慢。如果可以在本地范围内找到某些内容,它始终是查找某些内容的最快方法,这就是为什么您要使用var foo =.
2021-05-11 02:32:43
@RobW:当然是的。新的 js 引擎将优化很多对象查找。引擎将具有内部查找哈希来访问属性。所以是的,这个微优化将对旧浏览器产生更大的影响(=== IE<9)
2021-05-20 02:32:43
这值得么?(显然是的,否则他为什么会这样做?)

绝对没错。因为,正如你所说,循环每次都会计算数组长度。所以这会造成巨大的开销。在您的 firebug 或 chrome 开发工具中运行以下代码片段。

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

// 一个分析器函数,它将以毫秒为单位返回给定函数的执行时间
var getExecutionTime = 函数(fn){
    var start = new Date().getTime();
    fn();
    var end = new Date().getTime();
    控制台日志(结束 - 开始);
}

var 优化 = 函数(){
    var newItems = [];
    for (var i = 0, len = items.length; i < len; i++) {
        newItems.push(items[i]);
    }
};


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

getExecutionTime(优化);
getExecutionTime(未优化);

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

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

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

您应该包括 Firefox 和 Chrome 的版本。这是一个非常详细的 JSPerf:jsperf.com/caching-array-length/4
2021-05-09 02:32:43

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

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

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

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

这样做的一个原因是,如果您在循环期间向数组添加元素但不想迭代它们。假设你想把 [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之前的数组大小进行比较,则循环将尝试访问超出新大小的元素。

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

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

让我们从下面的片段中理解这一点:

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);