Array.prototype.forEach() - JavaScript 编辑
forEach()
方法对数组的每个元素执行一次给定的函数。
The source for this interactive example is stored in a GitHub repository. If you'd like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
语法
arr.forEach(callback(currentValue [, index [, array]])[, thisArg])
参数
callback
- 为数组中每个元素执行的函数,该函数接收一至三个参数:
currentValue
- 数组中正在处理的当前元素。
index
可选- 数组中正在处理的当前元素的索引。
array
可选forEach()
方法正在操作的数组。
thisArg
可选- 可选参数。当执行回调函数
callback
时,用作this
的值。
返回值
描述
forEach()
方法按升序为数组中含有效值的每一项执行一次 callback
函数,那些已删除或者未初始化的项将被跳过(例如在稀疏数组上)。
可依次向 callback
函数传入三个参数:
- 数组当前项的值
- 数组当前项的索引
- 数组对象本身
如果 thisArg
参数有值,则每次 callback
函数被调用时,this
都会指向 thisArg
参数。如果省略了 thisArg
参数,或者其值为 null
或 undefined
,this
则指向全局对象。按照函数观察到 this
的常用规则,callback
函数最终可观察到 this
值。
forEach()
遍历的范围在第一次调用 callback
前就会确定。调用 forEach
后添加到数组中的项不会被 callback
访问到。如果已经存在的值被改变,则传递给 callback
的值是 forEach()
遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift()
),之后的元素将被跳过——参见下面的示例。
forEach()
为每个数组元素执行一次 callback
函数;与 map()
或者 reduce()
不同的是,它总是返回 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()
方法不是应当使用的工具。
若你需要提前终止循环,你可以使用:
- 一个简单的 for 循环
- for...of / for...in 循环
Array.prototype.every()
Array.prototype.some()
Array.prototype.find()
Array.prototype.findIndex()
这些数组方法则可以对数组元素判断,以便确定是否需要继续遍历:
译者注:只要条件允许,也可以使用 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
将 for 循环转换为 forEach
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
参数会被忽略,因为箭头函数在词法上绑定了 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
扁平化数组
下面的示例仅用于学习目的。如果你想使用内置方法来扁平化数组,你可以考虑使用 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 函数的使用备注
如果使用 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
Polyfill
forEach()
是在第五版本里被添加到 ECMA-262 标准的;这样它可能在标准的其他实现中不存在,你可以在你调用 forEach()
之前插入下面的代码,在本地不支持的情况下使用 forEach()
。该算法是 ECMA-262 第5版中指定的算法。它假定 Object
和 TypeError
拥有它们的初始值,且 callback.call
等价于 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
};
}
规范
规范 | 状态 | 备注 |
---|---|---|
ECMAScript (ECMA-262) Array.prototype.forEach | Living Standard | |
ECMAScript 2015 (6th Edition, ECMA-262) Array.prototype.forEach | Standard | |
ECMAScript 5.1 (ECMA-262) Array.prototype.forEach | Standard | Initial definition. Implemented in JavaScript 1.6. |
浏览器兼容性
BCD tables only load in the browser
The compatibility table in this page is generated from structured data. If you'd like to contribute to the data, please check out https://github.com/mdn/browser-compat-data and send us a pull request.参见
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论