你有沒有遇到過這種情況:某個函數被柯里化(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 編輯過