一、定义
reduce()
方法对数组中的每个元素执行一个由您提供的reducer
函数(升序执行),将其结果汇总为单个返回值。也就是说可以在给定初值的情况下,遍历数组中所有元素,执行reducer函数,并返回结果。
二、用法
arr.reduce(callback[, initialValue])
-
callback
接收4个参数:您的 reducer 函数的返回值分配给累计器,该返回值在数组的每个迭代中被记住,并最后成为最终的单个结果值。
reducer = (acc, cur[, idx[, src]]) => { // function // 返回函数累计处理的结果 return acc }
-
Accumulator (acc)
(累计器)累计器累计回调的返回值; 它是上一次调用回调时返回的累积值,或
initialValue
(见于下方)。- 传入
initialValue
时,则acc
=initialvalue
,遍历从第0个元素开始 - 未传入
initialValue
时,则acc
=arr[0]
,遍历从第1个元素开始
let arr = [1,2,3,4,5] arr.reduce((acc, cur, idx) => { // 未传入初值,遍历从第1个元素开始,第一次时acc=1,cur=2 // 1,3,6,10,15 console.log(acc) return acc += cur }) arr.reduce((acc, cur, idx) => { // 传入初值0,遍历从第1个元素开始,第一次遍历时acc=0,cur=1 // 0,1,3,6,10,15 console.log(acc) return acc += cur }, 0)
- 传入
-
Current Value (cur)
(当前值)数组中正在处理的元素。
- 传入
initialValue
时,则cur
=arr[0]
,遍历从第0个元素开始 - 未传入
initialValue
时,则crr
=arr[1]
,遍历从第1个元素开始
let arr = [1,2,3,4,5] arr.reduce((acc, cur, idx) => { // 未传入初值,遍历从第1个元素开始 // 2,3,4,5 console.log(cur) }) arr.reduce((acc, cur, idx) => { // 传入初值0,遍历从第1个元素开始 // 1,2,3,4,5 console.log(cur) }, 0)
- 传入
-
-
Current Index (idx)
(当前索引)[可选]数组中正在处理的当前元素的索引。 如果提供了
initialValue
,则起始索引号为0,否则为1let arr = [1,2,3,4,5] arr.reduce((acc, cur, idx) => { // 未传入初值,遍历从第1个元素开始 // 1,2,3,4 console.log(idx) }) arr.reduce((acc, cur, idx) => { // 传入初值0,遍历从第1个元素开始 // 0,1,2,3,4 console.log(idx) }, 0)
-
Source Array (src) (源数组)[可选]
调用
reduce()
的数组 -
initialValue
初始值作为第一次调用
callback
函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用reduce
将报错。
三、注意
reduce为数组中的每一个元素依次执行
callback
函数,不包括数组中被删除或从未被赋值的元素let arr = [1,2,,4,5] // undefined、null都视为被赋值 arr.reduce((acc, cur, idx) => { // 未传入初值 // 1,3,4 console.log(idx) }) arr.reduce((acc, cur, idx) => { // 0,1,3,4 console.log(idx) }, 0)
在没有初始值的空数组上调用
reduce
将报错[].reduce((acc, cur, idx) => { // callback未执行,但有初始值,故返回初始值 console.log(idx) }, 0) [].reduce((acc, cur, idx) => { // callback未执行,没有初始值,报错 console.log(idx) }) // Uncaught TypeError: Reduce of empty array with no initial value
未传入初始值,则
callback
执行arr.length-1
次,因为遍历从第1个元素遍历,acc
=arr[0]
;传入初始值,则callback
执行arr.length
次,因为遍历从第0个元素遍历,acc
=initialValue
四、拓展
1.initialValue为对象时,arr该如何变化?
- 传入初值
acc === initValue
,故任何修改都不会影响arr
let arr = [{i: 1}, {i: 2}, {i: 3}]
let initialValue = {i: 0}
arr.reduce((acc, cur, idx) => {
// 直接修改acc, 即操作initialValue
acc.i += cur.i
return acc
}, initialValue)
// arr = [{i: 1}, {i: 2}, {i:3}]
// initialValue = {i:6}
- 未传入初值
acc === arr[0]
,对acc
做任何运算,即相当于操作arr[0]
let arr = [{i: 1}, {i: 2}, {i: 3}]
arr.reduce((acc, cur, idx) => {
// 直接修改acc
acc.i += cur.i
return acc
})
// arr = [{i: 6}, {i: 2}, {i:3}]
// {i:6}
解决思路:不直接修改acc
let arr = [{i: 1}, {i: 2}, {i: 3}]
arr.reduce((acc, cur, idx) => {
// 返回新对象
return {i: acc.i + cur.i}
})
// arr = [{i: 1}, {i: 2}, {i:3}]
// {i:6}
结论:提供初始值initialValue
更安全!
五、与其他方法进行对比
数组常用的遍历有
for
、forEach
、map
、reduce
、find
、some
、every
等
1.遍历规则
- 已删除的项
都跳过已删除的项
- 未初始化的项
除了
find
,其他的遍历都会跳过未初始化的项
let a = [1,null,,4,undefined,5,6]
a.find(item => {
console.log(item)
})
// 输出1,null,undefined,4,undefined,5,6
2.返回值
-
for
为代码块,无返回值 -
forEach
无返回值 -
map
返回一个新数组,每个元素都是回调函数的结果。 -
reduce
返回函数累计处理的结果,即acc
-
find
返回数组中第一个满足所提供测试函数的元素的值,否则返回undefined
。 -
some
如果回调函数返回至少一个数组元素的truthy
值,则返回true
;否则为false
。 -
every
如果回调函数的每一次返回都为truthy
值,返回true
,否则返回false
。
六、应用场景
1.求累加和
let arr = [{i: 1}, {i: 2}, {i: 3}]
let initialValue = 0
initialValue = arr.reduce((acc, cur, idx) => {
return acc + cur.i
}, initialValue)
// initialValue = 6
2.将二维数组转换为一维数组
let arr = [[0,1],[2,3],[4,5]]
let initialValue = []
initialValue = arr.reduce((acc, cur, idx) => {
return [...acc,...cur]
}, initialValue)
// initialValue = [0,1,2,3,4,5]
3.计算数组中每个元素出现的次数
let arr = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice']
let initialValue = {}
initialValue = arr.reduce((acc, cur) => {
acc[cur] = (acc[cur] || 0 ) + 1
return acc
}, initialValue )
// initialValue = {Alice: 2, Bob: 1, Tiff: 1, Bruce: 1}
4.按属性对object分类
let arr = [{name: 'a', age: 18}, {name: 'b', age: 19}, {name: 'c', age: 18}, {name: 'd', age: 20}]
let groupBy = (objectArray, property) => {
return objectArray.reduce((acc, cur) => {
// 当前元素存在该属性
if (property in cur) {
acc[cur[property]] = acc[cur[property]] || []
acc[cur[property]].push(cur)
}
return acc
}, {})
}
// 根据age属性对arr分类
groupBy(arr, 'age')
/*
{
18: [{name: 'a', age: 18}, {name: 'c', age: 18}],
19: [{name: 'b', age: 19}],
20: [{name: 'd', age: 20}]
}
*/
5.数组去重
let arr = [1,2,1,2,3,5,4,5,3,4,4,4,4]
arr.sort().reduce((acc, cur) => {
if (!acc.includes(cur)) {
acc.push(cur)
}
return acc
}, [])
// [1,2,3,4,5]
参考网址:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce