前两天在群里看到一道题,折腾半天没做出来。今天查了查,了解了前因后果,在此整理记录一下。之前都没怎么重视过reduce方法,后来发现它能做的事情还挺多的。
参考资料:
数组的reduce方法
数组的reduce方法相比其他的方法要复杂一些,但理解之后也不难,用法如下:
array.reduce((accumulator, item, index, array) => {
// do something...
}, initialValue)
reduce方法能够对数组使用,他接受两个参数,第一个参数是个函数fn
,第二个参数initialValue
为可选参数,表示累加器的初始值。
而fn
中有4个参数,其参数的值会被initialValue
存在与否所影响:
- 当
initialValue
存在
参数为:
accumulator
: 累加器,初始值为initialValue
的值item
:数组的当前项,从第0项开始index
:数组的当前项对应的下标,从0开始array
:数组本身
- 而当
initialValue
不存在
参数为:
accumulator
: 累加器,初始值为数组第0项item
:数组的当前项,因为accumulator
为第0项,所以这里从1开始index
:数组的当前项对应的下标,从1开始array
:数组本身
简单的用例:
//
[0, 1, 2, 3, 4].reduce(function(accumulator, item){
return accumulator + item;
});
compose组合函数
题目:
const fn1 = x => x+1;
const fn2 = x => x+2;
const fn3 = x => x+3;
const fn4 = x => x+4;
let Fn = compose(fn1, fn2, fn3, fn4);
console.log(Fn(0)); // 10
console.log(Fn(5)); // 15
// 求compose函数
compose是redux源码中使用过的方法。他是函数式编程里常用的组合函数,和 redux 本身没有什么多大关系,先了解下函数式编程的一些概念:
代码:
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
// 这里注意,因为reduce没有传入第二个参数initValue
// 此时a的初始值为第0项, b为第1项
// 我们希望先执行b,得到的结果再执行a,所以要注意应该是a(b(...args))
// 若希望先执行a再执行b,就要反过来时b(a(...args))
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
其实 compose
函数做的事就是把 var a = fn1(fn2(fn3(fn4(x))))
这种嵌套的调用方式改成 var a = compose(fn1,fn2,fn3,fn4)(x)
的方式调用。
举一个实际的例子,看看这个函数是怎么执行的:
const fn1 = x => x+1;
const fn2 = x => x+2;
const fn3 = x => x+3;
const fn4 = x => x+4;
let x = 0
// 假设我这里想求得这样的值
let a = fn1(fn2(fn3(fn4(x)))) // 0 + 4 + 3 + 2 + 1 = 10
// 根据compose的功能,我们可以把上面的这条式子改成如下:
let composeFn = compose(fn1, fn2, fn3, fn4)
let b = composeFn(x) // 理论上也应该得到10
看一下compose(fn1, fn2, fn3, fn4)
根据 compose 的源码, 其实执行的就是: [fn1,fn2,fn3.fn4].reduce((a, b) => (...args) => a(b(...args)))
第几轮循环 | a的值 | b的值 | 返回的值 |
---|---|---|---|
第一轮循环 | fn1 | fn2 | (…args) => fn1(fn2(…args)) |
第二轮循环 | (…args) => fn1(fn2(…args)) | fn3 | (…args) => fn1(fn2(fn3(…args))) |
第三轮循环 | (…args) => fn1(fn2(fn3(…args))) | fn4 | (…args) => fn1(fn2(fn3(fn4(…args)))) |
循环最后的返回值就是 (...args) => fn1(fn2(fn3(fn4(...args))))
。所以经过 compose
处理过之后,函数就变成我们想要的格式了。
多维数组降维
多维数组降维也可以使用
reduce
结合递归来解决
// 极简版
const flattenDeep = (arr) => Array.isArray(arr)
? arr.reduce( (a, b) => [...a, ...flattenDeep(b)] , [])
: [arr]
// 为方便理解,写个注释版
const flattenDeep = (arr) => {
// 判断传入的参数是否是数组
if(Array.isArray(arr)){
// 使用redece
// a的初始值是 [], 对a解构放入结果数组
// 对后边的每个元素都递归执行flattenDeep
// 最后的结果就是,所有不是数组的元素,都被包裹,解构后放入结果数组
// 所有是数组的元素,都递归执行了flattenDeep,最终
// 蹭蹭递归之后,最终所有都被包裹,解构后放入结果数组
// 也就成了一维数组
return arr.reduce( (a, b) => [...a, ...flattenDeep(b)] , [])
}else{
// 当参数不是数组时,包裹成数组(在上边会解构)
return [arr]
}
}