JavaScript数组方法reduce详解
目录
- JavaScript数组方法reduce详解
- 一,前言
- 二,核心语法
- 三,案例
- 1.求和
- 2.找最大值
- 3.数组转对象
- 4.复合操作(同时实现 map + filter)
- 四,常见错误
- 1.空数组没有初始值
- 2.没有返回累加值
- 3.reduce 方法在遍历数组时,依赖于数组的长度和内容,如果在遍历过程中修改了数组(例如删除元素),会导致遍历逻辑混乱,结果不可预测。
- 五,reduce 的 polyfill
- 六,总结
一,前言
Reduce 是一种在函数式编程中常见的操作,它可以将一个序列(比如列表,数组)的所有元素通过某种规约逻辑合并成一个单一的数值。Reduce 的概念最早起源于函数式编程语言 Lisp,后来被广泛应用于其他语言中,如 Python ,javaScript。本文会用最易懂的方式帮你彻底掌握JavaScript数组reduce()方法。我们先从基础开始,逐步深入,最后通过实际案例巩固知识,最重要的是希望能够带给你一些思考。
二,核心语法
arr.reduce(callback(accumulator, currentValue, index, array), initialValue)
Accumulator(累计值)
CurrentValue(当前元素)
Index(当前索引)
Array(原数组)
initialValue(Accumulator的初始值)
三,案例
1.求和
const nums = [1, 2, 3];
const sum = nums.reduce((acc, num) => acc + num, 0);
代码分析:
此求和案例分别用了参数acc和num代表Accumulator和CurrentValue,并且将Accumulator的初始值赋为0。
运算步骤:
1,初始值为0,acc为0加第一次的当前值1 返回1
2,acc为1加第二次循环的当前值2,返回3
3,acc为3加第三次循环的当前值3,返回6
2.找最大值
const max = [4, 2, 7, 5].reduce((a, b) => Math.max(a, b), -Infinity);
代码分析:
此求最大值案例分别用了参数a和b代表Accumulator和CurrentValue,并且将Accumulator的初始值赋为-Infinity(负无穷大)。
运算步骤:
1,首先是负无穷和第一次的当前值4比较,返回4
2,a为4,和第二次的当前值2比较,返回4
3,a为4,和第三次当前值7比较,返回7
4,a为7,和第四次当前值5比较,返回7
说明:
-Infinity 是 JavaScript 中的一个特殊值,表示负无穷大。它不是变量,而是一个全局属性,属于 JavaScript 的 基本数据类型 之一。
这里也可以更换 -Infinity为100,那么max的返回值就是100。
3.数组转对象
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
const userMap = users.reduce((obj, user) => {
obj[user.id] = user;
return obj;
}, {});
返回结果:
{
"1": {
"id": 1,
"name": "Alice"
},
"2": {
"id": 2,
"name": "Bob"
}
}
说明:
数组转对象案例这里主要是想体现一下设置初始值的重要性
这里如果不设置其默认初始值可以先看一下返回结果的区别
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
const userMap = users.reduce((obj, user) => {
obj[user.id] = user;
return obj;
});
返回值:
{
"2": {
"id": 2,
"name": "Bob"
},
"id": 1,
"name": "Alice"
}
代码分析:
要想弄明白为什么没有设置空的初始值显示结果是这样,我们首先回到案例1求和的代码,
之前我们分析结论是基于有返回值的情况下得出的
1,初始值为0,acc为0加第一次的当前值1 返回1
2,acc为1加第二次循环的当前值2,返回3
3,acc为3加第三次循环的当前值3,返回6
那么如果对于求和案例不设置初始值又该如何分析呢?
对于没有设置初始值的reduce分析:第一次迭代时,acc 是数组的第一个元素,num 是数组的第二个元素。得到这个结论我们返回来分析代码:
const users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
const userMap = users.reduce((obj, user) => {
obj[user.id] = user;
return obj;
});
因为没有返回值,第一次obj是 { id: 1, name: ‘Alice’ },user是 { id: 2, name: ‘Bob’ }
执行 obj[user.id] = user 后,obj 变为 { id: 1, name: ‘Alice’, 2: { id: 2, name: ‘Bob’ } }。
注意:
JavaScript的reduce函数有没有设置初始值,直接影响第一次执行的Accumulator和CurrentValue
4.复合操作(同时实现 map + filter)
const numbers = [1, 2, 3, 4];
const doubledEvens = numbers.reduce((arr, num) => {
if (num % 2 === 0) {
arr.push(num * 2);
}
return arr;
}, []);
// 结果:[4, 8](先过滤偶数,再翻倍)
代码分析:
此求和案例分别用了参数arr和num代表Accumulator和CurrentValue,并且将Accumulator的初始值赋为[]。此例子中必须要设置Accumulator的初始值为[ ],否则,Accumulator的初始值是1,不是数组没有push方法就会报错。
四,常见错误
1.空数组没有初始值
[].reduce((a, b) => a + b); // 报错!
2.没有返回累加值
// 错误示例
[1,2,3].reduce((acc, num) => {
acc + num; // 没有 return!
});
// 正确:必须显式返回 acc
3.reduce 方法在遍历数组时,依赖于数组的长度和内容,如果在遍历过程中修改了数组(例如删除元素),会导致遍历逻辑混乱,结果不可预测。
五,reduce 的 polyfill
Polyfill 是指在不支持某个新特性的旧浏览器中,用 JavaScript 实现该特性的代码。换句话说,它是一个“补丁”,用来填补浏览器原生功能的缺失。
用白话说就是自己实现reduce,通过自己实现 reduce,可以更深入地理解它的工作原理。
Array.prototype.myReduce = function (callback, initialValue) {
// 1. 检查 this 是否为 null 或 undefined
if (this == null) {
throw new TypeError('Array.prototype.myReduce called on null or undefined');
}
// 2. 检查 callback 是否是函数
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
const array = Object(this); // 将 this 转换为对象
const length = array.length >>> 0; // 确保 length 是正整数
let accumulator;
let startIndex = 0;
// 3. 处理初始值
if (arguments.length >= 2) {
accumulator = initialValue; // 如果有初始值,直接使用
} else {
// 如果没有初始值,取数组的第一个元素作为初始值
if (length === 0) {
throw new TypeError('Reduce of empty array with no initial value');
}
accumulator = array[0];
startIndex = 1; // 从第二个元素开始遍历
}
// 4. 遍历数组
for (let i = startIndex; i < length; i++) {
if (i in array) {
// 调用回调函数,更新 accumulator
accumulator = callback(accumulator, array[i], i, array);
}
}
// 5. 返回最终结果
return accumulator;
};
使用示例:
const nums = [1, 2, 3, 4];
// 求和
const sum = nums.myReduce((acc, num) => acc + num, 0);
console.log(sum); // 10
// 找最大值
const max = nums.myReduce((acc, num) => Math.max(acc, num), -Infinity);
console.log(max); // 4
// 数组转字符串
const str = nums.myReduce((acc, num) => acc + num.toString(), '');
console.log(str); // '1234'
六,总结
reduce的参数看起来多,但是认真体会上面的案例不难理解,难点是如何记住
Accumulator(累计值)
CurrentValue(当前元素)
Index(当前索引)
Array(原数组)
参数顺序口诀:“All Cows In Australia”(ACIA)