这位同学,你是否看过redux关于compose的源码?redux中仅用3行代码就实现了compose功能。优雅的写法让人着迷,但优雅到过于抽象是否让你望而却步?
const compose = (...fns) => {
if(fns.length === 0) reutrn arg => arg
if(fns.length === 1) return fns[0]
return fns.reduce((curr, current) => (...args) => curr(current(...args)))
}
上述代码是redux中关于compose函数的源码。相信很多人和笔者一样第一眼看过去很苦恼这是什么意思,接下来我们分析一下 这3行实现代码到底做了什么
关于compose函数的作用概括起来就是:接受函数作为参数,并依次执行函数,函数的执行结果会作为下一个函数的入参
compose函数的核心代码就在最后一行:fns.reduce()
复习一下reduce函数的知识:是对每一项进行处理并将函数的return结果作为函数的第一个参数。
那么compose函数就可以被简化成:[fn1, fn2, fn3].reduce()
我们来一步一步的分析reduce的过程:
// 我们在写的清楚一些,每个函数都使用函数表达式来写
[
(num1 => num1 + 1),
(num2 => num2 + 2),
(num3 => num3 + 3)
].reduce((curr, current) => {
return (num) => curr(current(num))
})
reduce是一个数组函数,所以会执行数组的每一项
step 1:
return (payload) => (
(num1 => num1 + 1)((num2 => num2 + 2)(payload))
)
PS:为了更好的展示,我对调用的函数进行了换行和加上的括号(以下都是如此~)
在step 1执行时(num1 => num1 +1)
就是curr, (num2 => num2 + 2)
就是current。我们搞清楚第一步后面就会容易许多。
step2:
思考一下第二次执行reduce
函数时 对应的 curr, current
都是谁? 🤔
return (payload2) => (
(payload) => (num1 => num1 + 1)(
(num2 => num2 + 2)(payload)
)
)((num3 => num3 + 3)(payload2))
看清楚了吗? 其实就是step1的代入!
在 step 2中(payload) => (num1 => num1 + 1)((num2 => num2 + 2)(payload))
就是curr,(num3 => num3 + 3)(payload2)
就是current
看到这里已经有小伙伴晕倒了,没关系 我们借用变量来表示:
const step2Curr = (payload) => (num1 => num1 + 1)((num2 => num2 + 2)(payload))
return (payload2) => step2Curr((num3 => num3 + 3)(payload2))
还有点晕?我们在简化一下!
const current = (num3 => num3 + 3)
return (payload2) => step2Curr(current(payload))
豁然开朗了吗?我们又回到了compose
函数的本体
因为compose
本身就是一个抽象函数嘛😄
我们把compose
的抽象(高端)代码还原成具体的业务代码(接地气)就变成了:
[
(num1 => num1 + 1),
(num2 => num2 + 2),
(num3 => num3 + 3),
(num4 => num4 + 4)
].reduce()
const composer = (payload3) => (
(payload2) => (
(payload) => (
(num1 => num1 + 1)(
(num2 => num2 + 2)(payload)
)
)
)(
(num3 => num3 + 3)(payload2)
)
)(
(num4 => num4 +4)(payload3)
)
我们在没有compose思想前代码是这样婶儿的:
const composer = fn1(fn2(fn3(fn4(1))))
其实这一步就是具体代码被抽象成工具函数的过程
这里笔者想问大家一个问题,看完compose的解析过程 大家理解compose函数为什么是从右到左的执行顺序了吗?
最后附洋葱模型:
还有一个写法比较low的compose
函数(但我觉得这一版更能让人理解compose的含义)
const compose2(...fns) {
const _this = window
if(fns.length === 0) return arg => arg
if(fns.length === 1) return fns[0]
return function(...args) {
let finalVal = args
// 除了可以for从数组末尾倒数开始,也可以事先把数组reverse一下
for(let i = fns.length - 1; i > -1; i--) {
const v = Array.isArray(finalVal)? finalVal : [finalVal]
finalVal = fns[i].apply(_this, v)
}
return finalVal
}
}