🚧 开场:把你困住的误解先拆了
你是不是也以为:
"acc 是数组的每一项吗?是不是就相当于数组里的那个元素?"
答案是------不是!不是!真的不是!
让我们用这篇文章,从「误解」出发,到「掌握精髓」收官,完成一次技术脑内革命!
🏷️ 一、reduce 是什么?------数组界的「炼丹炉」
你可以把 reduce 理解为一口循环不断的炼丹炉。
🌰 经典语法结构:
javascript
arr.reduce((acc, cur, index, array) => {
return 新的 acc;
}, 初始值);
acc
(累计器):每一次处理的中间产物,也就是"丹药"cur
:数组当前项,是"投进去的新材料"return
的值会变成下一次的acc
初始值
:就是最开始丢进去的那一勺清水/引子
☕ 巧记口诀:
初始入炉为丹引,
每步炼化靠 acc,
一路处理到尾项,
千形万象终归一!
🤯 二、那 acc 到底是什么?
很多人以为 acc 是数组每个值,但 它其实是你自己 return 出来的中间结果。
看个例子就明白了:
javascript
[1, 2, 3].reduce((acc, cur) => {
console.log('acc:', acc, 'cur:', cur);
return acc + cur;
}, 0);
👉 输出如下:
makefile
acc: 0 cur: 1
acc: 1 cur: 2
acc: 3 cur: 3
最后返回 6!
你看见了吗?
- acc 初始是 0
- 第一轮:0 + 1 = 1,传给下一轮
- 第二轮:1 + 2 = 3
- 第三轮:3 + 3 = 6
acc 是你一手打造、一手维护、一手传承的"结果容器"。
📌 终极记忆法:
acc 是你"自己做出来"的,cur 是数组"送过来的"。
🧪 三、reduce 常见实战用法(带巧计)
1️⃣ 数组求和(基础)
ini
[1, 2, 3, 4].reduce((acc, cur) => acc + cur, 0);
💡 巧计:acc 是你口袋的余额,cur 是你捡到的零钱。
2️⃣ 统计频次(中级)
scss
['🍎', '🍌', '🍎'].reduce((acc, cur) => {
acc[cur] = (acc[cur] || 0) + 1;
return acc;
}, {});
💡 巧计:acc 是记账本,cur 是进店顾客,来一位就打正字。
3️⃣ 按类型分组(高级)
ini
const list = [
{ name: '小明', group: 'A' },
{ name: '小红', group: 'B' },
{ name: '小刚', group: 'A' },
];
const grouped = list.reduce((acc, cur) => {
(acc[cur.group] ||= []).push(cur);
return acc;
}, {});
💡 巧计:acc 是教室,cur 是学生,按班级安排座位。
4️⃣ 扁平化数组(进阶)
lua
[[1, 2], [3, 4], [5]].reduce((acc, cur) => acc.concat(cur), []);
💡 巧计:acc 是铺平的地面,cur 是被压扁的山丘。
5️⃣ 实现 map 和 filter(黑魔法)
ini
// map
arr.reduce((acc, cur) => {
acc.push(cur * 2);
return acc;
}, []);
// filter
arr.reduce((acc, cur) => {
if (cur % 2 === 0) acc.push(cur);
return acc;
}, []);
💡 巧计:
map 是换头术,filter 是拣人术,reduce 是百变术!
⚔️ 四、和其他数组方法比个高低
函数 | 功能 | 返回值类型 | 可中断 | 初始值 | 灵活性 |
---|---|---|---|---|---|
forEach | 只执行遍历 | 无(undefined) | ❌ | ❌ | 🚫 |
map | 一对一映射 | 数组 | ❌ | ❌ | 中 |
filter | 条件筛选 | 数组 | ❌ | ❌ | 中 |
some/every | 条件判断 | Boolean | ✅ | ❌ | 🚫 |
reduce | 多合一转化 | 任意类型 | ❌ | ✅ | ✅✅✅ |
🎯 结论:reduce 是 JS 数组操作界的「全能选手」,其他方法做不到的,它都能!
🧬 五、底层实现你也能写!
模拟原生 reduce 的核心逻辑 👇
ini
Array.prototype.myReduce = function (cb, initialValue) {
let acc = initialValue;
let i = 0;
const arr = this;
if (acc === undefined) {
if (!arr.length) throw new TypeError('空数组没初始值');
acc = arr[0];
i = 1;
}
for (; i < arr.length; i++) {
acc = cb(acc, arr[i], i, arr);
}
return acc;
};
💣 小心炸点:
css
[].reduce((a, b) => a + b); // ❌ 报错
[].reduce((a, b) => a + b, 0); // ✅ 返回 0
🔮 六、哲学升华 & 函数式世界里的 reduce
reduce 就是一条"函数管道",把每一步处理的结果传给下一步,直到最终收束。
javascript
const compose = (...fns) => x => fns.reduceRight((acc, fn) => fn(acc), x);
在 redux 中间件、koa 洋葱模型、Vue 插件链......都能看到 reduce 的影子。
🌌 它是一种思维方式:组合、累积、转化、递进。
🧨 七、终极巧记总结
🥋 再念一遍口诀:
reduce 是个炼丹炉,
入料为你定起初;
acc 是丹,cur 是火,
一路熬来百变出。
🎁 彩蛋题:flatten 多维数组
ini
const flatten = arr => arr.reduce((acc, cur) => {
return acc.concat(Array.isArray(cur) ? flatten(cur) : cur);
}, []);
✅ 你现在应该已经掌握了:
- ✅ acc 的本质(不是数组项,是 return 出来的中间结果)
- ✅ reduce 的运行机制
- ✅ 各种实战场景及巧妙类比
- ✅ 和其他方法的核心区别
- ✅ 自己实现 reduce
- ✅ reduce 在函数式中的地位
👇 最后,如果你还记得最初的问题:
"acc 是数组里的每一项吗?"
你应该可以坚定地回答:
不是!acc 是我 return 出来的结果,是我的魂,是我的锤,是我整个计算的载体!