第 72 题:为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因
作为一名前端开发,for 和 foreach 循环遍历几乎每天都在使用,那么这两种遍历方式哪一种效率更高呢? 效率高的原因是什么呢?
一、for() 循环
通过下标,对循环中的代码反复执行,功能强大,可以通过 index 取得元素。在处理比较复杂的处理的时候较为方便
二、forEach() 循环
forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。foreach 有的也叫增强 for 循环,foreach 其实是 for 循环的一个特殊简化版。注意 forEach() 对于空数组是不会执行回调函数的
array.forEach(function(currentValue, index, arr), thisValue)
function(currentValue, index, arr):必需。 数组中每个元素需要调用的函数。
currentValue | 必需,当前元素 |
index | 可选,当前元素的索引值。 |
arr | 可选,当前元素所属的数组对象。 |
thisValue: 可选。传递给函数的值一般用 this 值。如果这个参数为空, undefined 会传递给 this 值
三、console.time 和 console.timeEnd 用法
console.time 和 console.timeEnd 这两个方法可以用来让 Web 开发人员测量一个 JavaScript 脚本程序执行消耗的时间。随着 Web 应用越来越重要,JavaScript 的执行性能也日益受到重视,Web 开发人员知道一些性能测试机器是必须的。测试 JavaScript 性能的方法有很多,但 console.time/console.timeEnd 两个方法是最基本、最直接的技巧。
- console.time 方法是开始计算时间
- console.timeEnd 是停止计时,输出脚本执行的时间。
- 这两个方法中都可以传入一个参数,作为计时器的名称,它的作用是在代码并行运行时分清楚各个计时器。
- 对 console.timeEnd 的调用会立即输出执行总共消耗的时间,单位是毫秒。
// 启动计时器
console.time('计时器名称');
// (写一些测试用代码)
// 停止计时,输出时间
console.timeEnd('计时器名称');
四、测试性能
在 1000000 这个级别下,forEach 的性能高于 for
let arrs = new Array(1000000);
console.time('for');
for (let i = 0; i < arrs.length; i++) {};
console.timeEnd('for'); // for: 10.329833984375ms
console.time('forEach');
arrs.forEach((arr) => {});
console.timeEnd('forEach'); // forEach: 5.076904296875ms
在 10000000 这个级别下,forEach 的性能还是高于 for
let arrs = new Array(10000000);
console.time('for');
for (let i = 0; i < arrs.length; i++) {};
console.timeEnd('for'); // for: 95.157958984375ms
console.time('forEach');
arrs.forEach((arr) => {});
console.timeEnd('forEach'); // forEach: 37.9619140625ms
在 100000000 级以上的量级上 ,forEach 的性能远远低于 for 的性能
let arrs = new Array(100000000);
console.time('for');
for (let i = 0; i < arrs.length; i++) {};
console.timeEnd('for'); // for: 939.18994140625ms
console.time('forEach');
arrs.forEach((arr) => {});
console.timeEnd('forEach'); // forEach: 1614.8642578125ms
五、for 和 forEach 的区别
遍历
for 循环按顺序遍历,forEach 使用 iterator 迭代器遍历
数据结构
for 循环是随机访问元素,foreach 是顺序链表访问元素
性能上
对于 arraylist,是顺序表,使用 for 循环可以顺序访问,速度较快;使用 foreach 会比 for 循环稍慢一些。
对于 linkedlist,是单链表,使用 for 循环每次都要从第一个元素读取 next 域来读取,速度非常慢;使用 foreach 可以直接读取当前结点,数据较快;
六、如何选择
foreach 相对于 for 循环,代码减少了,但是 foreach 依赖 IEnumerable。在运行的时候效率低于 for 循环。当然了,在处理不确定循环次数的循环,或者循环次数需要计算的情况下。使用 foreach 比较方便。而且 foreach 的代码经过编译系统的代码优化后,和 for 循环的循环类似。
可以说,foreach 语句是 for 语句的特殊简化版本,在遍历数组、集合方面,foreach 为开发人员提供了极大的方便。在复杂的循环设计时,还是应该使用 for 循环更加的灵活。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(46)
for循环是底层写法,效率高。
forEach是封装写法,效率会低一些
`let arrs = new Array(1000000);
console.time('forEach');
arrs.forEach((arr) => {});
console.timeEnd('forEach');
console.time('for');
for (let i = 0,len=arrs.length; i < len; i++) {};
console.timeEnd('for');`
结果:
forEach: 7.42919921875ms
for: 4.7470703125ms
可读性确实很重要,另外函数式编程也是趋势。
其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。
v8 新版本其实现在性能上已经差不多了。但是forEach需要额外的内存和函数调用。不过新版本得v8给优化了。
https://jsperf.com/
foreach效率比for低主要分2个角度说。
2个地方,一个是.net 1.1之前的对值类型的装箱,一个是每次调用GetEnumator方法的函数调用带来的时间消耗,单一次都不消耗时间,但经过大量循环放大后,时间消耗比较明显。
.net 1.1之后的版本,foreach对值类型已经不装箱,不慢了,因为有了yield关键字。
但函数调用带来的堆栈创建内存分配则不可避免。
绝对意义上,for比foreach快,但从.net 1.1之后,这个差距缩小到多一层函数调用而已,不是特别严格的地方,还是用foreach好一点。因为foreach不止可以访问一个数组或List这样循环时能确定长度的集合,也可以访问可迭代的类型,对于一些不需要最开始就确定长度的,这样甚至效率更高,因为不需要在循环开始之前就准备好要循环的数据,而是每次foreach循环获取下一个数据。
其实也不用记什么情况用,多写写程序,应该不难区分用途
借鉴大佬https://me.csdn.net/wuyazhe
我咋觉得哥们走错片场了
forEach是声明式函数,我们不用关心内部如何去实现的;for循环是命令式函数,我们告诉计算器如何去做
在chrome v8 6.x以后。对forEach的实现进行了改变。目前在v8 6.x forEach和for性能是一样的
加一 10万以下 性能差不多
10万以上for性能秒杀foreach
forEach不能终止循环, 何来判断条件一说
既然foreach没法终止(try catch)。没有for循环性能好,为啥大家大家还用呢
对于高票的答案稍微改造了下, 上面有同学提出 new Array(N) 对于forEach来讲是不会循环的. 所以改造了下 数组的创建方式. 另外for循环中也做了些改造. 现在的测试结果基本就很明显了.
10万次循环的对比结果:
100万次的对比结果
1000万次的对比结果
为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因
赛高!
如果按照之前的方法测试确实如此,不过给数组填充内容后,10万的数据量结果相差甚微,在100万数据量for循环大于forEach
在 node 和浏览器环境下,小于百万级的数据遍历,我这边测出来的结果始终是 forEach 优于 for
一样的 不管是先赋值还是直接判断length都是上面的结论。 甚至在一千万时赋值length比直接判断耗时更长, 有点搞不懂
如果 使用
new Array(100000).fill(1)
,会发现,两者的差距并不大当仅仅是
new Array(100000)
时, forEach 并不会执行 callback这个问题有点问题,Chrome环境for比forEach快、Safari两种方式差异不明显、node环境forEach比for快
let arrs = new Array(100000);
for (let i = 0; i < arrs.length; i++) {
arrs[i] = i
};
console.time('for');
for (let i = 0; i < arrs.length; i++) {
};
console.timeEnd('for');
console.time('forEach');
arrs.forEach((arr) => {
});
console.timeEnd('forEach');
for: 1ms
forEach: 2ms
奇怪我把楼上的代码复制一下,改了改 时间久是 for效率更好了 而且 有值的for循环比无值的for 速度更快
你这个测试里forEach函数里的方法应该是一次都没被调用过 所以执行时间较短
@jjeejj 我理解是普通的业务需求没有那么大的数组,所以性能差距很小。
forEach写法较for简单一点。个人理解
@mm-bt 第一个测试我也是forEach稍微快一丝丝,记得以前看过说针对forEach的实现是改过的..
也就是用底层实现的循环(forEach,map)比自己写for循环效率要好..
当时我在浏览器里跑过测试发现的确是这样..(10万以内)
然而刚我又写了一遍类似的..单次的话..for循环是要慢一些..
我之前用浏览器的做的实验,现在改为
node
发现了不一样的结果在10万这个级别下,
forEach
的性能是for
的十倍在100万这个量级下,
forEach
的性能是和for
的一致在1000万级以上的量级上 ,
forEach
的性能远远低于for
的性能我也是,单独跑时间的话,是 forEach 快些,但是用那些性能测试工具的话,就是 for性能更好
比如 http://jsbench.github.io/ 和 https://jsperf.com/ 这两个工具测试
@jjeejj
看了你后面的时间测试结果,和我以前测得结果类似..你可以再加上map,10万以内map也会快一些..
今天,我又查了下,发现,测试工具测试的是多次的结果...单次来看的确是forEach快..但是连着执行就会出现问题..10万以内的量级 连续执行10次就会有一倍的差距展示出来..
期待有大佬能解答,为什么会这样,以及循环的这几个阈值是怎么来的..为什么..
参考:
别人验证的文章
别人验证的文章2
v8的建议
@jjeejj 看到过一个说法,forEach能让代码更为简便和可读性更高,在性能不是特别影响的前提下,代码的可拓展性,可读性等会更为重要,而且随着浏览器引擎的升级,应该forEach的性能会被优化的越来越棒的
简单去看,就是手动控制循环次数,和方法判定结束,肯定是不一样的
for循环是常见的循环语句forEach和map是在ES5出的,但是在性能上后者不如前者,在次数少的情况下forEach会比for要快,但是到达了十万次时forEach明显就跟不上了。在大数据量的情况下for循环的兼容性和多环境运行表现比较优秀,forEach的优点是在数据量小时占优势,语义话更简洁。循环时没有返回值。map和forEach差不多但是map循环有返回值
在chrome浏览器上测了一下,当数组length超过3355443时,forEach的耗时是超过for循环的
一个是判断数组的长度,一个是 判断对象的长度,还有判断对象里面有没有数组下标这个key,
类似于 list 跟 map。
我猜forEach需要的额外内存使得重复实验时触发了垃圾回收,因此稍慢于for
为什么
forEach
的性能那么差,为什么还要有这个方法呢?难道就是为了方便?for循环本来就是最原始的循环方案 foreach是基于它封装的
牛逼
为哈我测试出来foreach更高呢 chrome版本越高 性能越好
这是地址是截图的测试地址:
https://jsperf.com/foreach-compare-for
https://jsperf.com/testing-foreach-vs-for-loop
找了spec
其中forEach 里操作了toObject 以及判断是否终止循环条件比for loop 复杂一点。
@YouziXR 看起來是 jsperf
另外也推薦 jsbench.github.io,都很好用。
@yygmind 想问问题目中的截图,测试JS运行时间或者性能的网站,可以给个地址吗?
for 循环没有任何额外的函数调用栈和上下文;
forEach函数签名实际上是
array.forEach(function(currentValue, index, arr), thisValue)
它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,这里可能拖慢性能;