--- title: Array.prototype.forEach() slug: Web/JavaScript/Reference/Global_Objects/Array/forEach tags: - Array - ECMAScript5 - JavaScript - 参考 - 数组 - 方法 translation_of: Web/JavaScript/Reference/Global_Objects/Array/forEach ---
forEach() 方法对数组的每个元素执行一次给定的函数。
{{EmbedInteractiveExample("pages/js/array-foreach.html")}}
arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
callbackcurrentValueindex {{optional_inline}}array {{optional_inline}}forEach() 方法正在操作的数组。thisArg {{optional_inline}}callback 时,用作 this 的值。{{jsxref("undefined")}}。
forEach() 方法按升序为数组中含有效值的每一项执行一次 callback 函数,那些已删除或者未初始化的项将被跳过(例如在稀疏数组上)。
可依次向 callback 函数传入三个参数:
如果 thisArg 参数有值,则每次 callback 函数被调用时,this 都会指向 thisArg 参数。如果省略了 thisArg 参数,或者其值为 null 或 undefined,this 则指向全局对象。按照函数观察到 this 的常用规则,callback 函数最终可观察到 this 值。
forEach() 遍历的范围在第一次调用 callback 前就会确定。调用 forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach() 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 {{jsxref("Array.prototype.shift()", "shift()")}}),之后的元素将被跳过——参见下面的示例。
forEach() 为每个数组元素执行一次 callback 函数;与 {{jsxref("Array.prototype.map()", "map()")}} 或者 {{jsxref("Array.prototype.reduce()", "reduce()")}} 不同的是,它总是返回 {{jsxref("undefined")}} 值,并且不可链式调用。其典型用例是在一个调用链的最后执行副作用(side effects,函数式编程上,指函数进行 返回结果值 以外的操作)。
forEach() 被调用时,不会改变原数组,也就是调用它的数组(尽管 callback 函数在被调用时可能会改变原数组)。(译注:此处说法可能不够明确,具体可参考EMCA语言规范:'forEach does not directly mutate the object on which it is called but the object may be mutated by the calls to callbackfn.',即 forEach 不会直接改变调用它的对象,但是那个对象可能会被 callback 函数改变。)
备注: 除了抛出异常以外,没有办法中止或跳出 forEach() 循环。如果你需要中止或跳出循环,forEach() 方法不是应当使用的工具。
若你需要提前终止循环,你可以使用:
这些数组方法则可以对数组元素判断,以便确定是否需要继续遍历:
译者注:只要条件允许,也可以使用 {{jsxref("Array.prototype.filter()", "filter()")}} 提前过滤出需要遍历的部分,再用 forEach() 处理。
如你所见,3 和 7 之间空缺的数组单元未被 forEach() 调用 callback 函数,或进行任何其他操作。
const arraySparse = [1,3,,7];
let numCallbackRuns = 0;
arraySparse.forEach(function(element){
console.log(element);
numCallbackRuns++;
});
console.log("numCallbackRuns: ", numCallbackRuns);
// 1
// 3
// 7
// numCallbackRuns: 3
const items = ['item1', 'item2', 'item3'];
const copy = [];
// before
for (let i=0; i<items.length; i++) {
copy.push(items[i]);
}
// after
items.forEach(function(item){
copy.push(item);
});
备注:为了在控制台中显示数组的内容,你可以使用 console.table() 来展示经过格式化的数组。下面的例子则是另一种使用 forEach() 的格式化的方法。
下面的代码会为每一个数组元素输出一行记录:
function 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举个勉强的例子,按照每个数组中的元素值,更新一个对象的属性:
function Counter() {
this.sum = 0;
this.count = 0;
}
Counter.prototype.add = function(array) {
array.forEach(function(entry) {
this.sum += entry;
++this.count;
}, this);
// ^---- Note
};
const obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3 === (1 + 1 + 1)
obj.sum;
// 16 === (2 + 5 + 9)
因为 thisArg 参数(this)传给了 forEach(),每次调用时,它都被传给 callback 函数,作为它的 this 值。
备注:如果使用箭头函数表达式来传入函数参数, thisArg 参数会被忽略,因为箭头函数在词法上绑定了 {{jsxref("Operators/this", "this")}} 值。
下面的代码会创建一个给定对象的副本。 创建对象的副本有不同的方法,以下是只是一种方法,并解释了 Array.prototype.forEach() 是如何使用 ECMAScript 5 Object.* 元属性(meta property)函数工作的。
function copy(obj) {
const copy = Object.create(Object.getPrototypeOf(obj));
const propNames = Object.getOwnPropertyNames(obj);
propNames.forEach(function(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() 不会在迭代之前创建数组的副本。
var words = ['one', 'two', 'three', 'four'];
words.forEach(function(word) {
console.log(word);
if (word === 'two') {
words.shift();
}
});
// one
// two
// four
下面的示例仅用于学习目的。如果你想使用内置方法来扁平化数组,你可以考虑使用 {{jsxref("Array.prototype.flat()")}}(预计将成为 ES2019 的一部分,并且已在主要浏览器中实现)或参考其 polyfill。
/**
* Flattens passed array in one dimensional array
*
* @params {array} arr
* @returns {array}
*/
function flatten(arr) {
const result = [];
arr.forEach((i) => {
if (Array.isArray(i))
result.push(...flatten(i));
else
result.push(i);
})
return result;
}
// Usage
const problem = [1, 2, 3, [4, 5, [6, 7], 8, 9]];
flatten(problem); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
如果使用 promise 或 async 函数作为 forEach() 等类似方法的 callback 参数,最好对造成的执行顺序影响多加考虑,否则容易出现错误。
let ratings = [5, 4, 5];
let sum = 0;
let sumFunction = async function (a, b) {
return a + b;
}
ratings.forEach(async function(rating) {
sum = await sumFunction(sum, rating);
})
console.log(sum);
// Expected output: 14
// Actual output: 0
forEach() 是在第五版本里被添加到 ECMA-262 标准的;这样它可能在标准的其他实现中不存在,你可以在你调用 forEach() 之前插入下面的代码,在本地不支持的情况下使用 forEach()。该算法是 ECMA-262 第5版中指定的算法。它假定 {{jsxref("Object")}} 和 {{jsxref("TypeError")}} 拥有它们的初始值,且 callback.call 等价于 {{jsxref("Function.prototype.call()")}}。
// Production steps of ECMA-262, Edition 5, 15.4.4.18
// Reference: http://es5.github.io/#x15.4.4.18
if (!Array.prototype.forEach) {
Array.prototype.forEach = function(callback, thisArg) {
var T, k;
if (this == null) {
throw new TypeError(' this is null or not defined');
}
// 1. Let O be the result of calling toObject() passing the
// |this| value as the argument.
var O = Object(this);
// 2. Let lenValue be the result of calling the Get() internal
// method of O with the argument "length".
// 3. Let len be toUint32(lenValue).
var len = O.length >>> 0;
// 4. If isCallable(callback) is false, throw a TypeError exception.
// See: http://es5.github.com/#x9.11
if (typeof callback !== "function") {
throw new TypeError(callback + ' is not a function');
}
// 5. If thisArg was supplied, let T be thisArg; else let
// T be undefined.
if (arguments.length > 1) {
T = thisArg;
}
// 6. Let k be 0
k = 0;
// 7. Repeat, while k < len
while (k < len) {
var kValue;
// a. Let Pk be ToString(k).
// This is implicit for LHS operands of the in operator
// b. Let kPresent be the result of calling the HasProperty
// internal method of O with argument Pk.
// This step can be combined with c
// c. If kPresent is true, then
if (k in O) {
// i. Let kValue be the result of calling the Get internal
// method of O with argument Pk.
kValue = O[k];
// ii. Call the Call internal method of callback with T as
// the this value and argument list containing kValue, k, and O.
callback.call(T, kValue, k, O);
}
// d. Increase k by 1.
k++;
}
// 8. return undefined
};
}
| 规范 | 状态 | 备注 |
|---|---|---|
| {{SpecName('ESDraft', '#sec-array.prototype.foreach', 'Array.prototype.forEach')}} | {{Spec2('ESDraft')}} | |
| {{SpecName('ES6', '#sec-array.prototype.foreach', 'Array.prototype.forEach')}} | {{Spec2('ES6')}} | |
| {{SpecName('ES5.1', '#sec-15.4.4.18', 'Array.prototype.forEach')}} | {{Spec2('ES5.1')}} | Initial definition. Implemented in JavaScript 1.6. |
{{Compat("javascript.builtins.Array.forEach")}}