柯里化函数的核心是通过闭包“记住”已传参数,每次调用接收部分参数,待总数满足fn.length时执行原函数;闭包使内部函数持续访问外层参数数组,实现参数累积与延迟求值。

柯里化函数的核心是通过闭包把前面传入的参数“记住”,每次调用只接收一部分参数,直到参数总数满足要求才执行原函数。闭包存储中间变量的方式,就是让外层函数返回一个新函数,这个新函数能访问外层函数作用域中的变量(比如已收集的参数数组)。
基础柯里化实现(用数组存中间参数)
最直观的做法是:每次调用时把新参数 push 到一个数组里,返回的新函数继续等待更多参数;当参数够了,就用 fn(...args) 执行。
示例:
function curry(fn, arity = fn.length) {
return function curried(...args) {
if (args.length >= arity) {
return fn(...args);
} else {
return function(...moreArgs) {
return curried(...args, ...moreArgs);
};
}
};
}
// 使用
const add = (a, b, c) => a + b + c;
const curriedAdd = curry(add);
console.log(curriedAdd(1)(2)(3)); // 6
console.log(curriedAdd(1, 2)(3)); // 6
console.log(curriedAdd(1)(2, 3)); // 6
这里闭包捕获的是 curried 函数内部的 args(每次调用时的实参),以及外层的 fn 和 arity。每次嵌套调用都形成新的闭包,保存当前已收集的参数组合。
用闭包变量显式累积参数(更易理解)
也可以在最外层声明一个数组,在每次调用时往里 push,靠同一个闭包环境共享该数组:
function curry2(fn, arity = fn.length) {
const savedArgs = []; // 闭包变量,所有返回的函数都能访问它
return function(...args) {
savedArgs.push(...args);
if (savedArgs.length >= arity) {
const result = fn(...savedArgs.slice(0, arity));
savedArgs.length = 0; // 可选:重置,支持重复使用
return result;
}
return this; // 或返回自身,支持链式调用(需注意 this 绑定)
};
}
⚠️ 注意:这种写法不适合并发或多次独立调用(因为共用 savedArgs),实际中更推荐第一种“无状态、每次新建闭包”的方式。
关键点:闭包如何保存中间状态?
- JavaScript 中,函数定义时就确定了它能访问的词法作用域;返回的内部函数会携带这个作用域的引用
- 只要内部函数还存在(比如被赋值给变量、作为回调保留),外层函数的局部变量(如参数列表、计数器等)就不会被回收
- 每个柯里化调用链上的函数,其实都对应一个独立的闭包实例——例如
curried(1)创建第一个闭包,保存args=[1];curried(1)(2)创建第二个闭包,保存args=[1,2]
进阶小技巧:支持占位符和动态参数长度
真实库(如 Lodash)还支持占位符(如 _)和自动推断参数个数。简化版可加一个哨兵值判断是否跳过:
const placeholder = Symbol('placeholder');
function curryAdvanced(fn, arity = fn.length) {
return function curried(...args) {
const fullArgs = [];
let idx = 0;
for (let i = 0; i < arity; i++) {
if (idx < args.length && args[idx] !== placeholder) {
fullArgs.push(args[idx++]);
} else {
fullArgs.push(undefined); // 占位,继续等待
}
}
if (fullArgs.every(x => x !== undefined)) {
return fn(...fullArgs);
}
return function(...more) {
return curried(...args, ...more);
};
};
}
这里闭包依然靠嵌套函数维持对 fn、arity 和当前 args 的引用,逻辑更灵活但本质不变。

















