函数柯里化与 compose

柯里化

柯里化把接收多个参数的函数转换为可以连续收集参数的函数。

function curry(fn) {
  function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }

    return function collect(...nextArgs) {
      return curried.apply(this, [...args, ...nextArgs]);
    };
  }

  return curried;
}

function sum(a, b, c) {
  return a + b + c;
}

const curriedSum = curry(sum);
curriedSum(1)(2)(3); // 6
curriedSum(1, 2)(3); // 6
curriedSum(1)(2, 3); // 6

实现依赖 fn.length,因此默认参数、剩余参数和可选参数需要额外约定。工程实现可以让调用者显式传入目标参数数量。

compose 与 pipe

compose 从右到左组合一元函数:

function compose(...functions) {
  return (input) => functions.reduceRight(
    (value, fn) => fn(value),
    input,
  );
}

const trim = (value) => value.trim();
const lower = (value) => value.toLowerCase();
const slug = (value) => value.replaceAll(' ', '-');

compose(slug, lower, trim)('  Hello World  '); // hello-world

从左到右执行的版本通常叫 pipe

function pipe(...functions) {
  return (input) => functions.reduce(
    (value, fn) => fn(value),
    input,
  );
}

支持异步函数

function pipeAsync(...functions) {
  return (input) => functions.reduce(
    (promise, fn) => promise.then(fn),
    Promise.resolve(input),
  );
}

高频追问

  • 柯里化与偏函数有什么区别?
  • 如何保留调用时的 this
  • 为什么 fn.length 不完全可靠?
  • 如何让 compose 的第一个函数接收多个参数?