本文转载自我的个人博客。
这是一篇译文,有兴趣的同学可以阅读官方文档,图省事的同学可以直接看这篇精简版的译文。
reduce()
是一个数组方法,它接受一个由你定义回调函数,在数组的每一项上都会执行这个回调函数,最终返回一个值。
我们来先看两个例子感受一下:
const array1 = [1, 2, 3, 4];
//可以自己定义的回调函数
const reducer = (accumulator, currentValue) => accumulator + currentValue;
// 例子一
console.log(array1.reduce(reducer));
// 1 + 2 + 3 + 4
// 输出: 10
// 例子二
console.log(array1.reduce(reducer, 5));
// 5 + 1 + 2 + 3 + 4
// 输出: 15
语法
arr.reduce(callback[, initialValue])
reduce()
方法本身接受两个参数,callback
(回调函数),和initialValue
(初始值)。而回调函数本身又可以四个参数,所以整体的参数表如下所示:
-
callback
回调函数,数组中的每一项元素都将被执行此回调函数,接受四个参数:-
accumulator
存放的是每一次调用回调函数的返回值 -
currentValue
当前正在处理的数组元素 -
currentIndex
(可选)
当前正在处理的数组元素的index
-
array
(可选)
调用reduce()
的数组
-
-
initialValue
(可选)
如果提供了initialValue
,那么它将作为第一次调用回调函数时的第一个参数,如果没有提供,数组中的第一个元素将会被作为initialValue
。如果调用reduce()
的数组是一个空数组,又没有提供initialValue
,程序将会报错。
举两个例子,我们来看一下在有和没有initialValue
的时候,每执行一次回调函数,各参数的变化:
例一,没有定义 initialValue 的情况
const array1 = [0, 1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
console.log(array1.reduce(reducer));
callback | accumulator | currentValue | currentIndex | array | return value |
---|---|---|---|---|---|
first call | 0 | 1 | 1 | [0, 1, 2, 3, 4] | 1 |
second call | 1 | 2 | 2 | [0, 1, 2, 3, 4] | 3 |
third call | 3 | 3 | 3 | [0, 1, 2, 3, 4] | 6 |
fourth call | 6 | 4 | 4 | [0, 1, 2, 3, 4] | 10 |
由于没有定义initialValue
,在第一次执行累加时就需要数组中的第一项和第二项,第一项的值赋给了accumulator
,真正的累加从第二项开始。所以虽然数组中有五项,但是回调函数一共只被执行了四次。
例二,定义了initialValue 的情况
const array1 = [0, 1, 2, 3, 4];
const reducer = (accumulator, currentValue) => accumulator + currentValue;
//initialValue = 10
console.log(array1.reduce(reducer, 10));
callback | accumulator | currentValue | currentIndex | array | return value |
---|---|---|---|---|---|
first call | 10 | 0 | 0 | [0, 1, 2, 3, 4] | 10 |
second call | 10 | 1 | 1 | [0, 1, 2, 3, 4] | 11 |
third call | 11 | 2 | 2 | [0, 1, 2, 3, 4] | 13 |
fourth call | 13 | 3 | 3 | [0, 1, 2, 3, 4] | 16 |
fifth call | 16 | 4 | 4 | [0, 1, 2, 3, 4] | 20 |
在回调函数第一次执行时,initialValue
的值被赋给accumulator
,累加从数组的第一项开始执行,所以回调函数一共被执行了五次。
注意事项
如果调用reduce()
的数组是一个空数组,又没有提供initialValue
,程序将会报错。
提供initialValue
是一个更安全的做法。在某些情况下,如果不提供initialValue
,输出将具有不确定性,比如下面这个例子:
var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
var maxCallback2 = ( max, cur ) => Math.max( max, cur );
// 没有提供 initialValue
[ { x: 22 }, { x: 42 } ].reduce( maxCallback ); // 42
[ { x: 22 } ].reduce( maxCallback ); // { x: 22 }
[ ].reduce( maxCallback ); // TypeError
// 提供了initialValue
// map 与 reduce 的合用,这是一个更安全的策略,这样即使是空数组也不会报错
[ { x: 22 }, { x: 42 } ].map( el => el.x )
.reduce( maxCallback2, -Infinity );
更多的例子
reduce()
可以用在很多地方:
数组求和:
var total = [ 0, 1, 2, 3 ].reduce(
( accumulator, currentValue ) => accumulator + currentValue,
0
);
对象的属性求和:
var initialValue = 0;
var sum = [{x: 1}, {x: 2}, {x: 3}].reduce(
(accumulator, currentValue) => accumulator + currentValue.x
,initialValue
);
console.log(sum) // logs 6
合并数组:
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
( accumulator, currentValue ) => accumulator.concat(currentValue),
[]
);
计算对象中实例的个数:
var names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
var countedNames = names.reduce(function (allNames, name) {
if (name in allNames) {
allNames[name]++;
}
else {
allNames[name] = 1;
}
return allNames;
}, {});
// countedNames is:
// { 'Alice': 2, 'Bob': 1, 'Tiff': 1, 'Bruce': 1 }
根据对象属性分组:
var people = [
{ name: 'Alice', age: 21 },
{ name: 'Max', age: 20 },
{ name: 'Jane', age: 20 }
];
function groupBy(objectArray, property) {
return objectArray.reduce(function (acc, obj) {
var key = obj[property];
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(obj);
return acc;
}, {});
}
var groupedPeople = groupBy(people, 'age');
// groupedPeople is:
// {
// 20: [
// { name: 'Max', age: 20 },
// { name: 'Jane', age: 20 }
// ],
// 21: [{ name: 'Alice', age: 21 }]
// }
官方文档里还有很多更高级的用法,传送门。