compose函数

compose函数

先po一下实现方法

// 解法1 迭代
function compose(fns) {
  let fnsArr = [...fns]; // 因为arguments是个类数组,所以需要先将它转化为数组,方便后续迭代操作,类数组转化为数组的方法还有很多,比如slice(0),concat(),Array.from()等
  let index = fnsArr.length - 1; // 获取最后一个函数的索引
  let res = null; // 初始化结果,
  return function fn() { // 利用高阶函数进行函数的迭代调用
    let args = [...arguments]; // 子函数的入参
    res = fnsArr[index].apply(this, args); // 将当前函数的执行结果保存在res中,作为下一个函数的入参使用
    if (index <= 0) {
      return res; // 循环结束
    }
    index--;
    return fn.call(null, res);// 递归调用
  };
}

// 迭代的过程可以用reduce替换
// 解法2-reduce
const compose=(...funcs)=> {
  if (funcs.length === 0) {
    return (arg) => arg;
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  return funcs.reduce((pre, curr) => (...arg) => pre(curr(...arg)));
}
// 从右向左,reduceRight可以完美的解决这个问题
// 解法3——更加精炼的reduceRight
const compose=(...args)=>(...x) => args.reduceRight((pre, curr) => curr(x));

只需要获取函数实现方式的同学现在可以出门右转啦,想看compose函数详解的继续往下


刚开始知道这个函数是在看webpack,介绍loader的时候有一句话:loader 从右到左(或从下到上)地取值(evaluate)/执行(execute),按照一般的场景,函数的执行顺序不都是自上而下,自左向右的么,于是就发现了compose这个神奇的函数。

compose函数是函数式编程中使用较多的一种写法,顾名思义,它可以将一堆函数组合在一起使用,将外部函数依次通过内部函数的加工,最后输出结果。

这个解释可能还是有点迷惑,那就拿loader来说明一下,我们在进行webpack配置的时候,遇到css预编译工具(sass,less等),需要自上而下的配置style-loader,css-loader以及sass-loader/less-loader,如果打乱顺序,就会出现一些错误。webpack的解释如下:

loader 支持链式调用。链中的每个 loader 会将转换应用在已处理过的资源上。一组链式的 loader 将按照相反的顺序执行。链中的第一个 loader 将其结果(也就是应用过转换后的资源)传递给下一个 loader,依此类推。最后,链中的最后一个 loader,返回 webpack 所期望的 JavaScript

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          // [style-loader](/loaders/style-loader)
          { loader: 'style-loader' },
          // [css-loader](/loaders/css-loader)
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          },
          // [sass-loader](/loaders/sass-loader)
          { loader: 'sass-loader' }
        ]
      }
    ]
  }
};

通俗的解释就是当遇到sass文件时,首先需要通过sass-loader将sass文件编译成css,然后再通过css-loader将文件中的@importurl() 进行处理,最后再通过style-loader将处理好的CSS插入到DOM中。

在这个步骤中,每一个环节的入参都是上一个环节的处理结果,如果把它精简成函数,那应该就是这样的

// 举个🌰
function a(){
  // TODO
}
function b(){
  // TODO
}
function c(){
  // TODO
}
// 其中函数a的入参要依赖于函数b的结果,函数b的入参又要依赖于函数c的结果
const demo = a(b(c()))
console.log(demo)

问题来了,如果环节数很多很多,那可能会出现这种情况a(b(c(d(e(f(g())))))),这种代码看上去就头皮发麻,更别说要是哪位在开发的时候手抖删了那么一个右括号,找问题都可能找半天,于是人们开始思考,能不能以一种更优雅的形式对它进行处理?于是就出现了常见的compose函数的格式

function compose(){
  // TODO
}
function a(){
  // TODO
}
function b(){
  // TODO
}
function c(){
  // TODO
}
let demo = compose(a,b,c)()
console.log(demo)

这样看起来是不是就顺眼多了,不管千层饼里再夹千层饼,我始终都在大气层,接着问题出现了。。。这个函数调用确实很方便,但是要怎么实现呢?这里把开头的实现方法再po出来,是不是会容易懂一点了

// 解法1 迭代
function compose(fns) {
  let fnsArr = [...fns]; // 因为arguments是个类数组,所以需要先将它转化为数组,方便后续迭代操作,类数组转化为数组的方法还有很多,比如slice(0),concat(),Array.from()等
  let index = fnsArr.length - 1; // 获取最后一个函数的索引
  let res = null; // 初始化结果,
  return function fn() { // 利用高阶函数进行函数的迭代调用
    let args = [...arguments]; // 子函数的入参
    res = fnsArr[index].apply(this, args); // 将当前函数的执行结果保存在res中,作为下一个函数的入参使用
    if (index <= 0) {
      return res; // 循环结束
    }
    index--;
    return fn.call(null, res);// 递归调用
  };
}

// 迭代的过程可以用reduce替换
// 解法2-reduce
const compose=(...funcs)=> {
  if (funcs.length === 0) {
    return (arg) => arg;
  }
  if (funcs.length === 1) {
    return funcs[0];
  }
  return funcs.reduce((pre, curr) => (...arg) => pre(curr(...arg)));
}
// 从右向左,reduceRight可以完美的解决这个问题
// 解法3——更加精炼的reduceRight
const compose=(...args)=> (...x) => args.reduceRight((pre, curr) => curr(x));

compose函数在很多中间件中都会使用,比如之前说的loader,在redux中也有暴露compose方法

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值