2022-09-28JavaScript00
请注意,本文编写于 520 天前,最后修改于 520 天前,其中某些信息可能已经过时。

compose

compose 是函数式编程中一个非常重要的函数,compose 的函数作用就是组合函数的,将函数串联起来执行。将多个函数组合起来,一个函数的输出结果是另一个函数的输入参数,一旦第一个函数开始执行,就会像多米诺骨牌一样推导执行了。

2.1 简单例子

比如有这样的需求,输入一个名字 yideng,然后全部变成大写,打印输出 HELLO YIDENG,我们可以考虑用函数组合的方法来解决这个问题,需要两个函数 greeting、toUpper

const greeting = (name) => `hello ${name}`;
const toUpper = (str) => str.toUpperCase();
const fn = compose(toUpper, greeting);
console.log(fn("yideng"));
// HELLO YIDENG

这就是 compose 的大致使用,主要有以下几点:

  • compose 参数是函数,返回也是一个函数
  • 除了第一个函数接受参数,其它函数接受的都是上一个函数的返回值,所以初始函数的参数是多元的,而其它函数的接受值是一元的。
  • compose 函数可以接受任意的参数,所有的参数都是函数,且执行方向是自右向左的,初始函数一定放到参数的最右面。

知道这几点之后,上边的 compose 执行过程就很容易分析出来了,执行 fn('yideng')的时候,初始函数为 greeting,执行结果作为参数传递给 toUpper,再执行 toUpper,得出最后的结果。

compose 的好处就是如果再想加一个处理函数,不需要修改 fn,再执行一个 compose 就可以了。比如再加一个 trim。

const trim = (str) => str.trim();
const newFn = compose(trim, fn);
console.log(newFn("yideng"));

2.2 参考实现

  • 简单实现:递归实现
const compose = function (...funcs) {
        let len = funcs.length,
          count = len - 1,
          result = null;
        // 首先compse 返回的是一个函数
        return function fn(...args) {
          // 函数体里就是不断执行args函数,将上一个函数的执行结果作为下一个执行函数的输入参数,需要一个count来记录args函数列表的执行情况
          result = funcs[count].apply(this, args);
          // 递归退出条件
          if (count <= 0) {
            count = len - 1;
            return result;
          } else {
            count--;
            return fn.call(null, result);
          }
        };
      };
      // 测试
      const greeting = (name) => `hello ${name}`;
      const toUpper = (str) => str.toUpperCase();
      const fn = compose(toUpper, greeting);
      console.log(fn("yideng"));
  • 实现方式二:迭代实现
function compose(...fns) {
  let isFirst = true;
  return (...args) => {
    return fns.reduceRight((result, fn) => {
      if (!isFirst) return fn(result);
      isFirst = false;
      return fn(...result);
    }, args);
  };
}
  • lodash.js 库的实现
var flow = function (funcs) {
  var length = funcs.length;
  var index = length;
  while (index--) {
    if (typeof funcs[index] !== "function") {
      throw new TypeError("Expected a function");
    }
  }
  return function (...args) {
    var index = 0;
    var result = length ? funcs[index].apply(this, args) : args[0];
    while (++index < length) {
      result = funcs[index].call(this, result);
    }
    return result;
  };
};
var flowRight = function (funcs) {
  return flow(funcs.reverse());
};

const greeting = (name) => `hello ${name}`;
const toUpper = (str) => str.toUpperCase();
const fn = flowRight([toUpper, greeting]);
console.log(fn("yideng"));

可以看出,lodash 的本来实现是从左到右实现的,但也提供了从右到左的 flowRight,还多了一层函数的校验,而且接收的是数组,不是参数序列。

本文作者:前端小毛

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!