Redux之compose

2,066 阅读3分钟

前言

在前面的基础篇中我们讲解了createStore,bindActionCrearoes,combineReducers,在高级篇讲解了applyMiddleware,致此redux相关的5个API就剩下一个compose,之所以把compose单独写一篇文章的原因是因为是compose五个API里唯一一个能单独拿出来用的函数,就是函数式编程里常用的组合函数,和 redux 本身没有什么多大关系。

compose的作用

把复杂的多函数嵌套调用,组合成纯粹的函数调用,实现fn1(fn2(fn3(fn3(...args))))-->compose(fn1,fn2,fn3,fn4)(...args)这样单纯可读的函数调用方式

举例说明

let { compose } = require('redux');
function add1(str) {
  return '1' + str;
}
function add2(str) {
  return '2' + str;
}
function add3(str) {
  return '3' + str;
}
function add4(str) {
  return '4' + str;
}
let result = add1(add2(add3(add4('GuYan'))));
console.log(result); // '1234GuYan'
console.log(compose(add1,add2,add3,add4)('GuYan')); // '1234GuYan'
  • 执行分析
    • 【1】componse执行返回一个函数
    • 【2】componse的参数为函数,返回的函数执行的时候实现了参数函数从右向左依次执行且每一个参数函数执行的结果为下一参数函数执行的入参,返回函数执行传入的参数作为第一次执行的参数函数的入参
  • 源码实现
export default function compose(...funcs){
    // 如果参数没传,我们自己内部实现一个函数返回
    if(funcs.lenth === 0){
        return arg => arg
    }
    // 如果传入的参数只有一个函数,直接返回该函数
    if(funv.length === 1){
        retrun funcs[0]
    }
    /*如果传入的参数为多个函数,我们为了实现执行分析的【2】中的从右向左依次执行且每一个参数函数执行的结果为下一参数函数执行的入参,不难想到我们数组中的遍历方法,
    没错就是reduce,但是reduce的遍历是从左向右的,而我们要实现的是从右向左执行,
    所以我们选用reduceRight来实现*/
    return funcs.reduceRight((a,b)=>(...args)=>b(a(...args)));
}

相信大家一定可以读懂前面的源码实现了(如果有不明白的地方欢迎下方留言),但是细心的朋友们会发现真正的源码的实现并不是用reduceRight而是reduce,所以我们结合源码分析一遍我们的例子中代码的执行过程

源码分析

export default function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }
  if (funcs.length === 1) {
    return funcs[0]
  }
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

上面代码片段是我直接从源码中拷贝过来的,为了方便讲解我们将箭头函数修改成普通函数形式,结果如下

export default function compose(...funcs) {
  // 前两种我就不复述了
  return funcs.reduce(function (a,b) {
    return function (...args) {
      return a(b(...args))
    }
  })
}
  • 结合示例
    • 第一次迭代
      • 参数:add1add2
      • 返回function fn1(...args1){return add1(add2(...args1))}
    • 第二次迭代
      • 参数:function fn1(...args1){return add1(add2(...args1))}add3
      • 返回:function fn2(...args2){return (function fn1(...args1){return add1(add2(...args1))})(add3(...args2))}
      function fn2(...args2) {
       return (function fn1(...args1) {
          return add1(add2(...args1))
        })(add3(...args2))
      }
      /* 源代码执行的时候返回的是a执行传入b执行返回的结构,
      此时a是function fn1(...args1){return add1(add2(...args1))},b是add3;
      所以我用一个让function fn1(...args1){return add1(add2(...args1))}自执行传入add3(...args2),
      需要注意的是此时函数中的args1就是add3(...args2) */
      
    • 第三次迭代
      • 参数:function fn2(...args2){return (function fn1(...args1){return add1(add2(...args1))})(add3(...args2))}add4
      • 返回:function fn3(...args3) {return (function fn2(...args2) {return (function fn1(...args1) {return add1(add2(...args1))})(add3(...args2))})(add4(...args3))}
      function fn3(...args3) {
          return (function fn2(...args2) {
              return (function fn1(...args1) {
                  return add1(add2(...args1))
                  })(add3(...args2))
              })(add4(...args3))
          }
      /* 此时a是function fn2(...args2){return (function fn1(...args1){return add1(add2(...args1))})(add3(...args2))},b是add4(...args3);
       * 如第二次迭代一样我们依据用一个自执行函数包裹a,且传入参数`add4(...args3)`
       * 需要注意的是此时函数中的args2就是add4(...args3)
       */
      
第几次循环 a的值 b的值 返回的值
第一次 add1 add2 (...args1)=>(add1(add2(...args1)))
第二次 (...args1)=>(add1(add2(...args1))) add3 (...args2)=>((...args1)=>(add1(add2(...args1))))(add3(...args2))(...args2)=>add1(add2(add3(...args2)))
第三次 (...args)=>(add1(add2(add3(...args)))) add4 (...args3)=>((...args2)=>(add1(add2(add3(...args)))))(add4(...args3))(...args)=>add1(add2(add3(add4(...args))))