标准柯里化函数是将多参数函数转换为每次接收部分参数、累计达原函数形参个数时立即执行的单参数链式函数,核心依据fn.length自动判断求值时机,支持占位符实现参数自由组合。

什么是标准柯里化函数
柯里化(Currying)是把接受多个参数的函数变换成接受单一参数(第一个参数)并返回一个新函数的过程,新函数接收余下参数并返回结果。标准柯里化要求:固定参数个数、自动终止求值、支持提前传参、不依赖调用次数判断是否完成。关键不是“越少越好”,而是“参数够了就立刻算,不够就继续等”。
核心实现逻辑:靠函数长度推断目标参数数
JavaScript 中函数的 length 属性返回形参个数(不含 rest 参数),这是判断是否该求值的可靠依据。每次传参后,检查累计参数是否 ≥ 原函数 length;满足则执行,否则返回新的柯里化函数。
- 用闭包保存已传入的参数(
args) - 新函数调用时,合并旧 args 和新参数
- 合并后若长度达标,直接
fn(...merged);否则递归返回柯里化函数 - 为支持动态求值(如后期补全、中途重置),可暴露
resolve()或bind()风格方法,但标准实现通常不强制提供
支持参数自由组合的关键:允许部分传参 + 占位符
纯柯里化默认顺序传参,但“自由组合”需支持跳过某些参数。常见做法是引入占位符(如 _):
- 定义全局占位符对象(如
const _ = Symbol('placeholder')) - 合并参数时,遇到占位符不覆盖原位置,仅记录“此处待填”
- 每次调用都重新检查:所有非占位符位置是否已填满 → 是则执行,否则返回新函数
- 示例:
add(1, _, 3)(2) → 6,其中add是三参数函数,_表示第二个参数暂缺
完整可运行的标准实现(含占位符)
以下代码满足:自动识别参数个数、支持任意顺序补全、一次调用可传多参、返回函数具备相同行为:
const _ = Symbol('curry placeholder');
function curry(fn) {
const arity = fn.length;
function curried(...args) {
const filled = [];
let nextIdx = 0;
// 合并已有参数,处理占位符
for (let i = 0; i < arity; i++) {
if (nextIdx < args.length && args[nextIdx] !== _) {
filled[i] = args[nextIdx++];
} else if (i < curried._args.length && curried._args[i] !== _) {
filled[i] = curried._args[i];
} else {
filled[i] = _;
}
}
// 检查是否全部填满
const isComplete = filled.every(x => x !== _);
if (isComplete) {
return fn(...filled);
} else {
const newCurried = curry(fn);
newCurried._args = filled;
return newCurried;
}
}
curried._args = Array(arity).fill(_);
return curried;
}
使用示例:
const sum3 = curry((a,b,c) => a+b+c);<br> sum3(1)(2)(3); // 6<br> sum3(_, 2, 3)(1); // 6<br> sum3(1, _, 3)(2); // 6<br> sum3(1)(_, 3)(2); // 6
注意事项与边界处理
- rest 参数(...args)会使
fn.length === 0,此时无法自动推断,需手动指定期望参数数(如curry(fn, 3)) - 箭头函数无
length或始终为 0,应避免对其柯里化,或改用命名函数 - 多次调用同一柯里化函数实例不会共享状态,每次都是独立闭包 —— 这是预期行为
- 占位符方案会略微增加开销,生产环境如无需自由组合,用精简版(无占位符)更高效

















