拆解compose

42 阅读2分钟

composed例子

export default function compose(...funcs) {
  // 对函数个数为0的情况特判
  if (funcs.length === 0) {
    return (arg) => arg;
  }
  // 对函数个数为1的情况特判
  if (funcs.length === 1) {
    return funcs[0];
  }

  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  );
}

const add1 = (x) => {
  console.log("add1");
  return x + 1;
};
const mul2 = (x) => {
  console.log("mul2");
  return x * 2;
};
const div3 = (x) => {
  console.log("div3");
  return x / 3;
};
const composedFunc = compose(add1, mul2, div3);
console.log(composedFunc(3));

运行日志

div3
mul2
add1
3

我们拆解的主要是这一行代码

  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  );

funcs是一个数组,

reduce

arr.reduce(callback(accumulator, currentValue[, index[, array]])[, initialValue])

reduce有两个参数

  • callback 执行数组中每个值 (如果没有提供 initialValue则第一个值除外)的函数,
  • initialValue 作为第一次调用 callback函数时的第一个参数的值。 如果没有提供初始值,则将使用数组中的第一个元素。 在没有初始值的空数组上调用 reduce 将报错。

所以

  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args))
  );

等价于

  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args)),
    funcs[0]
  );

对吧, 你是这样想的, 但是实际测试一下,

div3
mul2
add1
add1
4

原来的运行日志是3,

现在的运行日志是4,

说明上面的等价, 并不等价

实际上等价于这个

  return funcs.reduce(
    (a, b) =>
      (...args) =>
        a(b(...args)),
    (x) => x
  );

(x)=>x, 是个函数, 相当于数学上的, 如果是加法, 那就是0, 如果是乘法, 那就是1,

总之, 他不能有任何作用; 此处没有作用, 就是它的作用.

callback的四个参数

  1. Accumulator (acc) (累计器)
  2. Current Value (cur) (当前值)
  3. Current Index (idx) (当前索引)
  4. Source Array (src) (源数组)
(a, b) =>
  (...args) =>
    a(b(...args)),

a 是Accumulator (acc) (累计器)

b 是Current Value (cur) (当前值)

然后一直循环组合, 最后是这样的

a(b(c(d(e(f(g(x)))))))

我们可以把这些方法抽象为 f(x)

f 就相当于 a · b · c · d · e · f · g

整个reduce最后返回的是

(...args) =>
  a(b(...args)),

也就是 f(x)