Daily-Interview-Question icon indicating copy to clipboard operation
Daily-Interview-Question copied to clipboard

第 72 题:为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因

Open Rashomon511 opened this issue 5 years ago • 47 comments

Rashomon511 avatar May 13 '19 01:05 Rashomon511

  • for 循环没有任何额外的函数调用栈和上下文;

  • forEach函数签名实际上是

array.forEach(function(currentValue, index, arr), thisValue)

它不是普通的 for 循环的语法糖,还有诸多参数和上下文需要在执行的时候考虑进来,这里可能拖慢性能;

lvtraveler avatar May 13 '19 01:05 lvtraveler

@yygmind 想问问题目中的截图,测试JS运行时间或者性能的网站,可以给个地址吗?

YouziXR avatar May 13 '19 02:05 YouziXR

@YouziXR 看起來是 jsperf

另外也推薦 jsbench.github.io,都很好用。

schaoss avatar May 13 '19 02:05 schaoss

找了spec image image

其中forEach 里操作了toObject 以及判断是否终止循环条件比for loop 复杂一点。

thisisandy avatar May 13 '19 02:05 thisisandy

@yygmind 想问问题目中的截图,测试JS运行时间或者性能的网站,可以给个地址吗?

https://jsperf.com/testing-foreach-vs-for-loop

Jalever avatar May 13 '19 03:05 Jalever

这是地址是截图的测试地址: https://jsperf.com/foreach-compare-for

jjeejj avatar May 13 '19 06:05 jjeejj

为哈我测试出来foreach更高呢 chrome版本越高 性能越好

mm-bt avatar May 13 '19 08:05 mm-bt

牛逼

JasonMa-95 avatar May 13 '19 08:05 JasonMa-95

for循环本来就是最原始的循环方案 foreach是基于它封装的

JasonMa-95 avatar May 13 '19 08:05 JasonMa-95

为什么 forEach 的性能那么差,为什么还要有这个方法呢?难道就是为了方便?

jjeejj avatar May 14 '19 04:05 jjeejj

为什么 forEach 的性能那么差,为什么还要有这个方法呢?难道就是为了方便?

@jjeejj 我理解是普通的业务需求没有那么大的数组,所以性能差距很小。 forEach写法较for简单一点。个人理解

LiuMengzhou avatar May 14 '19 04:05 LiuMengzhou

@mm-bt 第一个测试我也是forEach稍微快一丝丝,记得以前看过说针对forEach的实现是改过的.. 也就是用底层实现的循环(forEach,map)比自己写for循环效率要好.. 当时我在浏览器里跑过测试发现的确是这样..(10万以内) 然而刚我又写了一遍类似的..单次的话..for循环是要慢一些..

Zephylaci avatar May 14 '19 05:05 Zephylaci

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

jjeejj avatar May 14 '19 06:05 jjeejj

@mm-bt 我也是,一样的结果,记得以前看过针对forEach的实现是改过的..依稀记得,我在浏览器里跑循环测试时间很多情况下forEach在数量非常大的情况下,比单纯的for要快一些...map也比for快,至少在10万次循环以内是这样

我也是,单独跑时间的话,是 forEach 快些,但是用那些性能测试工具的话,就是 for性能更好 比如 http://jsbench.github.io/ 和 https://jsperf.com/ 这两个工具测试

jjeejj avatar May 14 '19 06:05 jjeejj

@jjeejj 看了你后面的时间测试结果,和我以前测得结果类似..你可以再加上map,10万以内map也会快一些.. 今天,我又查了下,发现,测试工具测试的是多次的结果...单次来看的确是forEach快..但是连着执行就会出现问题..10万以内的量级 连续执行10次就会有一倍的差距展示出来.. 期待有大佬能解答,为什么会这样,以及循环的这几个阈值是怎么来的..为什么.. 参考: 别人验证的文章 别人验证的文章2 v8的建议

var number = 100000;//array大小
var iteranum = 10;//迭代次数
var array = [];
for(let i=0;i<number;i++)
{
    array[i] = i+1;
}

//test cicle
var len = array.length;
//正常for循环
console.time('normal for');
for(let k=0;k<iteranum;k++)
{
    for(let i=0;i<len;i++)
    {
        array[i]+1;
    }
}

console.timeEnd('normal for');

//倒序for循环
console.time('reverse for');
for(let k=0;k<iteranum;k++)
{
    for(let i=len-1;i--;)
    {
        array[i]+1;
    }
}
console.timeEnd('reverse for');

//while循环
console.time('while');
for(let k=0;k<iteranum;k++)
{   
    let i=0;
    while(i<len)
    {
        array[i]+1;
        i++;
    }
}
console.timeEnd('while');

//for-in循环
console.time('for-in');
for(let k=0;k<iteranum;k++)
{
    for(let i in array)
    {
        array[i]+1;
    }
}
console.timeEnd('for-in');

//for each 循环
console.time("for each");
for(let k=0;k<iteranum;k++)
{
    array.forEach(function(e){
        e+1;
    });
}
console.timeEnd("for each");

//map 循环
console.time("map");
for(let k=0;k<iteranum;k++)
{
    array.map(function(e){
        e+1;
    });
}
console.timeEnd("map");

Zephylaci avatar May 14 '19 06:05 Zephylaci

@jjeejj 看到过一个说法,forEach能让代码更为简便和可读性更高,在性能不是特别影响的前提下,代码的可拓展性,可读性等会更为重要,而且随着浏览器引擎的升级,应该forEach的性能会被优化的越来越棒的

TH-coder avatar Jun 14 '19 03:06 TH-coder

简单去看,就是手动控制循环次数,和方法判定结束,肯定是不一样的

ttma2002 avatar Jul 11 '19 08:07 ttma2002

for循环是常见的循环语句forEach和map是在ES5出的,但是在性能上后者不如前者,在次数少的情况下forEach会比for要快,但是到达了十万次时forEach明显就跟不上了。在大数据量的情况下for循环的兼容性和多环境运行表现比较优秀,forEach的优点是在数据量小时占优势,语义话更简洁。循环时没有返回值。map和forEach差不多但是map循环有返回值

ragnar-document avatar Jul 18 '19 01:07 ragnar-document

在chrome浏览器上测了一下,当数组length超过3355443时,forEach的耗时是超过for循环的

Arrogant128 avatar Jul 18 '19 08:07 Arrogant128

一个是判断数组的长度,一个是 判断对象的长度,还有判断对象里面有没有数组下标这个key, 类似于 list 跟 map。

sailei1 avatar Jul 18 '19 08:07 sailei1

我猜forEach需要的额外内存使得重复实验时触发了垃圾回收,因此稍慢于for

lxx2013 avatar Jul 25 '19 11:07 lxx2013

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

你这个测试里forEach函数里的方法应该是一次都没被调用过 所以执行时间较短

287 avatar Jul 30 '19 06:07 287

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

liuguang2016 avatar Jul 31 '19 12:07 liuguang2016

@jjeejj 看到过一个说法,forEach能让代码更为简便和可读性更高,在性能不是特别影响的前提下,代码的可拓展性,可读性等会更为重要,而且随着浏览器引擎的升级,应该forEach的性能会被优化的越来越棒的

可读性确实很重要,另外函数式编程也是趋势。

wulichenyang avatar Aug 06 '19 08:08 wulichenyang

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。

aigle123 avatar Aug 07 '19 08:08 aigle123

v8 新版本其实现在性能上已经差不多了。但是forEach需要额外的内存和函数调用。不过新版本得v8给优化了。

ryouaki avatar Sep 12 '19 03:09 ryouaki

@yygmind 想问问题目中的截图,测试JS运行时间或者性能的网站,可以给个地址吗?

https://jsperf.com/

NECKLI avatar Dec 04 '19 03:12 NECKLI

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

hduhdc avatar Dec 13 '19 01:12 hduhdc

对于一些不需要最开始就确定长度的,这样甚至效率更高,因为不需要在循环开始之前就准备好要循环的数据,而是每次foreach循环获取下一个数据。 其实也不用记什么情况用,多写写程序,应该不难区分用

我咋觉得哥们走错片场了

ryouaki avatar Dec 13 '19 02:12 ryouaki

yygmind avatar Dec 16 '19 02:12 yygmind

为什么 forEach 的性能那么差,为什么还要有这个方法呢?难道就是为了方便?

forEach是声明式函数,我们不用关心内部如何去实现的;for循环是命令式函数,我们告诉计算器如何去做

iceycc avatar Jan 12 '20 15:01 iceycc

foreach 循环性能1000000下不差吧

------------------ 原始邮件 ------------------ 发件人: "冰洋Zz"<[email protected]>; 发送时间: 2020年1月12日(星期天) 晚上11:26 收件人: "Advanced-Frontend/Daily-Interview-Question"<[email protected]>; 抄送: "杭州电子科技大学-胡大昌"<[email protected]>;"Comment"<[email protected]>; 主题: Re: [Advanced-Frontend/Daily-Interview-Question] 第 72 题:为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因 (#121)

为什么 forEach 的性能那么差,为什么还要有这个方法呢?难道就是为了方便?

forEach是声明式函数,我们不用关心内部如何去实现的;for循环是命令式函数,我们告诉计算器如何去做

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

hduhdc avatar Jan 13 '20 01:01 hduhdc

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。 image image 这种比较可能无法说明问题

zxhuaman avatar Feb 23 '20 08:02 zxhuaman

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。 image image 这种比较可能无法说明问题

在chrome v8 6.x以后。对forEach的实现进行了改变。目前在v8 6.x forEach和for性能是一样的

ryouaki avatar Feb 24 '20 02:02 ryouaki

  1. 问题不成立,没有谁远高于谁;
  2. 最高票的测试代码有问题: new Array(N).forEach压根不循环; i < arr.length如果大循环应该提前定义长度变量;

whosesmile avatar Mar 11 '20 06:03 whosesmile

捕获 加一 10万以下 性能差不多
10万以上for性能秒杀foreach

luckC-newing avatar Mar 11 '20 09:03 luckC-newing

找了spec image image

其中forEach 里操作了toObject 以及判断是否终止循环条件比for loop 复杂一点。

forEach不能终止循环, 何来判断条件一说

Vivomo avatar Mar 24 '20 08:03 Vivomo

既然foreach没法终止(try catch)。没有for循环性能好,为啥大家大家还用呢

monxiaolee avatar Apr 18 '20 09:04 monxiaolee

对于高票的答案稍微改造了下, 上面有同学提出 new Array(N) 对于forEach来讲是不会循环的. 所以改造了下 数组的创建方式. 另外for循环中也做了些改造. 现在的测试结果基本就很明显了.

10万次循环的对比结果:

let arrs = Array.from({length: 100000});

console.time('for');
let len = arrs.length;
for (let i = 0; i < len; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');
for: 2.68310546875ms
forEach: 2.286376953125ms

100万次的对比结果

for: 2.40771484375ms
forEach: 15.508056640625ms

1000万次的对比结果

for: 7.268798828125ms
forEach: 153.18212890625ms

hycript avatar May 30 '20 08:05 hycript

为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因

  • forEach需要额外的内存和函数调用,上下文作用域等等,所以会拖慢性能
  • 新版浏览器已经优化的越来越好,性能上的差异会越来越小

soraly avatar Jun 29 '20 02:06 soraly

为什么普通 for 循环的性能远远高于 forEach 的性能,请解释其中的原因

  • forEach需要额外的内存和函数调用,上下文作用域等等,所以会拖慢性能
  • 新版浏览器已经优化的越来越好,性能上的差异会越来越小

赛高!

fhuy avatar Sep 04 '20 09:09 fhuy

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

如果按照之前的方法测试确实如此,不过给数组填充内容后,10万的数据量结果相差甚微,在100万数据量for循环大于forEach

let arrs = new Array(100000).fill(1);

console.time('for');
let for1 = []
for (let i = 0; i < arrs.length; i++) {
    for1.push(arrs[i])
};
console.timeEnd('for');

console.time('forEach');
let for2 = []
arrs.forEach((arr, index) => {
 for2.push(arr)
});
console.timeEnd('forEach');
for: 2.760986328125 ms
forEach: 2.88427734375 ms

xianjing123 avatar Dec 29 '20 03:12 xianjing123

在 node 和浏览器环境下,小于百万级的数据遍历,我这边测出来的结果始终是 forEach 优于 for

MrLeihe avatar Apr 26 '21 14:04 MrLeihe

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

其实你这个测试方法有点小问题,for循环这样写会影响性能的,for (let i = 0; i < arrs.length; i++)每次循环都会计算arrs的长度,你可以改成for (let i = 0, let len = arrs.length; i < len; i++)再试一下。

一样的 不管是先赋值还是直接判断length都是上面的结论。 甚至在一千万时赋值length比直接判断耗时更长, 有点搞不懂

xld1393679430 avatar May 24 '21 12:05 xld1393679430

我之前用浏览器的做的实验,现在改为 node发现了不一样的结果

let arrs = new Array(100000);

console.time('for');
for (let i = 0; i < arrs.length; i++) {

};
console.timeEnd('for');

console.time('forEach');
	
arrs.forEach((arr) => {
 
});
console.timeEnd('forEach');

for: 2.263ms
forEach: 0.254ms

在10万这个级别下, forEach 的性能是 for的十倍

for: 2.263ms
forEach: 0.254ms

在100万这个量级下, forEach 的性能是和for的一致

for: 2.844ms
forEach: 2.652ms

在1000万级以上的量级上 , forEach 的性能远远低于for的性能

for: 8.422ms
forEach: 30.328m

如果 使用 new Array(100000).fill(1) ,会发现,两者的差距并不大

当仅仅是 new Array(100000) 时, forEach 并不会执行 callback

yft avatar Aug 01 '21 14:08 yft

这个问题有点问题,Chrome环境for比forEach快、Safari两种方式差异不明显、node环境forEach比for快

deyinliu avatar Aug 25 '21 02:08 deyinliu

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 速度更快

runner-up-js avatar Mar 23 '22 09:03 runner-up-js