你有没有遇到过这种情况:某个函数被柯里化(Currying)了,调用方式变成了fn(a)(b)(c)
,但你突然想让它变回普通的fn(a, b, c)
?这时候,反柯里化就是你的救星!
1. 先说说柯里化是啥(复习一下)
柯里化是把一个多参数函数变成一连串单参数函数的过程。比如:
function add(a, b) {
return a + b;
}
function curriedAdd(a) {
return function(b) {
return a + b;
};
}
console.log(add(1, 2));
console.log(curriedAdd(1)(2));
柯里化好用,但有时候我们拿到一个柯里化函数,却希望它能像普通函数一样调用。这时候就需要反柯里化!
2. 反柯里化:让函数"反悔"
反柯里化的本质是:把一个柯里化函数还原成普通的多参数函数。
举个🌰,假设我写了一个柯里化的乘法函数:
function curriedMultiply(a) {
return function(b) {
return a * b;
};
}
const multiply = curriedMultiply;
但现在我想让它能直接multiply(2, 3)
调用,怎么办?
方案1:手动反柯里化
function uncurry(fn) {
return function(a, b) {
return fn(a)(b);
};
}
const normalMultiply = uncurry(curriedMultiply);
console.log(normalMultiply(2, 3));
方案2:通用反柯里化函数
如果不知道函数被柯里化了几层,可以写一个更通用的版本:
function uncurry(fn) {
return function(...args) {
let currentFn = fn;
for (const arg of args) {
if (typeof currentFn !== 'function') {
throw new Error('参数过多,无法继续调用!');
}
currentFn = currentFn(arg);
}
return currentFn;
};
}
const curriedAddThree = a => b => c => a + b + c;
const normalAdd = uncurry(curriedAddThree);
console.log(normalAdd(1, 2, 3));
3. 我踩过的坑:第三方库的柯里化函数
去年我用一个工具库时遇到了这个问题。库里的某个API是这样的:
const fetchData = (url) => (params) => (options) => {
return fetch(url, { ...params, ...options });
};
每次调用都得写fetchData('/api')({ id: 1 })({ timeout: 5000 })
,太麻烦了!
于是我祭出反柯里化大法:
const normalFetchData = uncurry(fetchData);
normalFetchData('/api', { id: 1 }, { timeout: 5000 });
4. 什么时候用反柯里化?
- 适配第三方库:当库的API是柯里化风格,但你想用普通调用方式时
- 代码重构:团队决定不再使用柯里化,需要批量改造旧代码
- 提高可读性:某些场景下直接传多个参数更直观
5. 反柯里化的局限性
- 参数长度必须固定:如果柯里化函数允许部分应用(如
fn(a)(b)
和fn(a)(b)(c)
混用),反柯里化会失效 - 性能影响:多了一层函数调用,但对大多数场景影响微乎其微
总结
- 柯里化是好东西,但有时候我们需要让函数"回归普通"
- 反柯里化就是把
fn(a)(b)(c)
变回fn(a, b, c)
的技术 - 特别适合处理第三方库的柯里化API
转自https://juejin.cn/post/7512284328867495948
该文章在 2025/6/6 9:15:46 编辑过