真正支持多参数分批调用的柯里化需动态判断参数是否收齐:用 fn.length 获取形参个数,闭包累积参数,达标后执行;须用普通函数(箭头函数无 length/arguments);不支持 rest 参数需额外处理;lodash 加 placeholder(如 _)实现参数占位,提升业务灵活性。
柯里化不是简单地把 add(a, b, c) 拆成 add(a)(b)(c),关键在于「自动判断参数是否收齐」。如果硬编码只接受 3 个参数,那遇到 add(1)(2, 3) 或 add(1, 2)(3) 就会出错。
正确做法是让函数持续收集参数,直到总数满足原函数的 length(形参个数),再执行。但要注意:箭头函数没有 arguments 和 length 绑定,所以必须用普通函数;Function.prototype.length 只反映声明时的形参个数,不包含 rest 参数(...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));
};
};
}
fn.length 判断是否“够数”,不是靠预设数字apply(this, ...) 保证原始函数的 this 上下文不丢失(a, b, ...rest) => {})会误判 length === 2,需额外处理
真实场景中,你经常需要跳过某个参数先传后面的,比如 _.curryRight(format, _, 'USD')(1234.56)。原生实现不支持“留空”,因为 curry(add)(1)(undefined)(3) 会被当作传了 undefined,而不是占位。
lodash 用 _ 占位符解决这个问题,内部维护一个标记数组,在最终调用前过滤掉占位符,再按顺序填充真实参数。
ajax(url)(data)(callback) 必须严格按序调用ajax(_, _, headers)(url)(data),适合配置复用不是为了炫技,而是解决具体痛点:事件处理器绑定、高阶组件参数预置、useCallback 防止重复创建。
onClick 传带参数的 handler:onClick={handleClick(id)} 每次渲染都新建函数 → 用 onClick={curry(handleClick)(id)},但注意:必须提前 curry,不能在 render 里调 curry(),否则每次都是新函数withApi(apiConfig)(Component),比写 withApi(Component, apiConfig) 更符合组合习惯useCallback 缓存:若 fetchUser 是柯里化函数,useCallback(fetchUser(userId), [userId]) 才真正依赖稳定柯里化本身开销很小,但副作用常被低估:闭包链变长、堆内存占用上升、Chrome DevTools 中函数名显示为 curried 或 anonymous,断点难定位。
displayName(React)或用 Object.defineProperty(fn, 'name', {...}) 补充curry(fn) 很容易变成 any,需手写泛型重载