compose方法redux中的compose函数定义compose方法利用数组的reduce方法,将多个方法按照一定的顺序组合成一个方法,从而达到函数自动有序执行的效果。各个方法的执行顺序取决于reduce方法的应用方式。compose的简单实现如下://compose接受多个函数作为输入参数fun1fun2functioncompose(...funs){constlen=funs.length//处理两种特殊情况,函数个数为0||1if(!len)return(...arg)=>argif(len===1)returnfuns[0]//下面的组合方法b会在方法returnfuns.reduce((a,b)=>(...arg)=>a(b(arg)))//下面的组合方法a方法会在b方法之前执行//returnfuns.reduce((a,b)=>(...arg)=>b(a(arg)))}组合函数(a,b)b函数的返回值是普通数据,作为a函数函数b()的输入参数{//如果返回值是普通对象return{...}}functiona(arg){//arg数据应用||processingreturn{...}}按照上面的形式,如果函数a要应用函数b的返回值,最好知道b函数返回值的数据类型和字段细节,会导致严重的a和b方法之间的耦合。b返回一个函数作为函数a的输入参数functionb(){//返回值是functionreturn(...arg)=>{//...}}functiona(next){//next是前置的操作//next()可以在这里操作next//下一个后操作return(...arg)=>{//下一个预操作//next()可以在这里操作next//下一个后操作}}Compared与前面的形式相比,这种形式可以实现函数之间的解耦效果,即函数a不需要关心函数b的返回值,只要知道它的返回值是一个函数就可以了,当调用它的时候它认为是合适的,这也控制了函数调用的顺序,从而达到类似于洋葱模型的执行效果。applyMiddleware方法applyMiddleware以函数的形式构造一个闭包,为外部定义的方法提供内部指定的api;下面的例子//接受一个外部定义的方法数组funsfunctionapplyMiddleware(...funs){constapis={dispatch:()=>({})}//遍历并执行外部定义的方法来构造一个闭包;并将返回值作为结果返回returnfuns.map(fun=>fun(apis))}操作的funs函数形式是构造一个闭包,fun的返回值应该是一个使用apis的函数。functionmyMiddleware1(apis){//返回一个方法,该方法会调用apis暴露的方法形成闭包,returnactions=>{//apis方法调用const{dispatch}=apisdispatch(actions)}}constmyMiddlewareWrappers=applyMiddleware(myMiddleware1)//myMiddlewareWrappers是[actions=>{const{dispatch}=apis;dispatch(actions);}]可能会有疑问:既然只是api中的函数调用,为什么要这么复杂?包实现?调用者直接调用api里面的函数不就可以达到目的了吗?闭包的应用:闭包让我们可以在函数外控制函数内的执行逻辑。比如常见的防抖和拦截功能可以使用闭包来实现函数柯里化,从而达到函数入参缓存的效果,进而实现一个为什么不直接调用api的内部方法:api的内部方法确实一开始不存在,可能是动态生成的,可以通过闭包实现1.b中提到的单例效果。通常我们不是简单的函数调用,往往会附带额外的操作。不应用闭包会导致如下效果functionfun1(arg){console.log(arg)apis.dispatch(arg)}functionfun2(arg){arg.b=100apis.dispatch(arg)}//api方法的调用可能随处可见,但仔细看,api方法的调用其实是重复的代码。对比应用闭包的效果//没有重复调用api内部函数,构造的闭包函数会自动执行api内部的方法//api暴露但未完全暴露constresults=applyMiddleware(myMiddleware1,myMiddleware1)results[0](100)results[1](200)如何结合compose和applyMiddleware两种方式得到神奇的效果?首先考虑上面以myMiddleware的形式定义的方法,直接将applyMiddleware得到的方法列表组合起来constmyMiddlewareWrappers=applyMiddleware(myMiddleware1,myMiddleware2)//通过compose将方法列表组合成一个方法constresult=compose(...myMiddlewareWrappers)//此时result的形式是什么?//根据compose的功能,不难想象result是一个函数//actions=>myMiddleware1(myMiddleware2(actions))至此,compose和applyMiddleware已经合并,但是仔细观察myMiddleware1和applyMiddleware的形式2个函数,你会发现一些问题是myMiddleware1和2方法的返回值不是一个函数,可以通过一开始分析compose得到。这种形式会造成函数之间严重的耦合问题。因此需要对myMiddleware进行一步步的改造,如下functionmyMiddleware3(apis){//返回一个方法,会调用apis中暴露的方法,形成一个闭包,//同时,为了保证compose过程中每个函数输入的参数仍然是一个函数,下面的返回值需要再封装一次returnnext=>actions=>{//...next(actions)//...}}magic但是上面的我们还是不满意结果导致api内部函数的调用不在函数调用者手中,也就是函数调用者只能传入一个actions参数,但无法控制apis函数的具体执行时机。如何实现控制反转的效果?动作实际上可以是一种方法。这时候可以通过传递参数的方式将api传递给action,这样就可以将api的控制权交给真正的函数调用者。下面这个functionmyMiddleware2(apis){//返回一个方法,会调用apis暴露的方法形成闭包,returnnext=>actions=>{//控制反转actions(apis)}}//兼容性可以结合myMiddleware2和myMiddleware1的功能来实现//也就是说当调用者需要控制api时,可以将action以函数的形式传入,否则可以在api中以指定的形式传入作为参数functionmyMiddleware2(apis){//返回一个方法,会调用apis中暴露的方法形成闭包,returnnext=>actions=>{//控制反转if(typeofactions==='function')返回动作(apis);下一个(动作)}}
