数组拓展方法(一):forEach、filter、map

758 阅读3分钟

数组拓展方法是从ES5中才使用的

一. JSON与数组的联系

知识回顾:很多大的项目JSON数据存在HTML中,减少了对服务器的请求
同时以数组的形式存储多项JSON数据:[{……},{……},{……}],这样的方式称之为 JSON对象集合 也叫 JSON数据集合

这样的话就形成了一套数据结构 ( 对象中各项打上引号、 []存储多项对象 ),这种数据结构称之为JSON
JSON数据就是对象,JSON数据集合就是数组

数组里面每一个值 称之为 数组元素 element

二. forEach(): 遍历

1. forEach()

它是遍历数组的方法: forEach() 方法用于调用数组的每个元素,并将元素传递给回调函数。

 data = [1, 2, 3];
       data.forEach(myFunction);

       function myFunction(item) {
           var sum = 0;
           for (var i = 0; i < data.length; i++) {
               sum = sum + data[i];  //sum = 1 + 2 + 3
           }
           console.log(sum);
       }

因为data中有三个元素,所以遍历function三次,输出 6 三次

forEach()是数组的原型上面的方法(Array.prototype)
所以说只能数组用这个方法,对象等不能用

forEach()每遍历出一个元素就会执行一次function-->需要提供三个参数:

data.forEach(function(elem, index, ayyay){ /数组元素, 索引, 正在遍历的数组
 console.log(this);
});

elem:当前数组的元素
index: 当前元素的下标
array:正在遍历的数组

因为数组里面每一个值 称之为 数组元素 element
所以在forEach()中常把 data[i]写成 elem, 把 i 写成 index
同时这里面的this指向window
同时forEach()的 括号里面有两个参数, 第二个参数更改函数内部的this指向

因为 var data = [ ' 1 ' , ' 2 ' ]; ,有两个元素, 所以以下结果输出两次:

  <script>
        var data = ['1', '2'];
        data.forEach(function (elem, index, ayyay) {
            console.log(this);
        },{name: 'test'});      /返回:{name: "test"}
        
        
          var data = ['1', '2'];
        data.forEach(function (elem, index, ayyay) {
            console.log(this);
        },1);                  /返回 Number {1}
       
        
          var data = ['1', '2'];
        data.forEach(function (elem, index, ayyay) {
            console.log(this);
        }, false);                    /返回 Boolean {false}
   
   
     var data = ['1', '2'];
        data.forEach(function (elem, index, ayyay) {
            console.log(this);
        },undefined);      /Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
    </script>

因为this 一定会指向一个对象, 所以填1、false等会强制给它转换成对象形式(包装类知识), 所以第二个参数填null、undefined 时不成功

2. forEach()方法的重写

重写: 详细写该方法的原理

    <script>
        var data = [1, 2, 3];
        var fn = function (elem, index, array) {
            console.log(this);  //指向data,因为data调用此方法; 经过apply后指向第二个实参       
        }

        Array.prototype.myForEach = function (fn) {
            var arr = this,  //因为是数组去调用自定义的方法,所以this指向该数组, 在方法中声明一个变量arr 去保存this
                len = arr.length,
                args2 = arguments[1] || window; //如果传入了第二个参数 就将其保存到args2, 如果没传,args就保存window


            for (var i = 0; i < len; i++) {
                fn.apply(args2, [arr[i], i, arr]); //每循环一次 都要遍历这个函数fn,  
                //使用apply 改变该函数的指向, 原本fn指向window, apply之后指向参数args2, 即下面的Sum
            }
        }

        var a = 1;

        data.myForEach(fn, a); //输出三个 Number {1}
 //当a = undefined时,输出三个:Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
    </script>

总结:forEach()有两个功能:

  • 遍历: 调用的数组有几个元素,就遍历几次
  • 将第二个实参调入第一个实参函数中

三. filter(): 筛选、过滤

与forEach() 最大的不同是 它会返回一个新的数组,即不会改变原数组:

   <script>
        var data = [{ 'name': '0', 'age': '18' }];

        var newArr = data.filter(function (elem, index, array) {
            return elem.name === '0';  //正确就返回data ,错误就返回空

        })
        console.log(newArr);
    </script>

该方法通过返回的值是 true还是false 来判断哪些数据需要放到新数组中去: 上式用形参function来和data比较,有和data相同的话就把data赋给 newArr

filter()方法的重写

<script>
    var data = [{ 'name': '0', 'age': '18' },{ 'name': '0', 'age': '8' }, {'name': '1', 'age': '8'} ];

var fn = function (elem, index, array) {
    return elem.name === '0';
}

Array.prototype.myFilter = function (fn) {
    var arr = this,
        len = arr.length,
        args2 = arguments[1] || window,
        newArr = [];

    for (var i = 0; i < len; i++) {
        fn.apply(args2, [arr[i], i, arr]) ? newArr.push(arr[i]) : " "; 
        //加的是arr[i], 和args2无关
    }
    return newArr
}
var newARR = data.myFilter(fn, [{ 'name': 'jack', 'age': '100' }]);
console.log(newARR); //输出{ 'name': '0', 'age': '18' },{ 'name': '0', 'age': '8' }
    </script>

对应的判断语句在fn的函数内,apply并无什么用,因为window的存在,只要符合fn中的判断语句,就会一直执行,不管第二个实参的个数。同时,符合条件的项将数组元素(只是data中的) arr[i]放入新数组中( newArr.push(arr[i]) )
所以第二个参数并不参与进去,以后箭头函数分析this指向等才会有用 , 这才是可填可不填的真谛

四. map: 映射

map() 方法返回一个新数组,数组中的元素为原始数组元素调用函数处理后的值。 并不会改变原数组的值

  <script>
        var data = [{ 'name': '0', 'age': '18' }];
        var newArr = data.map(function (elem, index, arr) {
            elem.name = this.name + elem.name;
            // this.name 就是第二个形参的值, this指向第二个形参
            return elem;
        }, { 'name': '[JS前端]' })
        console.log(newArr);     
    </script>

上式输出 {name: "[JS前端]0", age: "18"}
可以得出结论: map的作用就是将参数的值放入到数据中: data.map(fn, args2)

2. map() 方法 重写

        var data = [{ 'name': '0', 'age': '18' }];
        Array.prototype.myMap = function (fn) {
            var arr = this,
                len = arr.length,
                arg2 = arguments[1] || window,
                nArr = [],
                item;

            for (var i = 0; i < len; i++) {
                item = arr[i];
       nArr.push(fn.apply(arg2, [item, i, arr]));  //唯一的不同就是for循环,for循环决定该方法的特性
            }
            return nArr;
        }

        var fn = function (elem, index, Array) {
            elem.name = this.name + elem.name;
            return elem;
        }
        var newArr = data.myMap(fn, { name: '[JS前端]' });
        console.log(newArr);

输出 : { name: "[JS前端]0", age: "18" }
所以map可以修改数据,又能添加数据

五. 总结

这些重写方法 唯一的不同就是for循环, for循环决定该方法的特性