赞
踩
本文会从以下几个角度来对 forEach 进行解释:
forEach 首次提出于 ES3,并且在 ES5 中进行了完善。
forEach() 方法会对数组的每个元素都调用一次给定的 callbackFn 函数。
forEach 的参数有:
forEach 返回值为 undefined(无返回值)。
callbackFn 是一个必选参数,表示数组中每个元素所调用的函数。
函数调用时带有以下参数:
element -> 数组中正在处理的当前元素。
index -> 数组中正在处理的当前元素的索引。
array -> forEach() 方法正在操作的数组。
我们使用 forEach 方法很少使用该参数,大家了解下即可:
thisArg 是一个可选参数,表示每次调用 callbackFn 函数时,this 的指向。
thisArg 使用示例如下:
//thisArg使用场景 class Counter { constructor() { this.sum = 0; this.count = 0; } add(array) { array.forEach(function countEntry(entry) { this.sum += entry; //元素值的总和 ++this.count; //元素个数 }, this); } } const obj = new Counter(); obj.add([2, 5, 9]); console.log(obj.count); // 3 console.log(obj.sum); // 16
forEach 必返回 undefined(无返回值)
forEach 方法语法格式如下:
const arr = [1, 2, 3, 4]; // 回调函数 arr.forEach(callbackFn); arr.forEach(callbackFn, thisArg); // 内联回调函数 arr.forEach(function (element) {}); arr.forEach(function (element, index) {}); arr.forEach(function (element, index, array) {}); arr.forEach(function (element, index, array) {}, thisArg); // 箭头函数 //使用箭头函数表达式来传入函数参数时, thisArg 参数会被忽略。 arr.forEach((element) => {}); arr.forEach((element, index) => {}); arr.forEach((element, index, array) => {}); //arr.forEach((element, index, array) => {},thisArg) //此方法无法使用,因为箭头函数在词法上绑定了 this 值,函数里面没有this,而是使用了外层函数的this。
forEach 循环进行扁平化数组操作,代码如下:
//封装扁平化数组
const flatten = (arr) => {
const result = [];
arr.forEach((item) => {
if (Array.isArray(item)) {
result.push(...flatten(item));
} else {
result.push(item);
}
});
return result;
};
const nested = [1, 2, 3, [4, 5, [6, 7], 8, 9]];
console.log(flatten(nested)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
forEach() 遍历的范围在第一次调用 callbackFn 前就会确定,而且不会修改原数组。
另外 forEach() 不会在迭代之前创建数组的副本,所以:
数组的空位(empty item)的详解可参见阮一峰老师的 es6 入门
//在迭代时 使用shift方法删除了数组的头元素
const arr = [1, 2, 3, 4];
arr.forEach((item) => {
console.log(item);
if (item === 2) {
arr.shift(); //1 将从数组中删除
}
}); // 1 // 2 // 4
console.log(arr); // [2, 3, 4]
//在迭代时 使用pop方法删除了数组的尾元素
const arr = [1, 2, 3, 4];
arr.forEach((item) => {
console.log(item);
if (item === 2) {
arr.pop(); //1 将从数组中删除
}
}); // 1 // 2 // 3
console.log(arr); // [ 1, 2, 3 ]
这是一道非常经典的面试题,考验了我们 js 的基础。
如果在 forEach 循环中想使用continue; break; return;这三个关键字来跳出循环时:
SyntaxError: Illegal continue statement: no surrounding iteration statementSyntaxError: Illegal break statement这是我们平时开发所使用的方式,该方法的算法效率为:O(n),性能较好。
//try catch + throw 方法
const arr = [1, 2, 3, 4];
try {
arr.forEach((item) => {
if (item === 2) {
throw new Error(`值为${item}时跳出forEac循环`);
}
console.log(item); //只打印 1
});
} catch (e) {
console.log(e); //Error: 值为2时跳出forEac循环
}
console.log(arr); // [1, 2, 3, 4]
受2. forEach 的特别之处的内容启发,所以我想到了该方法。
该方法的算法效率为:O(n²),性能较差。
至于两种方法的性能对比,可参考我的这篇博客JavaScript 中的 try catch 语句的性能分析。
//splice + return 方法
//在迭代时 使用splice方法 删除数组中的元素
const arr = [1, 2, 3, 4];
let spliceArr = null;
arr.forEach((item, index) => {
if (item === 2) {
spliceArr = arr.splice(index); // 将 2 以后的元素全部删除 并赋值给spliceArr
return;
}
console.log(item); // 1
});
arr.splice(arr.length, spliceArr.length, ...spliceArr); //将删除的元素拼接回去
console.log(arr); // [1, 2, 3, 4]
这也是一道经典的面试题,大家可以去看看这个视频面试官问:有了 for 循环为什么还要 forEach?。
下面我将从本质,语法,性能三方面来进行讲解。
迭代器是 ES6 新增的一种特殊对象,它的标志是返回对象的 next()方法,迭代行为判断在 done 之中,能在不暴露内部表示的情况下,迭代器实现了遍历。
由于迭代器这块知识我尚未深入了解,所以暂时就不过多解释。
参数的区别
arr.forEach(function (element, index, array) {}, thisArg)for (initialization; expression; post-loop-expression) statement中断方式的区别
continue; break; return;这三个关键字来跳出循环在forEach对element进行的操作原则上不会修改原数组,除非直接使用index下标操作原数组。
forEach 循环只能下标为 0 开始,不能进行认为干预,而 for 循环不同。
在 chrome 62 和 Node.js v91.0 环境下:for 循环比 forEach 快 1 倍,forEach 比 map 快 20%左右。
原因分析:
但抛开应用场景谈性能等于“耍流氓”,使用大家一定要结合实际场景来选择一种合适的方法。
码字不易,觉得有帮助的朋友点赞,关注走一波。
这是我目前所了解的知识面中最好的解答,当然可能存在一点的误区。
所以如果对本文存在疑惑,可以去评论区进行留言,欢迎大家指正文中的错误观点。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。