reduce()
干嘛的?
reduce()方法对数组中的每个元素执行一个自己提供的 reducer 函数(升序执行),将其结果汇总为单个返回值。
demo
const arr = [1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 1+2+3+4
console.log(arr.reduce(reducer));
// expected output: 10
// 5+1+2+3+4
console.log(arr.reduce(reducer))
// expected output: 15
语法
arr.reduce(callback(accumulator, currentValue[, index[, array]])[,initialValue])
callback
callback 执行数组中每个值(如果没有提供 initialValue 则第一个值除外)的函数,包含四个参数
| callback 的参数 | name | 是否可选 |
|---|---|---|
| accumulator | 累计器累计回调的返回值;上一次调用时返回的累计值,或 initialValue | false |
| currentValue | 数组中正在处理的元素 | false |
| index | 数组中正在处理的当前元素的索引。如果提供了 initialValue,则起始索引号为 0,否则从索引 1 起始 | true |
| array | 调用 reduce()的数组 | true |
initialValue
作为第一次调用 callback 函数时的第一个参数的值。如果没有提供初始值,则将使用数组中的第一个元素。在没有初始值的空数组上调用 reduce 将报错
返回值
函数累计处理的结果
注意
如果没有提供 initialValue,reduce 会从索引 1 的地方开始执行 callback,跳过第一个索引。如果提供 initialValue,从索引 0 开始
如果数组为空且没有提供 initialValue,会抛出 TypeError 。如果数组仅有一个元素(无论位置如何)并且没有提供 initialValue, 或者有提供 initialValue 但是数组为空,那么此唯一值将被返回并且 callback 不会被执行。
| name | 有 initialValue | 无 initialValue |
|---|---|---|
| 空数组 | initialValue 作为唯一值,callback 不执行 | 抛出 TypeError |
| 数组仅有一个元素 | 据情况 | 数组中的元素作为唯一值,callback 不执行 |
demo2
var maxCallback = (acc, cur) => Math.max( acc.x, cur.x);
// reduce() 没有初始值
[{x: 2},{x: 22},{x:42}].reduce(maxCallback)
/**
第一次
acc {x:2}
cur {x:22}
Math.max(2,22) = 22
第二次
acc 22
cur {x:42}
所以 acc.x : undefined
Math.max(undefined,数值) : undefined
**/
[{x: 2},{x: 22}].reduce(maxCallback) // 22
/**
第一次
acc {x:2}
cur {x:22}
Math.max(2,22) = 22
**/
[{x:2}].reduce(maxCallback); // {x:2}
// 数组仅有一个元素+无initialValue,那么此唯一值将被返回并且 callback 不会被执行
[].reduce(maxCallback); // TypeError
// 空数组+无initialValue,抛出TypeError
var maxCallback2 = (max, cur) => Math.max(max, cur)
// map/reduce; 这是更好的方案,即使传入空数组或更大数组也可正常执行
[ { x: 22 }, { x: 42 } ].map( el => el.x )
.reduce( maxCallback2, -Infinity );
// -Infinity无穷小,任何数都比它大,写在这,也保证了有initialValue
可用场景
- 数组里所有值的和
- 累加对象数组里的值
- 计算数组中每个元素出现的次数
- 数组去重
- .....
计算数组中每个元素出现的次数
var names = ['Alice','Bob','Tiff','Bruce','Alice'];
var countedNames = names.reduce(function(allNames, name){
console.log("allNames,name", allNames, name);
/**
allNames,name {} Alice
allNames,name {Alice: 1} Bob
allNames,name {Alice: 1, Bob: 1} Tiff
allNames,name {Alice: 1, Bob: 1, Tiff: 1} Bruce
allNames,name {Alice: 1, Bob: 1, Tiff: 1, Bruce: 1} Alice
**/
if(name in allNames){
allNames[name]++;
}
else {
allNames[name] = 1;
}
return allNames;
},{})
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }