Array.prototype.forEach()

forEach() 方法对数组的每个元素执行一次提供的函数。
注意: 没有返回一个新数组!  没有返回值!
应用场景:为一些相同的元素,绑定事件处理器!
array.forEach(callback(currentValue, index, array){
    //do something
}, this)

array.forEach(callback[, thisArg])
callback
为数组中每个元素执行的函数,该函数接收三个参数:
currentValue(当前值)
数组中正在处理的当前元素。
index(索引)
数组中正在处理的当前元素的索引。
array
forEach()方法正在操作的数组。
thisArg可选
可选参数。当执行回调 函数时用作this的值(参考对象)。
 
forEach 方法按升序为数组中含有效值的每一项执行一次callback 函数,那些已删除(使用delete方法等情况)或者未初始化的项将被跳过(但不包括那些值为 undefined 的项)(例如在稀疏数组上)。
注意:稀疏数组,例如使用Array()构造函数创建一个长度不为零的数组的时候,就是一个稀疏数组。Array(3),遍历它的时候它并没有元素。
如果给forEach传递了thisArg参数,当调用时,它将被传给callback 函数,作为它的this值。否则,将会传入 undefined 作为它的this值。callback函数最终可观察到this值,这取决于 函数观察到this的常用规则。
 
forEach 遍历的范围在第一次调用 callback 前就会确定。调用forEach 后添加到数组中的项不会被 callback 访问到。如果已经存在的值被改变,则传递给 callback 的值是 forEach 遍历到他们那一刻的值。已删除的项不会被遍历到。如果已访问的元素在迭代时被删除了(例如使用 shift()) ,之后的元素将被跳过 - 参见下面的示例。

forEach() 为每个数组元素执行callback函数;不像map() 或者reduce() ,它总是返回 undefined值,并且不可链式调用。典型用例是在一个链的最后执行副作用。
 
注意:
没有办法中止或者跳出 forEach 循环,除了抛出一个异常。如果你需要这样,使用forEach()方法是错误的,你可以用一个简单的循环作为替代。如果您正在测试一个数组里的元素是否符合某条件,且需要返回一个布尔值,那么可使用 Array.every 或 Array.some。如果可用,新方法 find() 或者findIndex() 也可被用于真值测试的提早终止。
 
function logArrayElements(element, index, array) {
    console.log("a[" + index + "] = " + element);
}

// 注意索引2被跳过了,因为在数组的这个位置没有项
[2, 5, ,9].forEach(logArrayElements);

// a[0] = 2
// a[1] = 5
// a[3] = 9

[2, 5,"" ,9].forEach(logArrayElements);
// a[0] = 2
// a[1] = 5
// a[2] = 
// a[3] = 9

[2, 5, undefined ,9].forEach(logArrayElements);
// a[0] = 2
// a[1] = 5
// a[2] = undefined
// a[3] = 9


let xxx;
// undefined

[2, 5, xxx ,9].forEach(logArrayElements);
// a[0] = 2
// a[1] = 5
// a[2] = undefined
// a[3] = 9
使用thisArg
此处依靠一个数组来更新实例上的sum和count属性:
function Counter() {
    this.sum = 0;
    this.count = 0;
}

Counter.prototype.add = function(array) {
    array.forEach(function(entry) {
        this.sum += entry;
        ++this.count;
    }, this);
    //console.log(this);
};

var obj = new Counter();
obj.add([1, 3, 5, 7]);

obj.count; 
// 4 === (1+1+1+1)
obj.sum;
// 16 === (1+3+5+7)
因为thisArg参数 (this) 传给了forEach(),每次调用时,它都被传给callback函数,作为它的this值。
注意:如果使用箭头函数表达式传入函数参数,thisArg 参数会被忽略,因为箭头函数在词法上绑定了this值。
对象复制函数
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
如果数组在迭代时被修改了,则其他元素会被跳过
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版中指定的算法。算法假定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") {// callback不是函数,抛出错误
        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) {//存下第二个参数this值,如果没有就是undefined
        T = thisArg;
      }
  
      // 6. Let k be 0
      k = 0;//循环索引,起始为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);//指定this调用callback
        }
        // d. Increase k by 1.
        k++;
      }
      // 8. return undefined
    };
  }

 

posted @ 2018-07-01 20:19  hahazexia  阅读(648)  评论(0)    收藏  举报