高级前端
js
【Q181】如何实现 compose 函数,进行函数合成

如何实现 compose 函数,进行函数合成

更多描述 实现一个 compose 函数,进行函数合成,比如 redux 中的 compose,react 高阶组件连续调用时的 compose

const add10 = (x) => x + 10;
const mul10 = (x) => x * 10;
const add100 = (x) => x + 100;
 
// (10 + 100) * 10 + 10 = 1110
compose(add10, mul10, add100)(10);

Issue 欢迎在 Gtihub Issue 中回答此问题: Issue 182 (opens in a new tab)

Author 回答者: RainMaker-Q (opens in a new tab)

function compose() {

  let fns = [...arguments];

  return function() {
    let args = [...arguments];
    let result = fns.reduce((ret, fn) => {
      ret = fn.apply(this, ret);
      return Array.isArray(ret) ? ret : [ret];
    }, args);

    return result;
  }
}

let toUpperCase = (x) => x.toUpperCase();
let exclaim = (x) => x + '!';
let shout = compose(toUpperCase,exclaim);
let str = shout('hello world');
console.log(str);

第一次试着写,简单查了一下compose是什么意思,竟然拼凑出来了。请各位指正。 :smile:

Author 回答者: RainMaker-Q (opens in a new tab)

看到一种新实现

function compose(...fns) {
  fns.reduce( (a, b) => (...args) => a(b(...args)));
}

Author 回答者: nieyao (opens in a new tab)

const compose = (...fns) => (...params) => fns.reduce((prev, current) => current(prev), ...params);

Author 回答者: shfshanyue (opens in a new tab)

实现一个从右向左(right to left)计算的组合函数:

const compose = (...fns) =>
  // 注意 f、g 的位置,如果实现从左到右计算,则置换顺序
  fns.reduce(
    (f, g) =>
      (...args) =>
        f(g(...args)),
  );

跑一个示例

const add5 = (x) => x + 5;
const multiply = (x) => x * 10;
const multiply10AndAdd5 = compose(add5, multiply);
multiply10AndAdd5(10); // 105

Redux 中广泛使用了 compose 函数,其中的实现如下

export default function compose(...funcs: Function[]) {
  if (funcs.length === 0) {
    // infer the argument type so it is usable in inference down the line
    return <T>(arg: T) => arg;
  }
 
  if (funcs.length === 1) {
    return funcs[0];
  }
 
  return funcs.reduce(
    (a, b) =>
      (...args: any) =>
        a(b(...args)),
  );
}

Author 回答者: shfshanyue (opens in a new tab)

@nieyao params 应该置后,测试示例时没有通过

Author 回答者: nieyao (opens in a new tab)

@nieyao params 应该置后,测试示例时没有通过

确实,忘了compose是从右往左执行的,那加个reverse 好了。 const compose = (...fns) => (...params) => fns.reverse().reduce((prev, current) => current(prev), ...params) 或者用reduceRight const compose = (...fns) => (...params) => fns.reduceRight((prev, current) => current(prev), ...params)

Author 回答者: Kiera569 (opens in a new tab)

function compose(...fn) {
  return (...args) =>
    fn
      .reverse()
      .slice(1)
      .reduce((acc, cur) => cur(acc), fn[0](...args));
}

Author 回答者: maolovecoding (opens in a new tab)

这种方式更优雅

const compose = (...fns) =>
  fns.reduceRight(
    (prev, next) =>
      (...args) =>
        prev(next(...args)),
  );

Author 回答者: kirazZ1 (opens in a new tab)

之前看了koa的compose实现,先入为主了,感觉写的没上面的优雅,不过能过用例:

function compose(...args) {
  const length = args.length;
  function dispatch(index, val) {
    if (index === length) return val;
    else {
      args = args || [];
      return args[index](dispatch(index + 1, val));
    }
  }
  return (init) => dispatch(0, init);
}

Author 回答者: Hazel-Lin (opens in a new tab)

const compose =
  (...fns) =>
  (params) =>
    fns.reduce((prev, cur) => ((prev = cur(prev ?? params)), prev), null);

看了大佬的实现,感觉我的实现突然不香了