写给自己看的 next 函数
前言
最早在看 redux
源码的时候,有个 applyMiddleware 函数,没能看明白(现在连它能干嘛的都忘了,衰)。只隐约记得,能够把一些 middleware 传入其中,然后它依次处理,顺序的执行一些逻辑(真忘了是干嘛的了)。
再如 express 中,某个中间件的使用都是 app.use(middleware)
, 而其内部具体干了什么还是不太清楚,最终呈现的效果是,当请求来的时候,依次的执行中间件,直到最后返回。
那这两个知识盲点给我的感觉是,都有种 "某个数组里存着函数,而里面的函数会依次执行" 的感觉。之后,偶然看到这个分享,说实话,觉得是能解决我的疑惑的,但还是绕不过来,理不清楚。
这次抽空强行理解了一下,赶紧留住思路,免得跑了~
需求
想象我们有这样的一个数组:
1. 自己的理解过程
1.1 正确分析
关键是 next 这个参数,我们需要的就是在 next 执行的时候,能够使得下一个函数执行,那么假如下一个函数就是 next, 会怎么样?
先只有两个函数:
fun1(fun2)
当 fun1 执行完之后,fun2 作为 next 执行,看起来没有问题。
但是!,当 fun2 执行的时候,fun2 的参数是 undefined,也就是说 fun2 的next 是 undefined ,那执行到 fun2 中的 next() 的时候就会报错。
那我们就想,给 fun2 传一个空函数:
这样也会有问题,就是 fun2(() => {})
这是一个函数调用,当它作为参数的时候,首先会被计算出来。同时 fun2(() => {})
的返回值是 undefined, 又相当于 fun1 的参数是 undefined, 又会报错。
那么我们需要的就是,fun2 自身接受一个空函数,但是又不可以直接以 fun2() 的形式传给 fun1 ,那么我们考虑把 fun2 包裹在函数里:
那这样就顺利的实现了功能。
那如果有三个函数的时候,我们自然可以推出以下的式子:
是的,但是,当函数个数不一定时,我们怎么来凑出这个式子呢?
先直接上 reduce 吧(就是这么没有道理):
先看看 reduce 的结构:
prev 为 initValue, 之后是 code
的返回值. curr 为当前迭代到的元素:
回到我们的例子: 当一个函数时:
当两个函数时:
当三个函数时:
套用 reduce, 那就是:
检验一下其余的: curr 为 fun1 时,run 函数最终返回:
curr 为 fun2 时,prev 为 () => fun1(() => {})
, run 函数最终返回:
curr 为 fun3 时,prev 为 () => fun2(() => fun1(() => {}))
, run 函数最终返回:
好像像那么回事,但是顺序好像反了,会先执行 fun3 而不是 fun1, 那我们换成 reduceRight:
那结果应该是:
最终实现了需求
1.2 易错
在理解 1.1 上述过程中,我尝试了以下的代码:
以上代码(在runJS)会输出 3,2,1 于是,我以为我改成 reduceRight 就可以了,但是改成 reduceRight 之后,依旧是输出 3,2,1. 那是为什么呢?
先转换以上代码:
curr 为 fun1 时,prev 为 () => {}
, 此时结果为:
curr 为 fun2 时,prev 为 fun1(() => {})
, 此时结果为:
curr 为 fun3 时,prev 为 fun2(fun1(() => {}))
, 此时结果为:
注意:
以上代码其实是会报错的!!!,如一开始分析的那样, fun1 会首先执行,返回值为 undefined, 当它作为 fun2 的参数时,其实是报错了,但是 runJS 居然没有报错。
题外话,之后找到了 runjs 的 github,发现居然不开源,也发现一个有意思的讨论: issue
以上的代码为什么还能输出结果呢?
因为在不报错的情况下,以上代码相当于:
即相当于三个函数同时执行,因此最终的输出结果,在 setTimtout 的时间不同的情况下,只和时间有关。
因此也告诉我们,我们的这种场景, reduce 里面必须要返回函数
2. 更多方法
2.1 递归法
其中 next 是个函数,相当于 () => run(arr.slice(1))
, 每次 next 函数执行,都会执行数组的下一项。
2.2 方法二
即 next 的功能就是执行数组的下一项,因此我们先定义 trigger 作为 next. 然后我们通过 map 函数,给每个函数外面包一层,当函数执行的时候,主动给它们传入 trigger 这个函数.
2.3 Promise 的方法
想一下,next 和 Promise 中的 resolve 是不是有点类似,就都是当执行的时候,会触发后续代码的执行。
于是我们同样构造 trigger 函数,但这次,我们利用 Promise#resolve 之后,执行 .then 代码的特性,来触发 trigger 个人觉得这种方法和 2.2 其实是一样的,而且添加了 Promise, 增加了执行流程,不推荐
3. compose 函数
函数式编程中有个 compose 函数,这个函数组合几个函数,形成一个新的函数:
即 compose 返回一个新的函数,该函数会被传入的函数,从右到左依次执行,同时右边函数的结果会作为左边函数的参数。
我们来看 reduce 的部分, 拆开是这样的:
当 reduce 没有第二个参数 initValue 时,prev 即为 funcs[0], curr 即为 funcs[1]。
因此,当只有两个函数时, prev 为 fun1
, curr 为 fun2
, 最终返回的函数是:
当有三个函数时: prev 为:
curr 为 fun3
, 那么最终的结果就是:
3.1 示例
参考资料
语雀: 来聊一道面试题吧
Last updated