柯里化是将多参函数转换为单参函数链,每次只传一个参数并返回新函数,直至收齐所有参数才执行;部分应用则可预设任意数量参数,不强制顺序或单参调用。

柯里化不是“把函数变短”,而是“把多参函数拆成单参函数链”
柯里化(Currying)的本质是:把一个接收 n 个参数的函数,转换为接收 1 个参数、返回接收下一个参数的新函数,直到收齐所有参数才真正执行。它和“部分应用(Partial Application)”常被混用,但关键区别在于——柯里化严格按顺序、每次只传一个参数,且返回的函数**固定接受下一个单一参数**;而部分应用可一次传多个、顺序也不强制,返回的函数仍可能接收剩余任意数量参数。
curry 函数怎么写?关注参数收集与闭包边界
最简可行的实现要处理两个核心逻辑:判断是否已收集够参数、用闭包暂存已传参数。注意不能简单用 arguments.length,因为箭头函数没有 arguments,且需兼容 rest 参数写法。
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...moreArgs) {
return curried.apply(this, args.concat(moreArgs));
};
};
}
-
fn.length返回函数定义时的形参个数(不含 rest 参数),是判断“是否收齐”的依据 - 每次调用
curried都形成新闭包,args是当前已传入的参数列表 - 递归返回的内部函数仍叫
curried,保持调用链一致,避免命名污染 - 不支持 rest 参数的原函数(如
(a, b, ...rest))会导致fn.length === 2,此时需手动指定参数长度或改用更健壮的库实现
为什么 lodash.curry 比手写更可靠?
手写版在以下场景容易出问题:
- 原函数有默认参数时,
fn.length不包含带默认值的参数(例如(a, b = 1)的length是1),导致提前触发执行 - 需要绑定
this上下文,手写版未处理bind或call场景 - 想支持占位符(如
_)跳过某参数,手写逻辑会急剧膨胀
lodash.curry 内部做了参数长度推断、上下文透传、占位符管理,且提供 curryRight 和 curry.placeholder 等扩展能力。实际项目中,除非明确控制依赖或学习目的,否则直接用 _.curry(fn) 更稳。
立即学习“Java免费学习笔记(深入)”;
部分应用(Partial Application)怎么实现?别和柯里化硬套
部分应用的目标是“预设若干参数,返回能接收剩余参数的函数”,不要求单参数、不强制顺序。实现更直接:
function partial(fn, ...presetArgs) {
return function(...remainingArgs) {
return fn.apply(this, presetArgs.concat(remainingArgs));
};
}
- 可一次传多个预设参数:
partial(add, 1, 2)→(c) => add(1, 2, c) - 不关心原函数有几个参数,也不检查是否“收齐”,纯粹拼接参数
- 若需支持占位符或动态预设,得额外维护索引映射,复杂度远超柯里化
柯里化是部分应用的一种特例,但反过来不成立。选哪个取决于你的调用模式:如果确定要链式单参数调用(比如 map(curry(fn)(a)(b))),用柯里化;如果只是想固化前几个配置项(比如 const logError = partial(console.error, '[ERROR]')),部分应用更自然。











