Javascript efficiency: 'for' vs 'forEach'
2017年Javascript中for()循环与a.foreach的当前标准是什么?
我目前正在努力通过柯尔特·斯蒂尔斯在乌迪米的"网络开发训练营",他在他的教诲中倾向于使用forEach而不是for。然而,作为课程工作的一部分,我在练习中搜索了各种各样的东西,我发现越来越多的建议使用for循环,而不是forEach。大多数人似乎说for循环更有效。
这是自课程编写以来(大约2015年)发生的变化,还是它们各自的优缺点,我们将从中学习到更多的经验。
任何建议都将不胜感激。
- 为什么要将自己与某人对2017年标准的主观想法联系起来?采用一个永恒的标准,比如编写干净的、可维护的代码,知道工程师们正在努力使最常用的构造非常有效,然后在遇到特定的性能问题时优化性能。
- 一个本地的for很难以纯粹的速度打败它。forEach()为每个迭代调用一个回调;因此,这显然会带来一些开销。
- 作为一个我很少使用的开发人员,大部分工作都是通过映射、过滤或减少方法完成的。
- @A.T.只要你不是那种纯粹使用map进行迭代并丢弃它生成的新数组的人。我不知道有多少人,我已经纠正了这个网站上的特定功能选择单。
- @佳能同意,循环选择是很重要的,在选择时要牢记这些方法的输出和剩余。我认为对于可读性来说,应该避免"for loops"的简单用法。
- 他们现在也一样快。
对于
for循环效率更高。它是一个循环结构,专门设计用于在条件为真时迭代,同时提供一个步进机制(通常用于增加迭代器)。例子:
1 2 3
| for (var i=0, n=arr.length; i < n; ++i ) {
...
} |
这并不意味着for循环总是更有效,只是JS引擎和浏览器已经对它们进行了优化。多年来,对于哪些循环结构更有效(对于,while,reduce,reverse while,等等),已经有了妥协——不同的浏览器和JS引擎都有自己的实现,它们提供不同的方法来产生相同的结果。随着浏览器进一步优化以满足性能需求,理论上,[].forEach的实现方式可以更快或与for相当。
效益:
- 有效率的
- 早期循环终止(符合break和continue)
- 条件控制(i可以是任何内容,并且不绑定到数组的大小)
- 变量范围(var i使循环结束后i可用)
前额
.forEach是主要在数组上迭代的方法(也可以在其他可枚举对象上迭代,如Map和Set对象)。它们是更新的,并且提供主观上更容易阅读的代码。例子:
1 2 3
| [].forEach((val, index)=>{
...
}); |
效益:
- 不涉及变量设置(迭代数组的每个元素)
- 函数/箭头函数将变量范围限定到块在上面的示例中,val将是新创建函数的参数。因此,任何在循环之前称为val的变量,在循环结束后都将保持其值。
- 主观上更容易维护,因为识别代码在做什么可能更容易——它在可枚举的上迭代;而for循环可以用于任意数量的循环方案。
性能
性能是一个棘手的话题,通常需要一些经验,当谈到预先考虑或方法。为了提前(在开发过程中)确定可能需要多少优化,程序员必须对过去的问题案例经验有一个很好的了解,并且对潜在的解决方案有一个很好的理解。
在某些情况下使用jquery可能会太慢(一个有经验的开发人员可能知道这一点),而在其他情况下可能是一个问题,在这种情况下,库的跨浏览器兼容性和执行其他功能(如Ajax、事件处理)的方便性将值得节省开发(和维护)时间。
另一个例子是,如果性能和优化就是一切,那么除了机器或程序集之外就没有其他代码了。显然情况并非如此,因为有许多不同的高级别和低级别语言,每种语言都有自己的权衡。这些权衡包括但不限于专业化、开发的易用性和速度、维护的易用性和速度、优化代码、无错误代码等。
途径
如果您没有很好的理解某些东西是否需要优化代码,那么首先编写可维护的代码通常是一个很好的经验法则。从那里,您可以测试并确定在需要时需要更多关注的内容。
也就是说,某些明显的优化应该是一般实践的一部分,不需要任何思考。例如,考虑以下循环:
1
| for (var i=0; i < arr.length; ++i ){} |
对于循环的每次迭代,javascript都会检索arr.length,这是每个循环的一个关键查找成本计算操作。没有理由不这样做:
1
| for (var i=0, n=arr.length; i < n; ++i){} |
这做了同样的事情,但只检索一次arr.length,缓存变量并优化代码。
- 优秀、彻底、非妄自尊大的回答,不局限于单一的时间点、实施或微基准测试。
- 太好了,这正是我要找的,而且比视频更深入。我真的很感激这一点,作为一个新人,有太多的东西需要学习,而这些东西很容易被忽略,仅仅是一个对另一个的偏爱,没有任何理由会阻碍你的未来。多谢!
- for循环在调试方面通常有一个优势:它们是进入、断点和检查的三重工具。使用foreach(),额外的回调级别可能会使事情变得复杂一些。
- 回答得好,但是没有计算出Array.prototype.length的概念是错误的,这里有一篇文章解释了它dmitripavletin.com/the-magic-behind-array-length-property
- @Felipebuccioni谢谢你,我实际上是在研究这个问题,因为我认为引擎现在检查数组是否会发生变化,以确定是否缓存了长度。尽管如此,出于传统目的,我认为优化引擎并没有那么慷慨,而且每次迭代的长度都会影响性能。我会看一看你的文章,并在我有时间的时候把它加入到答案中,或者希望一个杰出的社区成员会为我做一个修改,作为我的编辑:)
- 那么forin呢?
- @索洛莫努克专门问了关于for和forEach的问题。for..in也可以用来完成类似的工作,但它会迭代对象的属性,因此需要额外的代码。如果寻求另一种选择,我宁愿推荐迭代值的for..of。
- 哎呀,我想我把这两个搞错了。
- 我真的很喜欢这个答案和短语[...], if performance and optimization was everything, there would be no other code than machine or assembly.中的解释。我认为,使用声明块范围局部变量的let语句,forEach的第二个好处不再是优于for循环的好处,除非在不支持let的环境中。