数组常用遍历方法的实现原理

357 阅读1分钟

forEach

  • 遍历数组中的每一项值,方法无返回值。方法接收两个参数,第一个参数必选是一个回调函数,函数接收三个参数,第一个为遍历数组中的值,第二个为遍历数组中的索引,第三个为遍历数组本身;第二个参数可选,改变第一个参数内部的this指向。

    var data = [
      {name: 'zhangsan', age: 23},
      {name: 'lisi', age: 24},
      {name: 'wangwu', age: 25}
    ]
    
    data.forEach(function(item, index, arr){
      // 三个参数一般不会全部传入,看你具体的需求,这里的目的是学习/回顾,所以我全写了。
      console.log(item);
      console.log(index);
      console.log(arr);
      console.log(this);
    }, {name: 'zhaoliu'});
    
    // 重写forEach
    Array.prototype.myForEach = function (fn) {
      var arr = this, // 保存调用该函数的数组
          len = arr.length,
          arg = arguments[1] || window; // 第二个参数是改变第一个参数内部的 this 指向,如果没传就是默认指向window
    
      for(var i = 0; i < len; i++){
        fn.apply(arg, [arr[i], i, arr]);
      }
    }
    

filter

  • 筛选过滤数组,和forEach的用法相同,但是有返回值,会将满足回调函数条件的成员组成一个新的数组返回。

    var data = [
      {name: 'zhangsan', age: 23},
      {name: 'lisi', age: 24},
      {name: 'wangwu', age: 25}
    ]
    
    var res = data.filter(function(item, index, arr){
      return item.age > 23;
    })
    console.log(res); // -> [{name: 'lisi', age: 24}, {name: 'wangwu', age: 25}]
    
    // 重写 filter
    Array.prototype.myFilter = function (fn) {
      var arr = this,
          len = arr.length,
          arg = arguments[1] || window,
          res = []; // 因为要有返回值,且返回值是一个数组
      
      for(var i = 0; i < len; i++){
        // 如果返回结果为真 就添加到 res 中,否则什么也不干
        fn.apply(arg, [arr[i], i, arr]) ? res.push(arr[i]) : '';
      }
      return res; // 最后将数组返回
    }
    
  • 注意:原始的filter返回的数组和原始数组也都只是经过浅拷贝,就是数组中的值如果是引用类型的值,修改返回后的数组,原始数组也会改变。 解决方法:在我们自己写的函数往返回的数组添加之前,进行一次深拷贝。

map

  • 用法类似,也是返回一个新数组,数组中的成员为原始数组成员经过回调函数处理后的值,新数组与原始数组也是映射的关系(如果为引用类型值,修改新数组,原始数组也会改变)。

    var data = [
      {name: 'zhangsan', age: 23},
      {name: 'lisi', age: 24}
    ]
    
    var res = data.map(function (item, index, arr) {
      item.name = 'wangwu';
      return item;
    })
    console.log(res, data);
    
    // 重写map
    Array.prototype.myMap = function (fn) {
      var arr = this,
          len = arr.length,
          arg = arguments[1] || window,
          res = [];
    
      for(var i = 0; i < len; i++) {
        // 打断映射关系,可以在这里对原始数组的每一项进行深克隆
        res.push(fn.apply(arg, [arr[i], i, arr]));
      }
      return res;
    }
    

every

  • 返回一个布尔值,数组成员都复合回调函数的条件才返回true,否则返回false

    var data = [14, 18, 22];
    var res = data.every(function (item, index, arr) {
      return item > 18;
    })
    console.log(res); // -> false
    
    // 重写 every
    Array.prototype.myEvery = function (fn) {
      var arr = this,
          len = arr.length,
          arg = arguments[1] || window,
          res = true;
      
      for(var i = 0; i < len; i++){
        // 如果有一个不符合条件,就停止循环直接返回false
        if(!fn.apply(arg, [arr[i], i, arr])){
          res = false;
          break;
        }
      }
      return res;
    }
    

some

  • 也是返回一个布尔值,条件为只要有一个成员满足,就返回true,否则返回false

    var data = [14, 18, 22];
    var res = data.some(function (item, index, arr) {
      return item > 18;
    })
    console.log(res); // -> true
    
    // 重写some
    Array.prototype.mySome = function (fn) {
      var arr = this,
          len = arr.length,
          arg = arguments[1] || window,
          res = false;
      
      for(var i = 0; i < len; i++){
        // 如果有一个符合条件,就停止循环直接返回true
        if(fn.apply(arg, [arr[i], i, arr])){
          res = true;
          break;
        }
      }
      return res;
    }
    

reduce

  • 归纳函数,方法接收两个参数,第一个参数是回调函数,回调有四个参数,第一个previousValue(上一次回调返回的值,或是提供的初始值(initialValue)),第二个currentValue(当前被处理的成员),第三个index(当前成员的索引),第四个array(调用方法的数组本身);方法的第二个参数 initialValue (作为第一回调函数的第一个参数)。返回值就是previousValue / initialValue。

    var data = [
      {name: 'zhangsan', age: 13},
      {name: 'lisi', age: 16},
      {name: 'wangwu', age: 18},
      {name: 'zhaoliu', age: 22}
    ]
    
    var res = data.reduce(function (pre, item, index, arr) {
      if(item.age >= 18){
        pre.push(item);
      }
      return pre;
    },[]);
    console.log(res);
    
    // 重写 reduce 我们给他附加一个功能,如果传入第三个参数,就改变回调内部的this指向
    Array.prototype.myReduce = function (fn, initialValue) {
      var arr = this,
          len = arr.length,
          arg = arguments[2] || window, // 附加功能的参数
          item; 
      
      for(var i = 0; i < len; i++){
        item = arr[i];
        initialValue = fn.apply(arg, [initialValue, item, i, arr]);
      }
      return initialValue;
    }