← [ BACK_TO_NOTES ]

Array.prototype.reduce

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

#frontend #concept #evergreen #javascript #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

取第一个存在的索引属性值作为 accumulator,再从后续索引开始。稠密数组通常是 arr[0] 与索引 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

无 initialValue,arr[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;
}, Object.create(null));
// { 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;
}, Object.create(null));
// { 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);

计数与对象字典使用无原型对象,避免 constructor、proto 等字符串键与原型属性冲突;任意类型的键可考虑 Map。

reduceRight

从右到左寻找存在的索引属性,累积规则与 reduce 对称:

[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

初始值要符合运算语义,例如求和用 0、最大值可用 -Infinity;随意用 0 求最大值会把全负数数组算错。

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/10/7
Digital Biome
Access

键盘导航

j / k / ↑ / ↓
选择对象
Enter / o
打开选择
/ 或 Ctrl+K
全站搜索
i / f / 1–4
筛选当前列表
g → h / l / p / t / a
首页 / 笔记 / 项目 / 工具 / About
r → 1–9
打开所选对象的关联
u
返回上一页
Esc
关闭浮层 / 取消命令;输入框内清空文本