Array.prototype.reduce

reduce 对数组元素依次累积,回调接收 (accumulator, currentValue, index, array) 四个参数,返回最终累积值。

#type / concept #status / evergreen #resource / javascript #resource / ecmascript

[!info] related notes

Array.prototype.reduce

一句话定义

reduce 对数组每个元素依次执行回调,将结果累积到一个值上并返回。

签名

array.reduce(callbackFn)
array.reduce(callbackFn, initialValue)

callbackFn 参数

回调函数接收 4 个参数,比 map 多一个 accumulator

array.reduce(function callback(accumulator, currentValue, index, array) {
  // accumulator  : 上一次回调的返回值(或初始值)
  // currentValue : 当前正在处理的元素
  // index        : 当前索引
  // array        : 调用 reduce 的数组本身
}, initialValue);
参数含义
accumulator累积值,上一次回调的返回值
currentValue当前元素
index当前索引
array原数组本身

执行逻辑

有 initialValue

从索引 0 开始,accumulator 初始为 initialValue

第 0 步: acc = initialValue,    current = arr[0]
第 1 步: acc = 上一步返回值,    current = arr[1]
第 2 步: acc = 上一步返回值,    current = arr[2]
...
最后一步的 acc 就是最终返回值

无 initialValue

arr[0] 作为初始 accumulator,从索引 1 开始遍历:

第 0 步: acc = arr[0],          current = arr[1]
第 1 步: acc = 上一步返回值,    current = arr[2]
...

空数组 + 无 initialValue → TypeError

[].reduce((acc, cur) => acc + cur)         // TypeError
[].reduce((acc, cur) => acc + cur, 0)      // 0(安全)

基本用法

求和

[1, 2, 3, 4].reduce((acc, cur) => acc + cur, 0)  // 10

展开过程:

acc=0, cur=1 → 1
acc=1, cur=2 → 3
acc=3, cur=3 → 6
acc=6, cur=4 → 10

求最大值

[3, 7, 2, 9].reduce((max, cur) => cur > max ? cur : max)  // 9

initialValuearr[0]=3 作为初始 accumulator。

数组扁平化

[[1, 2], [3, 4], [5]].reduce((acc, cur) => acc.concat(cur), [])
// [1, 2, 3, 4, 5]

计数

const arr = ['a', 'b', 'a', 'c', 'b', 'a'];
arr.reduce((count, item) => {
  count[item] = (count[item] || 0) + 1;
  return count;
}, {});
// { a: 3, b: 2, c: 1 }

数组转对象

const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];

users.reduce((map, user) => {
  map[user.id] = user;
  return map;
}, {});
// { 1: { id: 1, name: 'Alice' }, 2: { id: 2, name: 'Bob' } }

管道 / compose

const pipe = (...fns) => (input) => fns.reduce((acc, fn) => fn(acc), input);

const double = x => x * 2;
const addOne = x => x + 1;
const square = x => x * x;

const transform = pipe(double, addOne, square);
transform(3);  // ((3*2)+1)^2 = 49

与 map / filter 组合

reduce 可以替代 map + filter,但可读性通常更差:

// 用 reduce 同时做过滤 + 映射(不推荐,仅作理解)
[1, 2, 3, 4, 5].reduce((acc, cur) => {
  if (cur % 2 === 0) acc.push(cur * 10);
  return acc;
}, []);
// [20, 40]

// 推荐写法
[1, 2, 3, 4, 5].filter(x => x % 2 === 0).map(x => x * 10);

reduceRight

从右到左遍历,参数和逻辑完全相同:

[1, 2, 3].reduceRight((acc, cur) => acc + '-' + cur)
// "3-2-1"

经典陷阱

1. 忘记 return

// ❌ 回调没有 return,acc 变成 undefined
[1, 2, 3].reduce((acc, cur) => { acc + cur }, 0)

// ✅ 用花括号必须显式 return
[1, 2, 3].reduce((acc, cur) => { return acc + cur }, 0)

// ✅ 或用简写
[1, 2, 3].reduce((acc, cur) => acc + cur, 0)

2. 空数组无 initialValue

[].reduce((acc, cur) => acc + cur)  // TypeError: Reduce of empty array with no initial value

始终传 initialValue 是更安全的习惯。

3. 参数错位

详见 higher-order-function-callback-mismatch

reduce 回调有 4 个参数,把现成函数传给 reduce 时更容易错位:

function add(a, b) { return a + b; }

[1, 2, 3].reduce(add)
// add(1, 2) → 3
// add(3, 3) → 6
// 结果 6(这个例子恰好正确,但依赖于 acc 和 value 的顺序)

与相关方法对比

方法返回值用途
reduce累积值求和、分组、扁平化、管道
reduceRight累积值从右累积
map新数组一对一映射
filter新数组(子集)过滤
forEachundefined副作用遍历

参考

创建于 2026/4/1 更新于 2026/5/27