柯里化闭包的参数复用本质是闭包捕获并持久化外层调用的参数,每次调用生成独立作用域互不干扰;手写curry需缓存args、动态判断参数数量、正确绑定this,且curried必须为普通函数而非箭头函数。

什么是柯里化闭包的参数复用本质
柯里化后能复用部分参数,靠的不是语法糖,而是闭包捕获了外层函数调用时传入的参数,并把它们封存在返回函数的词法环境中。只要返回的函数没被垃圾回收,那些参数就一直可用。
关键点在于:每次调用柯里化函数时,它生成的新函数都自带一套独立的闭包作用域,互不干扰。比如 add5 = curryAdd(5) 和 add10 = curryAdd(10) 各自记住自己的第一个参数,后续调用不会串。
手写一个带参数复用能力的 curry 函数
不能直接用 Function.prototype.bind,因为它不支持动态参数数量判断;也不能只靠递归返回函数而不缓存中间状态——那样每次调用都重新计算,失去“复用”意义。
- 用闭包保存已传参数(
args数组),每次调用都追加新参数 - 当累积参数数 ≥ 原函数期望参数数(
fn.length),立即执行并返回结果 - 否则返回新函数,继续等待参数,且该函数仍持有当前
args的引用
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));
};
};
}
注意:这里用 curried.apply(this, ...) 保证 this 上下文正确;如果原函数依赖 this(如对象方法),漏掉这步会导致绑定丢失。
为什么箭头函数在这里不能替代普通函数
闭包要复用参数,必须让内层函数能访问外层函数的 args 变量。箭头函数没有自己的 this 和 arguments,但它会继承外层函数的作用域链——这点没问题;问题出在递归调用上。
- 如果把
curried写成箭头函数:const curried = (...args) => { ... },那内部的curried.apply(...)就会报TypeError: curried is not a function - 因为箭头函数无法在声明完成前被自身引用(不存在变量提升+暂时性死区)
- 而普通函数声明或
function表达式可以被提前访问(函数提升或可被闭包捕获)
所以 curried 必须是具名函数表达式或函数声明,不能是箭头函数。
参数复用时容易忽略的 this 和 new 调用场景
上面的 curry 实现对普通函数调用足够,但如果目标函数是构造器(要用 new 调用),或者依赖严格绑定的 this(比如类方法、事件回调),直接复用会出错。
-
curry返回的函数不能被new调用,否则this指向新实例,但原函数可能没设计为构造器 - 若需支持
new,得在返回函数中检测new.target,并手动Reflect.construct,代价高且破坏纯柯里化语义 - 更现实的做法是:明确区分用途——参数复用场景只用于普通函数调用;构造逻辑单独封装,不混入柯里化链
真正难处理的不是语法,而是你是否清楚那个被闭包锁住的参数,在整个生命周期里会不会被意外修改(比如传入的是对象或数组)。一旦原始参数被改,所有复用它的函数都会看到变化——这不是 bug,是闭包本意,只是常被忽略。










