Array.prototype.forEach()
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
forEach()
方法对数组的每个元素执行一次给定的函数。
尝试一下
const array1 = ["a", "b", "c"];
array1.forEach((element) => console.log(element));
// Expected output: "a"
// Expected output: "b"
// Expected output: "c"
语法
forEach(callbackFn)
forEach(callbackFn, thisArg)
参数
callbackFn
-
为数组中每个元素执行的函数。并会丢弃它的返回值。该函数被调用时将传入以下参数:
thisArg
可选-
执行
callbackFn
时用作this
的值。参见迭代方法。
返回值
undefined
。
描述
forEach()
方法是一个迭代方法。它按索引升序地为数组中的每个元素调用一次提供的 callbackFn
函数。与 map()
不同,forEach()
总是返回 undefined
,而且不能继续链式调用。其典型的用法是在链式调用的末尾执行某些操作。
callbackFn
仅对已赋值的数组索引调用。对于稀疏数组中的空槽,它不会被调用。
forEach()
不会改变其调用的数组,但是,作为 callbackFn
的函数可以更改数组。请注意,在第一次调用 callbackFn
之前,数组的长度已经被保存。因此:
- 当调用
forEach()
时,callbackFn
不会访问超出数组初始长度的任何元素。 - 已经访问过的索引的更改不会导致
callbackFn
再次调用它们。 - 如果
callbackFn
更改了数组中已经存在但尚未访问的元素,则传递给callbackFn
的值将是在访问该元素时的值。已经被删除的元素不会被访问。
警告: 上述类型的并发修改经常导致难以理解的代码,通常应避免(特殊情况除外)。
forEach()
方法是通用的。它只期望 this
值具有 length
属性和整数键的属性。
除非抛出异常,否则没有办法停止或中断 forEach()
循环。如果有这样的需求,则不应该使用 forEach()
方法。
可以通过像 for
、for...of
和 for...in
这样的循环语句来实现提前终止。当不需要进一步迭代时,诸如 every()
、some()
、find()
和 findIndex()
等数组方法也会立即停止迭代。
forEach()
期望的是一个同步函数,它不会等待 Promise 兑现。在使用 Promise(或异步函数)作为 forEach
回调时,请确保你意识到这一点可能带来的影响。
const ratings = [5, 4, 5];
let sum = 0;
const sumFunction = async (a, b) => a + b;
ratings.forEach(async (rating) => {
sum = await sumFunction(sum, rating);
});
console.log(sum);
// 期望的输出:14
// 实际的输出:0
如果希望按顺序的或者并发的执行一系列操作,可以查看 promise 组合。
示例
在稀疏数组上使用 forEach()
const arraySparse = [1, 3, /* empty */, 7];
let numCallbackRuns = 0;
arraySparse.forEach((element) => {
console.log({ element });
numCallbackRuns++;
});
console.log({ numCallbackRuns });
// { element: 1 }
// { element: 3 }
// { element: 7 }
// { numCallbackRuns: 3 }
如你所见,不会为索引 2 处的缺失值调用回调函数。
将 for 循环转换为 forEach
const items = ["item1", "item2", "item3"];
const copyItems = [];
// before
for (let i = 0; i < items.length; i++) {
copyItems.push(items[i]);
}
// after
items.forEach((item) => {
copyItems.push(item);
});
打印出数组的内容
备注:
为了在控制台中显示数组的内容,你可以使用 console.table()
来展示经过格式化的数组。下面的例子则是另一种使用 forEach()
的格式化的方法。
下面的代码会为每一个数组元素输出一行记录:
const logArrayElements = (element, index /*, array */) => {
console.log(`a[${index}] = ${element}`);
};
// 注意,索引 2 被跳过,因为数组中这个位置没有内容
[2, 5, , 9].forEach(logArrayElements);
// logs:
// a[0] = 2
// a[1] = 5
// a[3] = 9
使用 thisArg
举个勉强的例子,按照每个数组中的元素值,更新一个对象的属性:
class Counter {
constructor() {
this.sum = 0;
this.count = 0;
}
add(array) {
// 只有函数表达式才有自己的 this 绑定
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
因为 thisArg
参数(this
)传给了 forEach()
,每次调用时,它都被传给 callbackFn
函数,作为它的 this
值。
对象复制函数
下面的代码会创建一个给定对象的副本。创建对象的副本有不同的方法,以下是只是一种方法,并解释了 Array.prototype.forEach()
是如何使用 Object.*
实用工具函数。
const copy = (obj) => {
const copy = Object.create(Object.getPrototypeOf(obj));
const propNames = Object.getOwnPropertyNames(obj);
propNames.forEach((name) => {
const desc = Object.getOwnPropertyDescriptor(obj, name);
Object.defineProperty(copy, name, desc);
});
return copy;
};
const obj1 = { a: 1, b: 2 };
const obj2 = copy(obj1); // 现在 obj2 看起来和 obj1 一模一样了
在迭代期间修改数组
下面的例子会输出 one
, two
, four
。
当到达包含值 two
的项时,整个数组的第一个项被移除了,这导致所有剩下的项上移一个位置。因为元素 four
正位于在数组更前的位置,所以 three
会被跳过。
forEach()
不会在迭代之前创建数组的副本。
const words = ["one", "two", "three", "four"];
words.forEach((word) => {
console.log(word);
if (word === "two") {
words.shift(); //'one' 将从数组中删除
}
}); // one // two // four
console.log(words); // ['two', 'three', 'four']
扁平化数组
下面的示例仅用于学习目的。如果你想使用内置方法来扁平化数组,你可以考虑使用 Array.prototype.flat()
。
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()
forEach()
方法读取 this
的 length
属性,然后访问每个整数索引。
const arrayLike = {
length: 3,
0: 2,
1: 3,
2: 4,
};
Array.prototype.forEach.call(arrayLike, (x) => console.log(x));
// 2
// 3
// 4
规范
Specification |
---|
ECMAScript® 2025 Language Specification # sec-array.prototype.foreach |
浏览器兼容性
Report problems with this compatibility data on GitHubdesktop | mobile | server | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
forEach |
Legend
Tip: you can click/tap on a cell for more information.
- Full support
- Full support