JavaScript - myArray.forEach 与 for 循环的细微差别

IT技术 javascript arrays for-loop internet-explorer-8 iterator
2021-03-06 18:04:01

我已经看到很多问题建议使用:

for (var i = 0; i < myArray.length; i++){ /* ... */ }

代替:

for (var i in myArray){ /* ... */ }

对于数组,由于迭代不一致(请参阅此处)。


但是,我似乎找不到任何似乎更喜欢面向对象循环的东西:

myArray.forEach(function(item, index){ /* ... */ });

这对我来说似乎更直观。

对于我当前的项目,IE8 兼容性很重要,我正在考虑使用Mozilla 的 polyfill,但是我不是 100% 确定这将如何工作。

  • 标准 for 循环(上面的第一个示例)和现代浏览器的 Array.prototype.forEach 实现之间有什么区别吗?
  • 现代浏览器实现与上面链接的 Mozilla 实现之间有什么区别(特别是 IE8)?
  • 性能不是一个问题,只是与迭代哪些属性的一致性。
3个回答

for循环和forEach方法最本质的区别是,有了前者,你可能会break跳出循环。您可以continue通过简单地从传递给 的函数返回来进行模拟forEach,但无法完全停止循环。

除此之外,两者有效地完成了相同的功能。由于变量提升,另一个细微差别涉及 for 循环中索引(以及所有包含变量)的范围。

// 'i' is scoped to the containing function
for (var i = 0; i < arr.length; i++) { ... }

// 'i' is scoped to the internal function
arr.forEach(function (el, i) { ... });

但是,我发现这forEach更具表现力——它代表了您遍历数组中每个元素的意图,并且它为您提供了对元素的引用,而不仅仅是索引。总的来说,这主要取决于个人品味,但如果您可以使用forEach,我会建议您使用它。


两个版本之间有一些更实质性的差异,特别是在性能方面。事实上,forEach正如这个 jsperf test所证明的那样,简单的 for 循环比该方法的性能要好得多

这种性能对您来说是否必要由您决定,在大多数情况下,我更喜欢表现力而不是速度。这种速度差异可能是由于在对稀疏数组进行操作时基本循环和方法之间存在细微的语义差异,如本答案所述

如果您不需要 的行为forEach和/或您需要尽早跳出循环,您可以使用 Lo-Dash_.each作为替代,它也可以跨浏览器工作。如果您使用的是 jQuery,它也提供了一个类似的$.each,只需注意每个变体中传递给回调函数的参数的差异。

(至于forEachpolyfill,如果您选择走那条路,它应该可以在较旧的浏览器中正常工作。)

此外,使用 Array.prototype.forEach 会使您的代码受扩展的支配。例如,如果您正在编写要嵌入到页面中的代码,则 Array.prototype.forEach 有可能被其他内容覆盖。
2021-04-24 18:04:01
如果你想在不同的浏览器和这样的垫片上看到一些测试结果,你可以看看这里,ci.testling.com/Xotic750/util-x
2021-04-26 18:04:01
@MarbleDaemon 这种可能性非常小,实际上是不可能的,因此可以忽略不计。Array.prototype.forEach用一些不兼容的版本覆盖可能会破坏如此多的库,因此无论如何避免它也无济于事。
2021-05-11 18:04:01
值得注意的是,库版本的each行为方式与 的 ECMA5 规范不同forEach,它们倾向于将所有数组视为密集数组(以避免 IE 错误,前提是您知道)。否则可能是“陷阱”。作为参考github.com/es-shims/es5-shim/issues/190
2021-05-13 18:04:01
还有一些原型方法允许您中断,例如Array.prototype.some循环直到您返回一个真值或直到它完全循环遍历数组。Array.prototype.every类似于Array.prototype.some但如果返回假值则停止。
2021-05-20 18:04:01

您可以使用自定义的 foreach 函数,它的性能会比 Array.forEach 好得多

您应该将其添加到您的代码中。这将向数组添加新函数。

function foreach(fn) {
    var arr = this;
    var len = arr.length;
    for(var i=0; i<len; ++i) {
        fn(arr[i], i);
    }
}

Object.defineProperty(Array.prototype, 'customForEach', {
    enumerable: false,
    value: foreach
});

然后你可以在任何地方使用它,比如 Array.forEach

[1,2,3].customForEach(function(val, i){

});

唯一的区别是快了 3 倍。https://jsperf.com/native-arr-foreach-vs-custom-foreach

更新:在新的 Chrome 版本中, .forEach() 的性能得到了改进。但是,该解决方案可以在其他浏览器中提供额外的性能。

JSPerf

许多开发人员(例如 Kyle Simpson)建议使用它.forEach来指示数组将有副作用和.map纯函数。for循环非常适合作为已知循环次数或任何其他不适合的情况的通用解决方案,因为它更容易交流,因为它对大多数编程语言的广泛支持。

例如

/* For Loop known number of iterations */
const numberOfSeasons = 4;
for (let i = 0; i < numberOfSeasons; i++) {
  //Do Something
}

/* Pure transformation */
const arrayToBeUppercased = ['www', 'html', 'js', 'us'];
const acronyms = arrayToBeUppercased.map((el) => el.toUpperCase));

/* Impure, side-effects with .forEach */
const acronymsHolder = [];
['www', 'html', 'js', 'us'].forEach((el) => acronymsHolder.push(el.toUpperCase()));

按照惯例,这似乎是最好的,但是社区还没有真正确定新的迭代协议for in循环的惯例一般来说,我认为遵循 JS 社区似乎愿意采用的 FP 概念是个好主意。