柯里化是将多参数函数转化为单参数函数链的过程,如add(1,2,3)变为add(1)(2)(3),每次调用只传一个参数,参数满足时执行;它不改变逻辑,仅改变调用方式,区别于偏函数,需注意fn.length限制、this绑定及生产环境推荐使用lodash或ramda。

柯里化不是“把函数变复杂”,而是把多参数函数拆成一系列单参数函数,每次调用只传一个参数,直到参数够了才真正执行。
什么是柯里化:从 add(1, 2, 3) 到 add(1)(2)(3)
柯里化(Currying)是把接收多个参数的函数,转化为接收单一参数的函数链。它不改变函数逻辑,只改变调用方式和参数传递节奏。
典型表现:
- 原函数:
const add = (a, b, c) => a + b + c,调用:add(1, 2, 3) - 柯里化后:
const curriedAdd = curry(add),调用:curriedAdd(1)(2)(3)或curriedAdd(1)(2, 3)(取决于实现是否支持多参收集聚合)
注意:柯里化 ≠ 偏函数(Partial Application)。偏函数可一次传多个参数并固定部分参数,而严格柯里化每次只接受一个参数。
立即学习“Java免费学习笔记(深入)”;
手写一个基础 curry 函数(ES5+ 兼容)
核心思路:利用闭包暂存已传参数,用 arguments 或剩余参数收集输入,参数数量满足时执行原函数。
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
}
};
}使用示例:
const multiply = (a, b, c) => a * b * c;
const curriedMul = curry(multiply);
console.log(curriedMul(2)(3)(4)); // 24
console.log(curriedMul(2, 3)(4)); // 24(因内部用 args.length >= fn.length 判断,支持“凑够即执行”)关键点:
-
fn.length返回函数声明时的形参个数(不包括 rest 参数),所以适用于普通函数 - 未满足参数数时,返回新函数,继续累积参数
- 必须用
apply保持this上下文,否则对象方法柯里化后会丢失绑定
常见陷阱:箭头函数、arguments、rest 参数与 fn.length
以下情况会让基础 curry 失效或行为异常:
- 原函数是箭头函数 →
fn.length仍有效,但箭头函数没有arguments和this,若你依赖this就不能直接柯里化箭头函数 - 原函数含 rest 参数(如
(a, b, ...rest) => {})→fn.length返回 2(忽略...rest),导致提前触发执行 - 想支持任意调用方式(如
f(1)(2, 3, 4)(5))→ 基础实现只看“当前层参数总数”,不区分“本次传几个”,需改用计数器或显式toString触发求值 - 异步函数柯里化后,返回的是 Promise,不是函数 → 不影响柯里化本身,但调用链末尾需
await,别忘了处理 Promise 链
生产环境建议:用 lodash.curry 或 ramda.curry
自己写的 curry 很难覆盖所有边界(如 this 绑定、new 调用、getter/setter、Symbol.toStringTag 等)。实际项目中更推荐成熟库:
-
import { curry } from 'lodash':支持占位符(_),允许跳过参数,如curry(add)(1, _, 3)(2) -
import { curry } from 'ramda':默认严格单参数,且对函数式组合更友好;ramda的curry还能自动识别length并兼容 rest 参数(通过解析 AST?不,其实是靠用户显式传入arity)
真正容易被忽略的是:柯里化后函数的 length 变成 1,这会影响依赖函数元信息的工具(比如某些类型推导、装饰器、AOP 框架),调试时看到 (...args) => {} 就知道它大概率被柯里化过了。











