在JavaScript数组函数中有一个reduce函数,和filter、map、forEach等方法略有不同,虽然中间也是有内部循环,但reduce多了一层递归。先看看reduce函数接收的参数。
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
函数规范中,第一个参数是一个回调、第二个参数是一个初始值,先来看看回调。
- callbackfn:一个回调函数,有四个回调参数分别是:上一个返回值,当前值,当前索引、操作数组。
后面是哪个很好理解,与filter、map等方法完全一样,但就第一个参数——上一个返回值,这是什么?这里就是reduce独特之处,这个回调参数就是递归出来返回的结果,reduce没遍历一次数组,便将返回值当做回调函数的第一个参数在此输出(previousValue)。
所以此处就特别注意:长度为0的数组是不能调用reduce函数的,因为reduce会默认遍历+递归一次,意味着必须用到previousValue
和currentValue
两个参数。 - initialValue:初始值
当没有传递初始值时,reduce函数会自动遍历+递归一遍,previousValue
回调参数为数组的第一个值,currentValue
这个回调参数也会从数组的第二个值开始读取。若传递了初始值,previousValue
为传递的默认值currentValue
也会从数字的第1个值开始读取。
下面我简单的写了一个基于reduce处理数组的工具类。包含查重、计数、求和、降维操作。
class ArrUtils {
constructor(arr = []) {
this.arr = arr
}
// 数组求和
sum(arr = this.arr) {
return arr.reduce((previousValue, currentValue) => previousValue + currentValue)
}
// 数组中对象求和 key -> 对象中的键名称
sumOfObject(key, arr = this.arr) {
return arr.reduce((previousValue, currentValue) => previousValue + currentValue[key], 0)
}
// 统计
count(arr = this.arr) {
return arr.reduce((previousValue, currentValue) => {
previousValue[currentValue] = currentValue in previousValue ? previousValue[currentValue] + 1 : 1
return previousValue
}, {})
}
// 查重
checkRepeat(arr = this.arr) {
return arr.reduce((previousValue, currentValue) => {
return !previousValue.includes(currentValue) ? previousValue.concat(currentValue) : previousValue
}, [])
}
// 扁平化
flat(arr = this.arr) {
return arr.reduce((previousValue, currentValue) => {
return previousValue.concat(Array.isArray(currentValue) ? this.flat(currentValue) : currentValue)
}, [])
}
}
let objArr = [
{ v: 1 },
{ v: 2 },
{ v: 5 }
]
const arrUtils = new ArrUtils(objArr)
console.log(arrUtils.sumOfObject('v')) // 8
就目前来看使用reduce的场景还是比较多的,尤其是在降维方面,代码简洁,执行率高。