目錄
reduce() 方法對數組中的每個元素執行一個由我們提供的reducer函數(升序執行),將其結果匯總為單個返回值。
1. 語法reduce說明
arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])
第一個參數: callback函數
執行數組中每個值 (如果沒有提供 initialValue則第一個值除外
)的函數,包含四個參數:
accumulator
累計器累計回調的返回值; 它是上一次調用回調時返回的累積值,或initialValue
(見於下方)。
currentValue
數組中正在處理的元素。
index
可選
數組中正在處理的當前元素的索引。 如果提供了initialValue
,則起始索引號為0,否則從索引1起始。
array
可選
調用reduce()
的原數組
第二個參數: initialValue
可選
作為第一次調用 callback
函數時的第一個參數的值。 如果沒有提供初始值,則將使用數組中的第一個元素。 在沒有初始值的空數組上調用 reduce 將報錯。
這個看下來都有點蒙,其實就是兩種情況,一種情況是給了初始值,一種是沒提供也就是reduce的第二個參數initialValue
。
reduce
為數組中的每一個元素依次執行callback
函數,不包括數組中被刪除或從未被賦值的元素,接受四個參數:
accumulator 累計器
currentValue 當前值
currentIndex 當前索引
array 數組
回調函數第一次執行時,accumulator
和currentValue
的取值有兩種情況:如果調用reduce()
時提供了initialValue
,accumulator
取值為initialValue
,currentValue
取數組中的第一個值;如果沒有提供 initialValue
,那么accumulator
取數組中的第一個值,currentValue
取數組中的第二個值。
如果沒有提供initialValue
,reduce 會從索引1的地方開始執行 callback 方法,跳過第一個索引。如果提供initialValue
,從索引0開始。
為了理解透舉個栗子:
展示:
1. 無初始值
假如運行下段reduce()
代碼:
[0, 1, 2, 3, 4].reduce(function(accumulator, currentValue, currentIndex, array){
return accumulator + currentValue;
});
callback 被調用四次,每次調用的參數和返回值如下表:
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 |
由reduce
返回的值將是最后一次回調返回值(10)。
2. 添加初始值,這樣子
提供一個初始值作為reduce()
方法的第二個參數,以下是運行過程及結果:
[0, 1, 2, 3, 4].reduce((accumulator, currentValue, currentIndex, array) => { return accumulator + currentValue; }, 10 );
// 提供初始值為 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 |
這種情況下reduce()
返回的值是20
。
這個之后就理解透了。
2. 一些用途
求和
var total = [ 0, 1, 2, 3 ].reduce(
( acc, cur ) => acc + cur,
0
);
// total 6
累加對象里的值
let sum = [{x: 1}, {x:2}, {x:3}].reduce(
(accumulator, currentValue) => accumulator + currentValue.x
,0
);
console.log(sum) // logs 6
二維數組變一維
var flattened = [[0, 1], [2, 3], [4, 5]].reduce(
( acc, cur ) => acc.concat(cur),
[]
);
// [0, 1, 2, 3, 4, 5]
計算數組中每個元素出現的次數
const names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
let 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 }
按屬性對object分類
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 }]
// }
3. 極力避免的情況
未設置初始化值時
var maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
var maxCallback2 = ( max, cur ) => Math.max( max, cur );
// reduce() 沒有初始值
console.log([ { x: 2 }, { x: 22 }, { x: 42 } ].reduce( maxCallback ));
// NaN
// 返回NaN的原因是在第二次的時候返回值是22 而22再 22.x就不行了。
console.log([ { x: 2 }, { x: 22 } ].reduce( maxCallback ));
// 22 原因是只執行一次 這也是幸運了
console.log([ { x: 2 } ].reduce( maxCallback )); // { x: 2 }
console.log([ ].reduce( maxCallback ));
// TypeError
// 如果數組為空且沒有提供initialValue,會拋出TypeError
// map/reduce; 這是更好的方案,即使傳入空數組或更大數組也可正常執行
[ { x: 22 }, { x: 42 } ].map( el => el.x )
.reduce( maxCallback2, -Infinity );