这个 reduce 函数,说起来其实也挺简单的,但是要实用起来有时候还是感觉有些困难
这是我自己的一些理解方式,分享给大家看一下,希望能帮助到不理解的小伙伴。直接用代码进行逐步的讲解
可能会比较啰(话)嗦(多),但是你看完了,应该就会用了
急性子可以直接去看 更多例子下的二维数组合并 看完应该也能理解
急性子可以直接去看 更多例子下的二维数组合并 看完应该也能理解
//跟其他博主一样,我们也用要给简单的数组开始讲
//需求:我们现在有如下这么一个数组,我们想要获得这个数组的总和
let arr = [1,2,3,4,5];
方式一
传统方式,使用一个循环来循环累加,
下面代码是对传统方式的一种例子
这种方式完全可以实现,并且代码量其实也并不复杂。但是我们既然要学习新的高阶函数,那么我们就不在推荐大家使用这种方式了,接下来请看【方式二】
ps:大家既然都来学 reduce 了,我就默认大家都会使用 => 箭头函数了,这里不对箭头函数做专门的讲解了
let count = 0;
arr.forEach(item=>count+=item);
console.log(count); //15
这方式一实在是太简单了吧。。。我就不讲了
方式二
使用 reduce 函数
如下代码:
let count = arr.reduce(function(pre, cur){
return pre+cur
});
console.log(count); //15
看到这里,你可能会有疑问,这代码量丝毫不比传统方式少,但是为什么还要用这种方式
如果你有这种疑问,那你可能就对 箭头函数 还不够理解,接下来我就要变形了!上面都说了不讲 箭头函数 了,但是我这里还是想用用 function 因为直接用 箭头函数 会变得比较难以理解,上面的那个代码纯粹是为了给初学者带来更好理解的一种方式。希望大家把这两种方式都牢记,能够在心里进行转换
方式二变形
let count = arr.reduce((pre,cur)=>pre+cur)
怎么样?是不是一下子省了不少事情?对这个 reduce 服不服?
我知道你可能还不服,咱们继续,先听我说说这个原理
我们先来看看主角 reduce 都有哪些参数 【这段内容相对比较重要】
Array.reduce( Function, initValue )
两个参数都非常的重要(虽然第二个可有可无)
Function: 不用说,肯定是个回调函数。由开发者提供 reduce 来回调
initValue: 第一次回调 Function 时会将 initValue 传入到 Function 的第一个参数中ps: 插入一段内容,我发现有的博主没有说。就是当 initValue 没有传入的时候,Function 的第一个参数,将会是数组 0 所对应的值届时 reduce 也将会少走一次循环
接下来说一下 Function 回调时 reduce 都给了哪些参数
function(pre, cur, index, arr)
pre: 上一次的结果集,这个值通常会使用 initValue 作为第一次的默认参数,不过,我上面也有提到,当 initValue 没有传入参数时这个值将会是 数组 的第一个值
cur: 当前元素,与我们使用 forEach/map 时的 item 一样。都是当前循环到的那个对象
通常我们只会使用上面的两个属性
index: 当前元素的索引
arr: 当前元素所属的数组
看完了上面对 reduce 之后,应该就会对 reduce 有一个大概的了解了
更多例子
下面我们举一些其他的例子来讲解
数组求和与数组求平均值
数组与树形相互转换
二维数组合并
//需求:将如下二维数组进行合并处理
let arr2 = [[1,2,3], [4,5,6], [7,8,9]]
插播一则小广告:我这里有一个我自己封装的多维数组拍平也是使用的 reduce 感兴趣的小伙伴可以来瞅瞅 点我查看 看完下面的代码,相信你也能写出来。好了,继续
let newArray1 = arr2.reduce(function(pre,cur){
return pre.concat(cur);
}, []);
let newArray2 = arr2.reduce((pre,cur)=>pre.concat(cur), []);
console.log(newArray2); //[1,2,3,4,5,6,7,8,9]
讲解一下代码执行原理
reduce 第一次进入循环时,会将我们的第二个参数传入到 pre 中
----此时 pre = [], cur = [1,2,3];
----然后我们将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 第二次进入循环
----此时 pre = [1,2,3], cur = [4,5,6]
----然后我们再次将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 第三次进入循环(由于 arr2 长度也就 3 所以这也是最后一次)
----此时 pre=[1,2,3,4,5,6], cur=[7,8,9]
----然后我们再次将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 退出循环 并将 pre 抛出
我们将会得到最后的结果 [1,2,3,4,5,6,7,8,9]
附文:
我们上面有提到过,当我们的 initValue 不传入时,reduce 会将数组第一位作为初始值,并且我们事先知道我们需求中,二维数组是固定的两层数组 如:表格 那么我们这时就可以省掉 reduce 的第二个 initValue 参数(虽然节约不了太多的性能)
当我们去掉 initValue 参数时,我们得到的结果是这样的
reduce 第一次进入循环
----此时 pre = [1,2,3], cur = [4,5,6]
----然后我们将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 第二次进入循环(我们这里其实循环就不再从 0 开始了,而是从 1 开始,所以这也是最后一次循环)
----此时 pre=[1,2,3,4,5,6], cur=[7,8,9]
----然后我们再次将 pre 与 cur 使用 concat 拼接成了一个新的数组,并 return
reduce 退出循环 并将 pre 抛出
我们将会得到最后的结果 [1,2,3,4,5,6,7,8,9]
数组去重
//需求,对如下数组进行去重处理
let arr = [1,2,3,1,2,3,4,5,6];
//需求,对如下数组,对 name 进行去重处理
let arr2 = [
{name: '小猴'},
{name: '小玉'},
{name: '小玉'},
{name: '桃子'},
{name: '桃子'},
{name: '小刘'}
];
let arr_result = arr.reduce((pre, cur) => {
!!!pre.includes(cur) && pre.push(cur);
return pre;
}, []);
console.log(arr_result); //[1,2,3,4,5,6]
let temp = [], //临时存储 name 用于标记某个名字的对象是否已经保存
arr2_result = arr2.reduce((pre, cur) => {
if (!temp.includes(cur.name)) {
temp.push(cur.name)
pre.push(cur);
}
return pre;
}, []);
console.log(arr2_result);
//[{name:'小猴'},{name:'小玉'},{name:'桃子'},{name:'小刘'}]
我猜你还没看够吧?
我这里还有一个我封装的 数组与树形 相互转换 的代码。点我看看