reduce学习笔记

reduce() 方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终为一个值,是es5新增的一个数组逐项处理的方法。
语法:
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
callback:执行数组中每个值的函数,包含四个参数
accumulator:累计器,累计回调的返回值。如何有设置initialValue,则该值为初始值,否则以数组的第一个元素作为初始值。所以,正在没有设置initialValue的空数组上调用reduce将报错
currentValue:当前正在处理的数组元素
currentIndex:当前正在处理的数组元素的索引 - 可选
array:调用reduce方法的数组 - 可选
initialValue:初始值 - 可选
注意:reduce对于空数组是不会执行callback的
下面通过栗子来验证一下:
1、首先验证一下空数组

const arr1 = []
    arr1.reduce((a, b, c, d) => {console.log('a: ' + a, 'b: ' + b, 'c: ' + c, 'd: d)
    })

对于一个空数组,并且没有给初始值的情况下,浏览器会抛出一个类型错误
在这里插入图片描述
给一个初始值,则不会执行该方法:

const arr1 = []
  arr1.reduce((a, b, c, d) => {
    console.log('a: ' + a, 'b: ' + b, 'c: ' + c, 'd: ' + d)
  }, 10)

在这里插入图片描述
可以看到,reduce对于空数组是不会执行callback的。
在平常开发中,reduce可以完成的,for基本也可以做到。那么为什么还要 用reduce?在我看来,他更有阅读性,更优雅,更有逼格。比如一个简单的累加:

const arr1 = [1, 2, 3, 4, 5]
  // for
  let result1 = 0
  for (let i = 0, len = arr1.length; i < len; i++) {
    result1 += arr1[i]
  }
  // reduce
  let result2 = arr1.reduce((a, b) => a + b)
  console.log(result1, result2)

在这里插入图片描述
结果都是一样的,但作为一名程序猿,得有自己的信仰:能用一行代码解决的事情,坚决不用两行代码。
一般用reduce用的比较多的是用它来数组去重,转化数组,或者是对象属性求和。
先来看数组去重:

// 简单数组
  const arr1 = [1, 2, 3, 4, 5, 5, 4, 3, 2, 1]
  let single = arr1.reduce((a, b) => {
    if (!a.includes(b)) {
      return a.concat(b)
    } else {
      return a
    }
  }, [])
  // 复杂数组
  let arr2 = [
    {
      value: 1,
      name: '1'
    },
    {
      value: 2,
      name: '2'
    },
    {
      value: 1,
      name: '1'
    }
  ]
  let hash = {}
  let complex = arr2.reduce((a, b) => { // hash[b.value] ? '' : hash[b.value] = true && a.push(b)
    if (!hash[b.value]) {
      hash[b.value] = true && a.push(b)
    }
    return a
  }, [])
   console.log(single)
   console.log(complex)

在这里插入图片描述
结果可以看到,都达到了去重的目的。其实简单数组的去重一般用的是Array.from(new Set(arr1)) 一行代码搞定。而数组对象的去重就没办法用Set了,所以一般在处理数组对象的去重时才会用到reduce。上面注释的三元表达式其实也是可以的,只是如果项目中用了eslint,直接使用三元表达式会报错,所以改成了if语句,也比较好理解。解释一下这段代码的意思,定义一个空对象hash,reduce方法传入了一个空数组,所以初始值为arr2的第一个元素。下面就是对数组arr2的从右到左的逐项处理,相当于一个遍历,只有在hash[b.value]为false的情况下,才进行下一步操作。下一步操作就是给hash[b.value]赋值,并将b塞到a里面。这样,在下一次遇到同样的b.value时,hash[b.value]就会存在,判断条件就变成了false,不会执行下一步操作,从而达到去重的目的。至于这里的true && a.push(b)为什么不可以改成false && a.push(b),因为这里其实做了一个隐式转换,push方法返回的是数组的长度,用true的话返回的还是该长度值,如果改成false,返回的值就是false了,这样再下一次判断hash[b.value]的时候取到的值就是false,整个判断条件就会变成true,这样就达不到去重的目的了。当然,或许会有一个疑问,既然true在这里只是做一个隐式转换,并且最后的值还不变,为什么还要加这个true呢?这个问题也是困扰了我很久,最后我想,大概是更利于阅读,更优雅,更有逼格吧。
下面说一下转化数组:

// 二维数组
  const arr3 = [[1, 2], [3, 4], [5, 6]]
  let newArr = arr3.reduce((a, b) => {
    return a.concat(b)
  }, [])
  console.log(newArr)
  // 多维数组
  const arr4 = [[1, 2], [3, 4], [5, 6, [7, 8]]]
  const handleGetNewArr = function(arr){
   return arr.reduce((a, b) => a.concat(
     Array.isArray(b) ? handleGetNewArr(b) : b
     ), [])
  }
  console.log(handleGetNewArr(arr4))

在这里插入图片描述
二维数组的基本很好理解,就用了一个es5的方法concat,把二维数组里的每一项拼接起来就形成了一个新的一维数组了。
多维数组的除了concat,还用到了递归,判断需要处理的当前项,即b是否为数组,如果是则递归调用handleGetNewArr方法,否则直接拼接。
最后说一下对象属性求和:

const arr5 = [
    {
      name: '圆珠笔',
      price: 10
    },
    {
      name: '橡皮擦',
      price: 20
    },
    {
      name: '钢笔',
      price: 30
    }
  ]
  const sum = arr5.reduce((a, b) => {
    return a + b.price
  }, 0)
  console.log(sum) // 60

数组对象的属性求和基本就是这样,没什么难理解的地方。
总的来说,reduce能做的事,用其他的遍历方法,如for,map等基本也可以实现。所以平常用的比较多的还是用它来进行数组对象的去重。
当然,还有一个reduceRight的方法,reduce是从右到左逐项处理,reduceRight是从左到右逐项处理,这个基本上没用到。因为两者除了处理顺序的差别之外,没啥区别了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值