JavaScript中遍历数组的方式:forEach 相对于 for 有什么优势◔ ‸◔?

问题:在实现同样功能的前提下,map和forEach除了能够节省代码量之外,还有其他的优势吗?

效率方面经过我的测试,for高于另外两个方法。总体排序:
for > forEach > map

效率测试代码:

var arr = [],
  max = 1000;
for (var i = 1; arr.push(i++) < max;);

var mapArr = [],forEachArr = [],forArr = [];

console.time('map');
arr.map(function (val) {
  mapArr.push(val);
})
console.timeEnd('map');

console.time('forEach');
arr.forEach(function (val) {
  forEachArr.push(val);
})
console.timeEnd('forEach');

console.time('for');
for (var i = 0; i < arr.length; i++) {
  forArr.push(arr[i]);
}
console.timeEnd('for');

执行了三次,结果分别为:
图片描述
图片描述
图片描述

--------------------- 分割线 ---------------------

补充问题:
map在功能定义上来说不适合与for以及forEach来进行比较,它会生成一个经过处理的新数组,因此内部实现肯定会相对于forEach来说更复杂一些。

这个问题应该问的是:forEach相对于for来说,实现同样的功能,除了方便书写能够节省代码量之外,还有没有其他的优势或者特殊用途?

备注:如果对于效率要求高的项目相信不会使用forEach来遍历数组,所以才会问使用forEach有没有在哪个方面有明显的好处。

-------------------1月16日更新------------------------
首先,感谢各位回答!
第一次的问题是:for+i、forEach、map这三种方式在遍历数组的时候,各有什么优势和区别?
这个问题中,map的功能定位不同于另外两个方式,所以放在一起做对比不是很合适。不过大家还是给出了一些很有用的答案。

后来原问题改成了:JavaScript中遍历数组的方式:forEach 相对于 for 有什么优势?
经过这么多天大家的解答以及补充,我学到了很多有用的知识点。再次感谢大家。

阅读 22.1k
16 个回答

forEach相比普通的for循环的优势在于对稀疏数组的处理,会跳过数组中的空位。
如下所示:

for 循环

var arr = new Array(1000);

arr[0] = 1;
arr[99] = 3;
arr[999] = 5;
// for循环
for (var i = 0, l = arr.length; i < l; i++) {
    console.log('arr[%s]', i, arr[i]);
}
console.log('i :' , i);
// ...
// arr[0] 1
// ...
// arr[99] 3
// ...
// arr[999] 5
// i : 1000

// for - in 循环
var count = 0;
for(var j in arr){
    count ++ ;
    if(arr.hasOwnProperty(j)){
        console.log('arr[%s]', j, arr[j]);
    }
}
console.log('count : ', count);
// arr[0] 1
// arr[99] 3
// arr[999] 5
// i : 1000

forEach循环

var arr = new Array(1000);
arr[0] = 1;
arr[99] = 3;
arr[999] = 5;

var count = 0;
arr.forEach(function(value, index) {
    count++;
    console.log(typeof index);
    console.log(index, value);
});
console.log('count', count);
// number
// 0 1
// number
// 99 3
// number
// 999 5
// count 3

Array 在 Javascript 中是一个对象, Array 的索引是属性名。事实上, Javascript 中的 “array” 有些误导性, Javascript 中的 Array 并不像大部分其他语言的数组。首先, Javascript 中的 Array 在内存上并不连续,其次, Array 的索引并不是指偏移量。实际上, Array 的索引也不是 Number 类型,而是 String 类型的。我们可以正确使用如 arr[0] 的写法的原因是语言可以自动将 Number 类型的 0 转换成 String 类型的 "0" 。所以,在 Javascript 中从来就没有 Array 的索引,而只有类似 "0" 、 "1" 等等的属性。有趣的是,每个 Array 对象都有一个 length 的属性,导致其表现地更像其他语言的数组。但为什么在遍历 Array 对象的时候没有输出 length 这一条属性呢?那是因为 for-in 只能遍历“可枚举的属性”, length 属于不可枚举属性,实际上, Array 对象还有许多其他不可枚举的属性。 深入了解 JavaScript 中的 for 循环

for, forEach, map 这三个方法本质上是不同的。

for 是循环的基础语法,可以有 for...in, foo...of,for(let i = 0; i < len; i++) 等。在for循环中可以使用 continue, break 来控制循环。

forEach 可以当做是for(let i = 0; i < len; i++)的简写,但是不能完成 i + n 这种循环,同时也不支持 continuebreak,只能通过 return 来控制循环。另外,使用forEach的话,是不能退出循环本身的。

map的用法应该是循环当前可循环对象,并且返回新的可循环对象,跟forforEach是不同的。

map可以做链式操作,forEach不可。

let arr = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16];
arr.map(i => i * 32).map(i => i.toString(16)).map(i => i.toUpperCase());

怎么都没人说for...in...会遍历prototype

A for...in loop only iterates over enumerable properties. The loop will iterate over all enumerable properties of the object itself and those the object inherits from its constructor's prototype.

for+i不会呢,所以没有特殊情况我一般都用for+i,不会有坑,也没觉得写起来多麻烦,性能还最好,不用担心兼容性的问题(forEach只支持IE9及以上)。
最主要的是,for有一个好处是可以break

我看上面的回答,有个很重要的都没有提到,循环里有异步函数时,for循环是继发执行的,foreach是并发的,具体可以自己试一下哈

async function tmd(i) {
  await new Promise((resolve)=>{setTimeout(()=>{
    console.log(i)
    resolve()
  },1000)})
}

let arr=[0,1,2]

async function forlog() {
  for(let i of arr){
    await tmd(i)
  }
}

async function felog() {
  arr.forEach(async function (i) {
    await tmd(i)
  })
}

mapforEach语义上和功能上都是不一样的,一个是将数组映射到一个新的数组,一个是对数组进行遍历
forEachfor循环更多是个语法糖吧,写的时候可以省一些代码,感觉比较linq
而且for有很多种用法,比如一般的for+ifor...in...for...of...,有的人比较迷糊的会用错,而forEach就一个用法,遍历数组,功能比较单一

个人觉得 第一是forEach代码写起来比较清晰,不用再次用索引去定位需要循环的元素。第二是forEach的索引是独立的,不容易出现问题。

arr.map(function (val) {
mapArr.push(val);
})

这么用不对。

要这么用:

mapArr = arr.map(i=>i);

首先, for + i 最快, 前提是length要提前计算出来, 而不是你写的代码那样, 改成这样

for (var i = 0, len = arr.length; i < len; i++) {
  forArr.push(arr[i]);
}

一般你会遇见到js代码中哪些数据量非常大, 比如我做的"数据可视化"这类项目(一般结合echarts和highcharts), 一般list集合都会有几百上千甚至上万个元素, 可能是天气的变化趋势, 也可能是股票的趋势, 那么如果要对list元素进行处理的时候, 一定是要用for + i的(如何涉及到快速查找某一个元素, 要转成hash这种key-value对象), 因为数据量越大, 越能显现出他的优势!
反之如果是一些数据量较小的业务数据处理, 而且是代码中出现挺频繁的, 最好用函数式编程的方式来让代码简洁, 并提高可读性, 因为for + i此时并没有多大的性能优势

补充几点吧:

  1. ES5的遍历方法可以直接用在非数组的List对象上(https://developer.mozilla.org...),省去了Array.from或者slice成数组的步骤,比如这里

    $$('div').forEach(item => { console.log(item) })
  2. 省去你自己写i了,(item, index)

  3. ES6写法(箭头函数)比较优雅,更尤其是.filter()方法时

讲道理本质不都是for循环。。map和forEach只是为了方便使用

forEach和map接收两个参数(callback,thisArg),回调函数接收三个参数(当前值,当前索引,当前数组)
所以我一般操作数组喜欢用map()
传送门:https://developer.mozilla.org...

说下forEach它是有兼容问题的(代码就不附上了,百度下),for没有。哈哈,有点答非所问

语义化 forEach

forEach里面有回调函数,完成回调这个功能肯定是在内部做了工作吧?要不你拿到的index,value的参数是怎么来的?所以,我认为回调函数的实现增加了内置方法的工作量。

arr.forEach(function callback(currentValue, index, array) {

//your iterator

}[, thisArg]);
有一个可以选择的上下文参数,thisArg 参数被传递到callback,每次被调用时,用作它的 this 值。
如果省略 thisArg,则 undefined 将用作 this 值。(这里很有可能存在一个上下文环境的坑!!)

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏