大家好,我是 CUGGZ。
在 JavaScript 中,reduce 是最難理解的數(shù)組方法之一,它是一個強大而靈活的高階函數(shù),下面就來看看 reduce 的妙用之處!
1. 基本用法
reduce() 是 JavaScript 中一個很有用的數(shù)組方法,MDN 對其解釋如下:
reduce() 方法對數(shù)組中的每個元素按序執(zhí)行一個 reducer 函數(shù),每一次運行 reducer 會將先前元素的計算結(jié)果作為參數(shù)傳入,最后將其結(jié)果匯總為單個返回值。
reduce() 方法的語法如下:
array.reduce(reducer, initialValue)
其中有兩個參數(shù):(1)reducer 函數(shù),包含四個參數(shù):
- previousValue:上一次調(diào)用 callbackFn 時的返回值。在第一次調(diào)用時,若指定了初始值 initialValue,其值則為 initialValue,否則為數(shù)組索引為 0 的元素 array[0]。
- currentValue:數(shù)組中正在處理的元素。在第一次調(diào)用時,若指定了初始值 initialValue,其值則為數(shù)組索引為 0 的元素 array[0],否則為 array[1]。
- currentIndex:數(shù)組中正在處理的元素的索引。若指定了初始值 initialValue,則起始索引號為 0,否則從索引 1 起始。
- array:用于遍歷的數(shù)組。
(2)initialValue 可選 作為第一次調(diào)用 callback 函數(shù)時參數(shù) previousValue 的值。若指定了初始值 initialValue,則 currentValue 則將使用數(shù)組第一個元素;否則 previousValue 將使用數(shù)組第一個元素,而 currentValue 將使用數(shù)組第二個元素。
下面是一個使用reduce() 求數(shù)組元素之和的例子:
const arr = [0, 1, 2, 3, 4];
const calculateSum = (previousValue, currentValue) => {
console.log('previousValue: ', previousValue);
console.log('currentValue:', currentValue);
return previousValue + currentValue;
};
arr.reduce(calculateSum)
reducer 會逐個遍歷數(shù)組元素,每一步都將當(dāng)前元素的值與上一步的計算結(jié)果相加(上一步的計算結(jié)果是當(dāng)前元素之前所有元素的總和),直到?jīng)]有更多的元素被相加。
這段代碼的輸出如下:

其執(zhí)行過程如下:

當(dāng)我們給reduce()方法一個初始值12時:
arr.reduce(calculateSum, 12);
其執(zhí)行過程如下:

如果數(shù)組為空且未提供初始值,reduce() 方法就會拋出 TypeError:
const reducer = (accumulator, currentValue) => accumulator + currentValue;
const result = [].reduce(reducer)
console.log(result)
輸出結(jié)果如下:

2. 使用技巧
(1)數(shù)組求和
reduce()方法最直接的用法就是對數(shù)組元素求和:
const total = [34, 12, 143, 13, 76].reduce(
(previousValue, currentValue) => previousValue + currentValue,
0
);
console.log(total);
其輸出結(jié)果如下:
(2)扁平數(shù)組
reduce()方法還可以用來扁平化數(shù)組:
const array = [[0, 1], [2, 3], [4, 5], [5, 6]];
const flattenedArray = array.reduce(
(previousValue, currentValue) => previousValue.concat(currentValue),
[]
);
console.log(flattenedArray);
輸出結(jié)果如下:
如果數(shù)組有不止一層嵌套數(shù)組,可以遞歸調(diào)用 reduce 函數(shù)來扁平化,然后將它們與最終的數(shù)組連接起來即可。
const nestedArray = [[1, [2, 3]], [4, 5], [[6, 7], [8, 9]]];
function flattenArray(nestedArray) {
return nestedArray.reduce(
(accumulator, currentValue) =>
accumulator.concat(
Array.isArray(currentValue) ? flattenArray(currentValue) : currentValue
),
[]);
}
const flattenedArray = flattenArray(nestedArray);
console.log(flattenedArray)
輸出結(jié)果如下:
[1, 2, 3, 4, 5, 6, 7, 8, 9]
(3)數(shù)組分組
假設(shè)有一個國家對象數(shù)組,根據(jù)國家所在洲對數(shù)組中的每個國家進行分組??梢允褂?reduce 方法來完成:
cosnt countries = [
{name: "Germany", continent: "Europe"},
{name: "Brazil", continent: "South America"},
{name: "India", continent: "Asia"},
{name: "France", continent: "Europe"},
{name: "South Korea", continent: "Asia"},
]
const groupedCountries = countries.reduce(
(groupedCountries, country) => {
if (!groupedCountries[country.continent]){
groupedCountries[country.continent] = []
}
groupedCountries[country.continent].push(country)
return groupedCountries
},
{}
);
console.log(groupedCountries)
輸出結(jié)果如下:

(4)使用 reduce() 代替 filter().map()
在 Javascript 中,數(shù)組的 filter 方法可以通過回調(diào)過濾數(shù)組中的元素,map 方法可以通過回調(diào)內(nèi)部傳遞的邏輯使用舊數(shù)組創(chuàng)建一個新數(shù)組。有時我們必須同時使用這兩種方法,對某些條件過濾的結(jié)果創(chuàng)建一個新數(shù)組。
可以使用 reduce 方法來完成相同的工作,這樣就只需要遍歷數(shù)組一次。例如,要創(chuàng)建一個大于 30 的數(shù)字的平方根數(shù)組,使用 filter().map() 可能會這么寫:
const numbers = [3, 21, 34, 121, 553, 12, 53, 5, 42, 11];
const newArray = numbers.filter(number number > 30).map(number Math.sqrt(number));
使用 reduce 實現(xiàn):
const numbers = [3, 21, 34, 121, 553, 12, 53, 5, 42, 11];
const newArray = numbers.reduce((previousValue, currentValue) => {
if (currentValue > 30) {
previousValue.push(Math.sqrt(currentValue))
}
return previousValue
}, []);
console.log(newArray);
輸出結(jié)果如下:
[5.830951894845301, 11, 23.515952032609693, 7.280109889280518, 6.48074069840786]
(5)統(tǒng)計數(shù)組元素出現(xiàn)次數(shù)
可以使用reduce來統(tǒng)計數(shù)組中每個元素出現(xiàn)的次數(shù):
const colors = ['green', 'red', 'red', 'yellow', 'red', 'yellow', 'green', 'green'];
const colorMap = colors.reduce((previousValue, currentValue) => {
previousValue[currentValue] >= 1 ? previousValue[currentValue]++ : previousValue[currentValue] = 1;
return previousValue;
},
{}
);
console.log(colorMap);
輸出結(jié)果如下:
{green: 3, red: 3, yellow: 2}
(6)串行執(zhí)行異步函數(shù)
有一組需要串行執(zhí)行的異步函數(shù),可以使用reduce()來調(diào)用執(zhí)行:
const functions = [
async function() { return 1; },
async function() { return 2; },
async function() { return 3; }
];
const res = await functions.reduce((promise, fn) => promise.then(fn), Promise.resolve());
console.log(res); // 輸出結(jié)果:3
這里的 res 就相當(dāng)于執(zhí)行了:
Promise.resolve().then(fn1).then(fn2).then(fn3);
(7)創(chuàng)建管道
假設(shè)有一組簡單的數(shù)學(xué)函數(shù),這些函數(shù)允許我們增加、減少、加倍和減半:
function increment(input) { return input + 1;}
function decrement(input) { return input — 1; }
function double(input) { return input * 2; }
function halve(input) { return input / 2; }
如果想對一個值進行多次上述操作,就可以使用reduce()。管道是用于將某些初始值轉(zhuǎn)換為最終值的函數(shù)列表的術(shù)語。我們只需將執(zhí)行過程中每一步用到函數(shù)寫在管道數(shù)組中即可。
const pipeline = [increment, double, decrement];
const result = pipeline.reduce((total, func) => {
return func(total);
}, 5);
console.log(result) // 輸出結(jié)果:11
(8)反轉(zhuǎn)字符串
可以使用reduce()實現(xiàn)字符串的反轉(zhuǎn):
const str = 'hello world';
[...str].reduce((a,v) => v + a); // 輸出結(jié)果:'dlrow olleh'
(9)數(shù)組去重
有一個包含重復(fù)項的數(shù)組,可以使用 reduce() 來對數(shù)組進行去重:
const arr = ["??", "??", "??", "??"];
const dedupe = (acc, currentValue) => {
if (!acc.includes(currentValue)) {
acc.push(currentValue);
}
return acc;
};
const dedupedArr = arr.reduce(dedupe, []);
console.log(dedupedArr); // ["??", "??"];
其執(zhí)行過程如下:

當(dāng) reduce 方法遍歷數(shù)組時,回調(diào)函數(shù)將應(yīng)用于數(shù)組中的每一項。它會檢查累加器中是否缺少當(dāng)前值,如果是,則將當(dāng)前值 push 到累加器中。
注:此示例僅用于說明 reduce 方法的工作原理,在實踐中,通常會選擇使用 Set 對數(shù)組進行去重,這是一種性能更高的方法:
dedupedArr = [...new Set(array)];