柯里化是将多参函数转为单参函数链的过程,不改变逻辑只改变调用方式;严格柯里化每次只收一个参数,区别于可收多个参数的部分应用;JavaScript 无原生支持,Lodash 的 _.curry 更可靠,支持占位符、rest 参数和 this 绑定。

柯里化不是语法糖,也不是黑魔法——它只是把一个接收多个参数的函数,变成一系列每次只接收一个参数的函数。关键在于:它不改变原函数逻辑,只改变调用方式。
什么是 curry?看一眼就懂的定义
比如 add(1, 2, 3) 是普通调用;柯里化后可以写成 add(1)(2)(3) 或 add(1)(2, 3),甚至 add(1, 2)(3) —— 具体形态取决于实现策略,但核心是「参数分批传入、延迟求值」。
- 柯里化 ≠ 部分应用(partial application):柯里化要求每次只收一个参数(严格柯里化),而部分应用可一次收多个
- JavaScript 没有原生
curry,必须手动实现或借助 Lodash 的_.curry - 返回的新函数始终是纯函数,不修改原函数,也不依赖外部状态
手写一个基础版 curry 函数
最简实现依赖函数的 length 属性(形参个数)来判断是否收集完参数:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
};
}
};
}
-
fn.length只反映声明时的形参个数,遇到 rest 参数(...rest)会返回 0,此时该实现失效 - 没处理
this绑定,若原函数依赖上下文,需用bind或显式传入 - 不支持提前传入空值或
undefined占位,例如curry(add)(1, undefined)(3)不会等待第二个参数
为什么 lodash.curry 更可靠?
Lodash 的 _.curry 默认支持占位符(_)、自动识别 rest 参数、保留 this 上下文,并允许指定最小参数个数(arity):
立即学习“Java免费学习笔记(深入)”;
const add = (a, b, c) => a + b + c; const curriedAdd = _.curry(add); curriedAdd(1)(2)(3); // 6 curriedAdd(1, _, 3)(2); // 6 curriedAdd(1)(2, 3); // 6
- 占位符机制让调用更灵活,适合 UI 回调等参数不确定的场景
- 如果原函数有默认参数(如
(a, b = 1, c)),fn.length返回的是非默认参数个数(这里是 2),Lodash 仍按声明行为处理 - 性能上略低于手写版(多了占位符判断和数组操作),但稳定性远胜
柯里化真正有用的三个场景
别为了函数式而柯里化。它解决的是具体问题:
- 配置复用:
const logError = curry(console.error)('APP')→ 后续直接logError('timeout') - React 事件处理器中避免内联函数(
onClick={curry(handleClick)(id)}比onClick={() => handleClick(id)}更利于shouldComponentUpdate判断) - API 封装:把
fetch(url, options)柯里化为apiGet('/users')(token),分离 endpoint 和 auth 逻辑
最容易被忽略的一点:柯里化函数一旦开始调用,就进入了“累积参数”状态,中间不能重置或跳过某次调用 —— 它没有 cancel、reset 或 peek 接口。需要这类能力时,应该考虑封装成类或使用闭包管理状态,而不是硬套柯里化。











