billfeller.github.io icon indicating copy to clipboard operation
billfeller.github.io copied to clipboard

JS遍历数组的3种不同实现与比较

Open billfeller opened this issue 6 years ago • 0 comments

实现说明

JS遍历数据有3中不同实现:

  1. Loop: for
  2. Map: array.prototype.map
  3. forEach: array.prototype.forEach

示例: image source: https://jsperf.com/loop-vs-map-vs-foreach

可以看到,for 实现最快,map,forEach有一定的性能损耗;但是,map,forEach 代码较 for 简洁;

map与forEach区别

首先看下MDN对于二者的定义:

The map() method creates a new array with the results of calling a provided function on every element in the calling array. var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])

The forEach() method executes a provided function once for each array element. arr.forEach(function callback(currentValue[, index[, array]]) { //your iterator }[, thisArg]);

可以看到,二者的区别在于map()返回新数组,而forEach()返回undefined。

比较二者的 Array.prototype.forEach 与 Array.prototype.map polyfill源码实现:

// 实现 ECMA-262, Edition 5, 15.4.4.18
// 参考: 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. 将O赋值为调用map方法的数组.
    var O = Object(this);

    // 2.将len赋值为数组O的长度.
    var len = O.length >>> 0;

    // 3.如果callback不是函数,则抛出TypeError异常.
    // See: http://es5.github.com/#x9.11
    if (typeof callback !== "function") {
      throw new TypeError(callback + ' is not a function');
    }

    // 4. 如果参数thisArg有值,则将T赋值为thisArg;否则T为undefined.
    if (arguments.length > 1) {
      T = thisArg;
    }

    // 5. 将k赋值为0
    k = 0;

    // 6. 当 k < len 时,执行循环.
    while (k < len) {

      var kValue;

      //遍历O,k为原数组索引
      if (k in O) {

        //kValue为索引k对应的值.
        kValue = O[k];

        // 执行callback,this指向T,参数有三个.分别是kValue:值,k:索引,O:原数组.
        callback.call(T, kValue, k, O);
      }
      // k自增1
      k++;
    }
    // 7. 返回undefined
  };
}
// 实现 ECMA-262, Edition 5, 15.4.4.19
// 参考: http://es5.github.com/#x15.4.4.19
if (!Array.prototype.map) {
  Array.prototype.map = function(callback, thisArg) {

    var T, A, k;

    if (this == null) {
      throw new TypeError(" this is null or not defined");
    }

    // 1. 将O赋值为调用map方法的数组.
    var O = Object(this);

    // 2.将len赋值为数组O的长度.
    var len = O.length >>> 0;

    // 3.如果callback不是函数,则抛出TypeError异常.
    if (Object.prototype.toString.call(callback) != "[object Function]") {
      throw new TypeError(callback + " is not a function");
    }

    // 4. 如果参数thisArg有值,则将T赋值为thisArg;否则T为undefined.
    if (thisArg) {
      T = thisArg;
    }

    // 5. 创建新数组A,长度为原数组O长度len
    A = new Array(len);

    // 6. 将k赋值为0
    k = 0;

    // 7. 当 k < len 时,执行循环.
    while(k < len) {

      var kValue, mappedValue;

      //遍历O,k为原数组索引
      if (k in O) {

        //kValue为索引k对应的值.
        kValue = O[ k ];

        // 执行callback,this指向T,参数有三个.分别是kValue:值,k:索引,O:原数组.
        mappedValue = callback.call(T, kValue, k, O);

        // 返回值添加到新数组A中.
        A[ k ] = mappedValue;
      }
      // k自增1
      k++;
    }

    // 8. 返回新数组A
    return A;
  };      
}

billfeller avatar Apr 02 '18 09:04 billfeller