数组拓展方法(二):every、some、reduce

915 阅读5分钟

一. 高阶函数和回调函数

高阶函数: function(fn) 括号里面有函数类型的参数 或者 返回的值是一个函数

     //
      function every(fn){
          fn(); //当一个函数执行的时候,它的参数是一个函数,执行时或者执行后,这个函数又执行了, 这个函数就是原函数的回调函数
         //所以回调函数属于高阶函数?
      }
//

回调函数: 把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。如果代码立即被执行就称为同步回调,如果在之后晚点的某个时间再执行,则称之为异步回调。

疑问

回调函数属于高阶函数范畴?

二. every: 判断数组元素有无

1.every也是原型链上的方法:Array.prototype.every
2.every()语句中,如果有一个不满足条件就停止遍历,条件就是return后面的表达式

var data = [{'name':'0', 'age':'18'}, {'name':'1', 'age':'28'}, {'name':'2', 'age':'28'} ]
var res = data.every(function(elem, index, arr){
    console.log(elem);     // 仅输出 {'name':'0', 'age':'18'}, {'name':'1', 'age':'28'}
    // 因为到第二个就return false了
    return elem.name == '0'
    console.log(elem);  //不会输出, 他会一步步遍历,到return 就戛然而止,不管return后面表达式是否正确,如果正确就遍历下一句,如果不正确就直接结束遍历, 
    //所以说不管return的表达式正确与否,其后面的所有语句都不会执行,正确了就再次遍历,不正确就结束遍历 
})

所以说不管return的表达式正确与否,其后面的所有语句都不会执行,正确了就再次遍历,不正确就结束遍历

  1. every() 方法用于检测数组所有元素是否都符合指定条件(通过函数提供)。

     var data = [{ 'name': '0', 'age': '18' }, { 'name': '1', 'age': '28' }, { 'name': '2', 'age': '28' }]
     var res = data.every(function (elem, index, arr) {
         // 因为到第二个就return false了
         return elem.name == '0'
     })
     console.log(res); //输出false
    

every的重写

 Array.prototype.myEvery = function (fn) {
            var arr = this, //data中的数据
                len = arr.length,
                args2 = arguments[1] || window,
                res = true;

            for (var i = 0; i < len; i++) {
                if (!fn.apply(args2, [arr[i], i, arr])) {
                    res = false;
                    break;
                }
            }
            return res;
        }

        var data = [ { name: '1', age: '28' }, { name: '2', age: '28' }];
        var res2 = data.myEvery(function (elem, index, array) {
            console.log(elem);
            return elem.name == '1' //判断data的数据, 如果==0 只会输出data的第一项, ==1输出两项 ,因为判断语句在log之后,总会输出一句
        }, { name: 'jack' })    
        //所以说every()括号内配合  return elem.xx == 'xx' 使用
        //return比较的是data中的值,那此句中argus2有什么用?

所以说every()括号内配合 return elem.xx == 'xx' 使用,比较data中的数据
但是argus2的作用是什么? 知道特性就好,以后箭头函数等会有用

三. some

其功能和every差不多,也是判断数组有无此元素
唯一的区别就是: 如果有一个满足条件就停止遍历,条件是return后面的表达式

通俗的讲: some 遇到正确的就停止, 遇到不正确的就一直循环下去,直至找到正确的
而every 第一项不是正确的就会停止遍历,第一项正确就会继续遍历

some重写

        Array.prototype.mySome = function (fn) {
            var arr = this, //data中的数据
                len = arr.length,
                args2 = arguments[1] || window,
                res = false;

            for (var i = 0; i < len; i++) {
                if (fn.apply(args2, [arr[i], i, arr])) {
                    res = true;
                    break;
                }
            }
            return res;
        }

        var data = [ { name: '1', age: '28' }, { name: '2', age: '28' }, { name: '23', age: '28' }];
        var res2 = data.mySome(function (elem, index, array) {
            console.log(elem);
            return elem.name == '0' //输出{ name: '1', age: '28' }, { name: '2', age: '28' }, { name: '23', age: '28' }
        }, { name: 'jack' })    
   //遇到正确的就停止,遇到不正确的就一直循环下去,直至找到正确的

四. reduce: 完成数据归纳

返回一个新的数组,把符合条件的值放入新数组中
reduce的第二个参数必须填

 var data = [ { name: '1', age: '28' }, { name: '2', age: '28' }, { name: '23', age: '28' }];
var initialValue = [];
var  nArr = data.reduce(function(prev, elem, index, arr){ //将prev与其他三项分开来看
   console.log(prev === initialValue);// 返回true,所以prev和initialValue是一个东西
    return prev; //必须return prev; 才会有值, 不然会输出3次undefined (次数为数组中元素的个数,即遍历的次数)

}, initialValue)

1.prev和initialValue是一个东西, prev的值从initialValue中获取, 所以第二个参数必须填
2.必须return才会有值,不然之后的返回会几次false(次数为数组中元素的个数 - 1,因为第一次返回true)

   var data = [{ name: '1', age: '28' }, { name: '2', age: '28' }, { name: '23', age: '28' }];
   var initialValue = [];
   var nArr = data.reduce(function (prev, elem, index, arr) { //将prev与其他三项分开来看
       prev.push(elem.name); // 每执行data.reduce 的时候都会操作一遍prev.push();
       console.log(prev); //输出["1"] 、  ["1", "2"]、 ["1", "2", "23"]
       return prev;

   }, initialValue)

每执行data.reduce 的时候 都会操作一遍prev.push();
reduce的本质其实也是遍历, 会执行一次次里面的语句

  var data = [{ name: '1', age: '28' }, { name: '2', age: '28' }, { name: '23', age: '28' }];
   var initialValue = [];
   var nArr = data.reduce(function (prev, elem, index, arr) { //将prev与其他三项分开来看
     
      if(elem.name === '1'){
          prev.push(elem);
       
      }
       return prev; //返回给新数组nArr

   }, initialValue)
  console.log(nArr); //输出{name: "1", age: "28"}

所以说reduce 和 filter 的作用是一样的,但是方法不同: reduce是用归纳的方式来做的,而filter是过滤
过滤: 返回布尔值是true就返回, 不是就扔掉
归纳: 在我的条件范围之内,往一个新容器里面放值(并不是原来的那个容器) 即将符合条件的全部放入新的容器中

  var data = [{ name: '1', age: '28' }, { name: '2', age: '28' }, { name: '23', age: '28' }];
   var initialValue = [];
   var nArr = data.reduceRight(function (prev, elem, index, arr) { //将prev与其他三项分开来看
          prev.push(elem);
       //    console.log(elem);
       return prev;

   }, initialValue)
   console.log(nArr); //输出: {name: "23", age: "28"} 、{name: "2", age: "28"}、{name: "1", age: "28"}
   

所以说reduceRight 和 reduce 唯一的不同就是返回结果是倒着来的 reduceRight就是倒序列

总结

所有的数组拓展方法本质都是 遍历、循环
reduce是从0开始遍历,reduceRight是从 长度(data.length-1) 开始遍历

reduce 实战

将cookie数据的" name= 1; name2= 2 ; name3= 23 " 形式改为对象的形式

    
var cookieData =  " name= 1;  name2= 2 ;  name3= 23 " 
cookieArr = cookieData.split(";");
// console.log(cookieArr); //数组,输出 [ name= 1", "  name2= 2 ", "  name3= 23 " ]

var nArr = cookieArr.reduce(function(prev, elem){
    var item = elem.split('=');
       console.log(item); //返回三个数组
    // [" name", " 1"]     ["  name2", " 2 "]   ["  name3", " 23 "]
    
     prev [item[0]] = item[1];
     return prev;
}, {})                //因为prev为空对象,所以nArr返回一个对象
console.log(nArr); 
//输出{" name": " 1", "  name2": " 2 ", "  name3": " 23 "}

reduce一次次遍历,把他们分隔开:[" name", " 1"] [" name2", " 2 "] [" name3", " 23 "]
同时 prev [item[0]] = item[1]: 因为prev为一个对象,它把数组的第0项变为了键名,第一项变为键值,
所以才会呈现 name : 1 的形式

reduce的重写


   Array.prototype.myReduce = function (fn, init) {
       var arr = this,
           len = arr.length,
           args2 = arguments[2] || window;  //对应init的下标


       for (var i = 0; i < len; i++) {
           init = fn.apply(args2, [init, arr[i], i, arr]);
       }
       return init;
   }


   var cookieData = " name= 1;  name2= 2 ;  name3= 23 "
   cookieArr = cookieData.split(";");
   var nArr = cookieArr.myReduce(function (prev, elem) {
       var item = elem.split('=');
       // console.log(item); //返回三个数组
       prev[item[0]] = item[1];
       return prev;
   }, {})
   console.log(nArr);

以上输出{" name": " 1", " name2": " 2 ", " name3": " 23 "}
1. args2 = arguments[2] || window; 是因为上面的参数有两个:第二个参数是init, 第三个参数才是{}
2. init = fn.apply(args2, [init, arr[i], i, arr]); 将 function 中返回的值 全部放入第二个参数 { } 中, 返回第二个参数

reduceRight重写

唯一的不同就是for循环里面

       for (var i = len - 1; i >= 0; i--) {
            init = fn.apply(args2, [init, arr[i], i, arr]);
        }
        return init;
    }

五. 难点

elem是调用数组中的元素,和this指向无关,所以不可能是第二个参数的数组元素, 此问题困扰了很久,最近才整明白