标准答案

  1. 柯里化 currying 是把一个多参数函数转换成可以分批接收参数的函数,例如把 fn(a, b, c) 转换成 fn(a)(b)(c) 或 fn(a, b)(c)。
  2. 手写 curry 的核心是收集参数。当已经收集到的参数数量达到原函数需要的参数数量时,执行原函数;否则返回一个新函数继续接收剩余参数。
  3. 偏函数 partial application 是预先固定一部分参数,返回一个等待剩余参数的函数。它不要求把函数拆成连续单参数调用。
  4. 两者的区别在关注点:柯里化强调参数逐步收集和函数组合,偏函数强调复用已有函数并预填部分参数。实现时还要注意 this、默认参数、剩余参数和 fn.length 的边界。

题目解析

柯里化最常见的判断条件是 args.length >= fn.length。这个判断适合普通固定参数函数,但默认参数、剩余参数会改变 length,因此工程工具函数通常允许显式传入 arity。

this 透传是手写题里的基础边界。包装函数如果直接 fn(...args),对象方法被柯里化后可能丢失调用上下文;用 apply 可以把当前调用的 this 继续传给原函数。

柯里化适合参数复用、延迟执行、函数组合和管道处理。普通业务代码不必为了形式感强行柯里化;当它能减少重复参数或让组合更清楚时再使用。

代码示例

下面示例展示支持分批传参的 curry,以及只固定部分参数的 partial。

JavaScript
function curry(fn) {
  function curried(...args) {
    const context = this

    if (args.length >= fn.length) {
      return fn.apply(context, args)
    }

    return function collect(...nextArgs) {
      return curried.apply(context, args.concat(nextArgs))
    }
  }

  return curried
}

function partial(fn, ...presetArgs) {
  return function partiallyApplied(...restArgs) {
    return fn.apply(this, presetArgs.concat(restArgs))
  }
}

const add = (a, b, c) => a + b + c
const curriedAdd = curry(add)

curriedAdd(1)(2)(3) // 6
curriedAdd(1, 2)(3) // 6

const addTen = partial(add, 10)
addTen(2, 3) // 15

常见误区

  • 把柯里化和偏函数完全等同。
  • 只支持一次传一个参数,不支持 fn(a, b)(c) 这种分批传参。
  • 忽略 this 透传,导致方法柯里化后上下文丢失。

作者信息