Skip to content

柯里化

柯里化表示:把一个接收多个参数的函数,改造成可以分多次逐步接收参数的函数。

柯里化解决什么问题

它最常见的价值是:

  • 固定部分参数
  • 复用通用逻辑
  • 延迟收集参数
  • 让函数组合更灵活

一个最直观的例子

js
function add(a, b, c) {
  return a + b + c
}

柯里化后,可以这样调用:

js
curry(add)(1)(2)(3)

也可以这样:

js
curry(add)(1, 2)(3)

基本实现思路

核心问题只有一个:

  • 当前参数够不够

如果参数已经够了,就执行原函数;如果还不够,就继续返回函数收集参数。

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

    return function (...restArgs) {
      return curried.apply(this, args.concat(restArgs))
    }
  }
}

调用效果

js
const sum = (a, b, c, d) => a + b + c + d

console.log(curry(sum)(1)(2)(3)(4)) // 10
console.log(curry(sum)(1, 2)(3, 4)) // 10
console.log(curry(sum)(1, 2, 3)(4)) // 10
console.log(curry(sum)(1, 2, 3, 4)) // 10

这说明柯里化后的函数不是只能“一次传一个参数”,而是可以分批收集。

为什么经常会用到 fn.length

fn.length 表示函数形参个数:

js
function add(a, b, c) {}

console.log(add.length) // 3

柯里化实现通常用它来判断“参数是不是已经收够了”。

柯里化和偏函数的关系

两者很像,但不完全一样。

  • 柯里化:把多参数函数改造成可分步传参
  • 偏函数:提前固定部分参数,得到一个新函数
js
function multiply(a, b, c) {
  return a * b * c
}

const multiplyBy2 = curry(multiply)(2)

这里看起来像偏函数效果,但底层仍然来自柯里化后的分步收参。

常见使用场景

预置通用参数

js
function request(baseURL, path) {
  return `${baseURL}${path}`
}

const withAPI = curry(request)('/api')

console.log(withAPI('/users')) // /api/users

函数组合

在函数式风格代码里,柯里化常用于把“通用逻辑”和“具体参数”拆开。

使用边界

柯里化不是“写得越抽象越高级”。

如果函数只是普通业务逻辑,而且不会被复用成多种预置版本,直接写普通函数通常更清晰。

还要注意:

  • 默认参数会影响 fn.length
  • 剩余参数也会影响“参数是否收够”的判断
  • 过度柯里化会让调用链太长,反而难读

使用建议

  • 需要分步固定参数、构造通用函数时,再考虑柯里化。
  • 手写实现时先抓住“参数不够就继续返回函数”这个核心。
  • 看到 fn.length 时,要记得它只是一个近似判断,不是万能规则。
  • 业务代码里不要为了形式统一而强行柯里化。

基于 MIT 许可发布