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

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60616 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅7 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment7 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端
爱敲代码的小鱼7 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax