柯里化定義
維基百科定義
- 是把接收多個參數(shù)的函數(shù),變成接受一個單一參數(shù)(最初函數(shù)的第一個參數(shù))的函數(shù),并且返回接受余下的參數(shù),而且返回結(jié)果的新函數(shù)的技術(shù)
- 柯里化聲稱 “如果你固定某些參數(shù),你將得到接受余下參數(shù)的一個函數(shù)”
簡單講就是將函數(shù)的多個參數(shù)返回不同的函數(shù)執(zhí)行,將多個參數(shù)進(jìn)行執(zhí)行拆解成單步驟執(zhí)行,一個函數(shù)執(zhí)行一個步驟
更簡單的講只傳遞給函數(shù)一部分參數(shù)來調(diào)用它,讓它返回一個函數(shù)去處理剩余的參數(shù)
舉個例子
function foo(a, b, c) {
return a + b + c;
}
//柯里化處理之后的函數(shù)
function sum(a) {
return function (b) {
return function (c) {
return a + b + c;
};
};
}
console.log(foo(10, 20, 30));
console.log(sum(10)(20)(30));
foo函數(shù)接收三個函數(shù),利用柯里化將三個參數(shù)返回相對應(yīng)的函數(shù)去執(zhí)行,每個函數(shù)執(zhí)行一個對參數(shù)的操作
可以將上面的柯里化處理函數(shù)簡化
const sum = (a) => (b) => (c) => a + b + c;
但是在將上面的例子來看,進(jìn)行柯里化之后的函數(shù),步驟繁瑣,執(zhí)行效率也不高,但是為什么還要將函數(shù)進(jìn)行柯里化呢,這里就涉及到設(shè)計模式相關(guān)的,有一個重要的模式就是讓函數(shù)的職責(zé)單一
- 當(dāng)我們在進(jìn)行函數(shù)式編程,每一個函數(shù)都盡可能只處理一類或者一個問題,而不是將所有的問題交給一個函數(shù)進(jìn)行處理
- 那么我們是否就可以將每次傳入的參數(shù)在單一的函數(shù)中進(jìn)行處理,處理完后在下一個函數(shù)中再使用處理后的結(jié)果
function sum(a) {
a = a + 2
return function (b) {
b = b * 2
return function (c) {
c = c ** 2
return a + b + c;
};
};
}
柯里化還有一個重要的使用場景是對參數(shù)復(fù)用
function log(date, type, message) {
console.log(
`[${date.getHours()}:${date.getMinutes()}]:[${type}]:[${message}]`
);
}
log(new Date(), 'debug', '參數(shù)錯誤');
log(new Date(), 'debug', 'not a function');
log(new Date(), 'debug', 'bad request');
以上面的例子來說,前兩個參數(shù)是重復(fù)的,每次都要寫同樣的代碼,唯一不同的是后面的message不同,如果有大量的日志需要重寫,會非常麻煩,這時就可以用柯里化將參數(shù)復(fù)用
const log = (date) => (type) => (message) => {
console.log(
`[${date.getHours()}:${date.getMinutes()}]:[${type}]:[${message}]`
);
};
//時間重復(fù)
const datelog = log(new Date());
datelog('debug')('參數(shù)錯誤');
datelog('request')('bad request');
//時間和類型重復(fù)
const dataAndType = log(new Date())('debug');
dataAndType('參數(shù)錯誤');
dataAndType('not a function');
dataAndType('bad request');
通過柯里化可以將重復(fù)的參數(shù)進(jìn)行復(fù)用,減少了代碼的冗余
自動柯里化函數(shù)實現(xiàn)
上面的柯里化函數(shù)都是手動實現(xiàn)的,接下來用一個函數(shù)來自動實現(xiàn)柯里化的過程
function currying(fn) {
//傳入需要處理的函數(shù)
function curryied(...args) {
//傳入處理函數(shù)的參數(shù)
/**
* 1.fn(10,20,30)
* 2.fn(10)(20)(30)
* 3.fn(10,20)(30)
*/
//第一種情況
//函數(shù)調(diào)用時參數(shù)全部使用
if (args.length >= fn.length) {
return fn.apply(this, args); //直接返回當(dāng)前調(diào)用的函數(shù),并且綁定當(dāng)前調(diào)用函數(shù)的this
} else {
//第二種情況
//第二種情況和第三種情況是一樣的,這里是將第二種情況轉(zhuǎn)換為第三種情況,在轉(zhuǎn)換為第一種情況
//遞歸調(diào)用
//此時args是10,args2是20,
//現(xiàn)在是需要將傳遞過來的參數(shù)進(jìn)行拼接,直到轉(zhuǎn)變成第一種情況
function curryied2(...args2) {
return curryied.apply(this, [...args, ...args2]);
}
return curryied2;
}
}
return curryied;
}
function add(x, y, z) {
return x + y + z;
}
var curryadd = currying(add);
console.log(curryadd);
console.log(curryadd(10)(20)(30));
console.log(curryadd(10, 20, 30));
console.log(curryadd(10, 20)(30));