JavaScript `Array.prototype.reduce()` 的妙用:不只是求和!

JavaScript Array.prototype.reduce() 的妙用:不只是求和!

在 JavaScript 中,reduce() 常被误解为"只是一个求数组总和的工具"。但实际上,reduce 是最强大、最灵活的数组高阶函数之一 ------它能模拟 mapfilterfind,还能实现分组、扁平化、数据转换、状态累积等复杂逻辑。

本文将带你深入 reduce 的核心机制,并通过 10+ 个实用场景,展示它的真正威力。


一、reduce 基础回顾

js 复制代码
arr.reduce(callback(accumulator, currentValue, index, array), initialValue)
  • accumulator(累加器):上一次回调的返回值,或初始值;
  • currentValue:当前元素;
  • initialValue(可选) :第一次调用 callback 时的 accumulator 值。

💡 核心思想:把数组"归约"成一个单一值(但这个"值"可以是对象、数组、字符串等任意类型!)


二、经典用法:不止于求和

1. 求数组总和 / 乘积

js 复制代码
const sum = [1, 2, 3].reduce((acc, n) => acc + n, 0); // 6
const product = [1, 2, 3].reduce((acc, n) => acc * n, 1); // 6

2. 找最大/最小值

js 复制代码
const max = [3, 1, 4].reduce((acc, n) => acc > n ? acc : n); // 4
// 或用 Math.max
const max2 = [3, 1, 4].reduce((acc, n) => Math.max(acc, n));

三、高级妙用:reduce 的真正实力

✅ 1. 模拟 map

js 复制代码
const doubled = [1, 2, 3].reduce((acc, n) => {
  acc.push(n * 2);
  return acc;
}, []); // [2, 4, 6]

📌 虽然不如 map 简洁,但展示了 reduce 的通用性。


✅ 2. 模拟 filter

js 复制代码
const evens = [1, 2, 3, 4].reduce((acc, n) => {
  if (n % 2 === 0) acc.push(n);
  return acc;
}, []); // [2, 4]

✅ 3. 数组去重(基于值)

js 复制代码
const unique = [1, 2, 2, 3, 1].reduce((acc, n) => {
  if (!acc.includes(n)) acc.push(n);
  return acc;
}, []); // [1, 2, 3]

// 更高效(用 Set)
const unique2 = [...new Set([1, 2, 2, 3, 1])];

✅ 4. 按属性分组(Group By)

js 复制代码
const users = [
  { name: 'Alice', age: 25 },
  { name: 'Bob', age: 30 },
  { name: 'Charlie', age: 25 }
];

const groupedByAge = users.reduce((acc, user) => {
  const key = user.age;
  if (!acc[key]) acc[key] = [];
  acc[key].push(user);
  return acc;
}, {});

// 结果:
// {
//   25: [{ name: 'Alice', ... }, { name: 'Charlie', ... }],
//   30: [{ name: 'Bob', ... }]
// }

💡 这是 reduce 最常见的实战场景之一!


✅ 5. 统计频次(Count Occurrences)

js 复制代码
const fruits = ['apple', 'banana', 'apple', 'orange'];

const count = fruits.reduce((acc, fruit) => {
  acc[fruit] = (acc[fruit] || 0) + 1;
  return acc;
}, {});

// { apple: 2, banana: 1, orange: 1 }

✅ 6. 扁平化嵌套数组(Flatten)

js 复制代码
const nested = [[1, 2], [3, 4], [5]];

const flat = nested.reduce((acc, arr) => acc.concat(arr), []);
// [1, 2, 3, 4, 5]

// 深度扁平化(递归)
function flattenDeep(arr) {
  return arr.reduce((acc, val) => 
    Array.isArray(val) ? acc.concat(flattenDeep(val)) : acc.concat(val), 
  []);
}

📌 ES2019 已有 flat()flatMap(),但理解 reduce 实现很有价值。


✅ 7. 链式操作合并(替代多次遍历)

假设要对数组:过滤偶数 → 平方 → 求和

传统写法(三次遍历):

js 复制代码
const result = arr
  .filter(x => x % 2 === 0)
  .map(x => x * x)
  .reduce((a, b) => a + b, 0);

reduce 一次遍历完成:

js 复制代码
const result = arr.reduce((sum, x) => {
  if (x % 2 === 0) sum += x * x;
  return sum;
}, 0);

性能更优(尤其大数据量时)!


✅ 8. 将数组转为对象(key-value 映射)

js 复制代码
const users = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' }
];

const userMap = users.reduce((acc, user) => {
  acc[user.id] = user;
  return acc;
}, {});

// { 1: { id: 1, name: 'Alice' }, 2: { id: 2, name: 'Bob' } }
// 后续可通过 userMap[1] 快速查找,O(1) 时间复杂度

✅ 9. 实现管道(Pipeline)或状态机

js 复制代码
const operations = [
  (x) => x + 1,
  (x) => x * 2,
  (x) => x.toString()
];

const result = operations.reduce((value, fn) => fn(value), 5);
// ((5 + 1) * 2).toString() → "12"

💡 函数式编程中常见模式。


✅ 10. 处理异步操作(谨慎使用)

虽然 reduce 不是为异步设计的,但可顺序执行 Promise:

js 复制代码
const urls = ['url1', 'url2', 'url3'];

urls.reduce((promise, url) => {
  return promise.then(results => 
    fetch(url).then(res => res.json()).then(data => [...results, data])
  );
}, Promise.resolve([]))
.then(allData => console.log(allData));

⚠️ 注意:这会串行执行 ,如需并行请用 Promise.all


四、使用 reduce 的最佳实践

✅ 优点

  • 灵活性极高:可生成任意类型的累积结果;
  • 单次遍历 :避免多次 map/filter 链式调用的性能开销;
  • 逻辑集中:复杂转换可在一处完成。

⚠️ 注意事项

  1. 务必提供 initialValue (除非你明确知道不需要);
    • 否则第一个元素会被当作初始值,可能引发 bug;
  2. 不要滥用 :简单场景优先用 map/filter,语义更清晰;
  3. 避免副作用reduce 应是纯函数,不要修改外部变量;
  4. 可读性权衡 :过于复杂的 reduce 可拆分为多个步骤。

五、总结:reduce 的思维模型

reduce 的本质是:遍历数组,逐步构建一个最终结果。

你想做什么? reduce 能否实现?
求和、求积 ✅ 当然
转换为新数组 ✅(模拟 map)
筛选元素 ✅(模拟 filter)
分组、计数 ✅ 最佳场景之一
扁平化
构建映射对象 ✅ 高效
状态累积(如表单校验)

掌握 reduce,你就拥有了 "用一个函数解决大多数数组问题" 的能力。但记住:强大不等于万能,在可读性和性能之间找到平衡,才是专业开发者的标志。

🌟 终极建议 :当你发现自己写了多个 map/filter 链,或者需要在循环中维护复杂状态时------试试 reduce

相关推荐
ℋᙚᵐⁱᒻᵉ鲸落2 小时前
移动端滑动手势冲突:overflow: auto导致外层横向滑动失效
前端·javascript·css·vue.js·html
默_笙4 小时前
🎃 前端学了 Next.js,后端该学啥?NestJS 就是 Node 版的蜜雪冰城
前端·javascript
前端snow5 小时前
ai agent --- 实现 openclaw 定时间效果
前端
码云之上5 小时前
换模型之后,Chatbot 为什么要自己做 compact?
前端·agent·前端工程化
এ慕ོ冬℘゜5 小时前
navigator 导航对象 BOM制作方法
javascript
星栈5 小时前
自定义 UI-UX-Pro-Max 的 CSV 知识库,把 AI 生成的后台拉回行业该有的样子
前端·agent·weui
研☆香5 小时前
简单的图片上传 删除 预览
前端
ITresearchGuest6 小时前
我用 AI 一小时写了一个世界杯数据可视化平台|前端 VibeCoding 初体验
前端·人工智能·信息可视化
慧一居士7 小时前
Vite项目中使用Less步骤,详细使用示例
前端·css
花间相见7 小时前
【LangChain组件03】—— LangChain Agents 执行与状态:工作流程与状态管理实战
java·前端·langchain