主题
柯里化
柯里化表示:把一个接收多个参数的函数,改造成可以分多次逐步接收参数的函数。
柯里化解决什么问题
它最常见的价值是:
- 固定部分参数
- 复用通用逻辑
- 延迟收集参数
- 让函数组合更灵活
一个最直观的例子
js
function add(a, b, c) {
return a + b + c
}柯里化后,可以这样调用:
js
curry(add)(1)(2)(3)也可以这样:
js
curry(add)(1, 2)(3)基本实现思路
核心问题只有一个:
- 当前参数够不够
如果参数已经够了,就执行原函数;如果还不够,就继续返回函数收集参数。
js
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args)
}
return function (...restArgs) {
return curried.apply(this, args.concat(restArgs))
}
}
}调用效果
js
const sum = (a, b, c, d) => a + b + c + d
console.log(curry(sum)(1)(2)(3)(4)) // 10
console.log(curry(sum)(1, 2)(3, 4)) // 10
console.log(curry(sum)(1, 2, 3)(4)) // 10
console.log(curry(sum)(1, 2, 3, 4)) // 10这说明柯里化后的函数不是只能“一次传一个参数”,而是可以分批收集。
为什么经常会用到 fn.length
fn.length 表示函数形参个数:
js
function add(a, b, c) {}
console.log(add.length) // 3柯里化实现通常用它来判断“参数是不是已经收够了”。
柯里化和偏函数的关系
两者很像,但不完全一样。
- 柯里化:把多参数函数改造成可分步传参
- 偏函数:提前固定部分参数,得到一个新函数
js
function multiply(a, b, c) {
return a * b * c
}
const multiplyBy2 = curry(multiply)(2)这里看起来像偏函数效果,但底层仍然来自柯里化后的分步收参。
常见使用场景
预置通用参数
js
function request(baseURL, path) {
return `${baseURL}${path}`
}
const withAPI = curry(request)('/api')
console.log(withAPI('/users')) // /api/users函数组合
在函数式风格代码里,柯里化常用于把“通用逻辑”和“具体参数”拆开。
使用边界
柯里化不是“写得越抽象越高级”。
如果函数只是普通业务逻辑,而且不会被复用成多种预置版本,直接写普通函数通常更清晰。
还要注意:
- 默认参数会影响
fn.length - 剩余参数也会影响“参数是否收够”的判断
- 过度柯里化会让调用链太长,反而难读
使用建议
- 需要分步固定参数、构造通用函数时,再考虑柯里化。
- 手写实现时先抓住“参数不够就继续返回函数”这个核心。
- 看到
fn.length时,要记得它只是一个近似判断,不是万能规则。 - 业务代码里不要为了形式统一而强行柯里化。
