非常教程

JavaScript参考手册

排列 | Array

array.forEach

forEach()方法对数组的每个元素执行一次提供的函数。

var a = ['a', 'b', 'c'];

a.forEach(function(element) {
    console.log(element);
});

// a
// b
// c

语法

arr.forEach(function callback(currentValue, index, array) {
    //your iterator
}[, thisArg]);

参数

callback为数组中每个元素执行的函数,该函数接收三个参数:

currentValue(当前值)数组中正在处理的当前元素。

index(索引)数组中正在处理的当前元素的索引。

array forEach()方法正在操作的数组。

thisArg可选,可选参数。当执行回调 函数时用作this的值(参考对象)。

执行回调函数时使用this的值(即参考Object)。

返回值

undefined.

描述

forEach方法按升序为数组中含有效值的每一项执行一次callback 函数,那些已删除或者未初始化的项将被跳过(例如在稀疏数组上)。

callback函数会被依次传入三个参数:

  • 数组当前项的值
  • 数组当前项的索引
  • 数组对象本身

如果给forEach()提供了thisArg参数,当调用时,它将被传给callback函数,作为它的this值。否则,将会传入undefined作为它的this值。callback函数最终可观察到this值,这取决于函数观察到this的常用规则。

forEach() 遍历的范围在第一次调用 callback 前就会确定。调用forEach() 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback的值是 forEach() 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用shift()) ,之后的元素将被跳过 - 参见下面的示例。

forEach()为每个数组元素执行callback函数;不像map()或者reduce(),它总是返回undefined值,并且不可链式调用。典型用例是在一个链的最后执行副作用。

forEach()不改变它被调用的数组(虽然执行callback,但如果调用可能也会这样做)。

没有办法中止或者跳出 forEach() 循环,除了抛出一个异常。如果你需要这样,使用forEach()方法是错误的,你可以用一个简单的循环作为替代。如果您正在测试一个数组里的元素是否符合某条件,且需要返回一个布尔值,那么可使用 every()some()。如果可用,新方法find()或者findIndex()也可被用于真值测试的提早终止。

示例

转换为forEach

之前

var items = ['item1', 'item2', 'item3'];
var copy = [];

for (var i=0; i<items.length; i++) {
  copy.push(items[i])
}

之后

var items = ['item1', 'item2', 'item3'];
var copy = [];

items.forEach(function(item){
  copy.push(item)
});

打印出数组的内容

下面的代码会为每一个数组元素输出一行记录:

function logArrayElements(element, index, array) {
  console.log('a[' + index + '] = ' + element);
}

// Notice that index 2 is skipped since there is no item at
// that position in the array.
[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
};

var obj = new Counter();
obj.add([2, 5, 9]);
obj.count;
// 3 
obj.sum;
// 16

因为thisArg参数 (this) 传给了forEach(),每次调用时,它都被传给callback函数,作为它的this值。

如果使用箭头函数表达式传入函数参数,thisArg参数会被忽略,因为箭头函数在词法上绑定了this值。

对象复制函数

下面的代码会创建一个给定对象的副本。 创建对象的副本有不同的方法,以下是只是一种方法,并解释了Array.prototype.forEach() 是如何使用ECMAScript 5 Object.* 元属性(meta property )函数工作的。

function copy(obj) {
  var copy = Object.create(Object.getPrototypeOf(obj));
  var propNames = Object.getOwnPropertyNames(obj);

  propNames.forEach(function(name) {
    var desc = Object.getOwnPropertyDescriptor(obj, name);
    Object.defineProperty(copy, name, desc);
  });

  return copy;
}

var obj1 = { a: 1, b: 2 };
var obj2 = copy(obj1); // obj2 looks like obj1 now

如果数组在迭代时被修改了,则其他元素会被跳过。

下面的例子输出"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

兼容旧环境(Polyfill)

forEach 是在第五版本里被添加到 ECMA-262 标准的;这样它可能在标准的其他实现中不存在,你可以在你调用forEach之前 插入下面的代码,在本地不支持的情况下使用forEach()。该算法是 ECMA-262 第5版中指定的算法。算法假定ObjectTypeError拥有它们的初始值。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 = arguments[1];
    }

    // 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.
  };
}

规范

Specification

Status

Comment

ECMAScript 5.1 (ECMA-262)The definition of 'Array.prototype.forEach' in that specification.

Standard

Initial definition. Implemented in JavaScript 1.6.

ECMAScript 2015 (6th Edition, ECMA-262)The definition of 'Array.prototype.forEach' in that specification.

Standard

ECMAScript Latest Draft (ECMA-262)The definition of 'Array.prototype.forEach' in that specification.

Living Standard

浏览器兼容性

Feature

Chrome

Edge

Firefox

Internet Explorer

Opera

Safari

Basic Support

(Yes)

(Yes)

1.5

9

(Yes)

(Yes)

Feature

Android

Chrome for Android

Edge mobile

Firefox for Android

IE mobile

Opera Android

iOS Safari

Basic Support

(Yes)

(Yes)

(Yes)

1

(Yes)

(Yes)

(Yes)

JavaScript

JavaScript 是一种高级编程语言,通过解释执行,是一门动态类型,面向对象(基于原型)的解释型语言。它已经由ECMA(欧洲电脑制造商协会)通过 ECMAScript 实现语言的标准化。它被世界上的绝大多数网站所使用,也被世界主流浏览器( Chrome、IE、FireFox、Safari、Opera )支持。JavaScript 是一门基于原型、函数先行的语言,是一门多范式的语言,它支持面向对象编程,命令式编程,以及函数式编程。它提供语法来操控文本、数组、日期以及正则表达式等,不支持 I/O,比如网络