之前也有了解過柯里化。可是,在實(shí)際開發(fā)中,對(duì)柯里化的運(yùn)用不是太多,因?yàn)椴惶宄?yīng)該在哪些情況下應(yīng)該使用它們。
看了ES6入門,看到了函數(shù)式編程,現(xiàn)在又再回來復(fù)習(xí)一下柯里化

柯里化 Curry
柯里化就是在特定的作用域中執(zhí)行指定的函數(shù),并只傳遞函數(shù)的一部分參數(shù)來調(diào)用它,返回一個(gè)函數(shù)去處理剩下的參數(shù)。
參數(shù)復(fù)用
先來看個(gè)例子,創(chuàng)建一個(gè) say 函數(shù),打印出帶有名字,前綴和問候語(yǔ)的一句話。
const say = (name, prefix, greeting) => `${greeting}, ${prefix} ${name}!`;
say('Tom', 'Mr', 'Hello'); // "Hello, Mr Tom"
say('James', 'Mr', 'Hello'); // "Hello, Mr James"
在上面的例子中,我們每一次調(diào)用 say 函數(shù)都必須傳入完整的三個(gè)參數(shù),才能保證正確的運(yùn)行結(jié)果,否則,雖然程序還是會(huì)正常運(yùn)行,可是未傳入的部分會(huì)變成 undefined。
const say = greeting => prefix => name => `${greeting}, ${prefix} ${name}!`;
Tip:上面say函數(shù)相當(dāng)于ES5寫法的:
function say(greeting) {
return function (prefix) {
return function (name) {
return greeting + ', ' + prefix + ' ' + name + '!'
}
}
}
然后就可以隨意組合
const greet = say('Hello');
const greetMeiNv = greet('美女');
const greetShuaiGe = greet('帥哥');
greetShuaiGe('Tom'); // "Hello, 帥哥 Tom!"
greetMeiNv('Cindy'); // "Hello, 美女 Cindy!"
Tip: 在使用柯里化的時(shí)候,參數(shù)的順序很重要,可以考慮根據(jù)易變化的程度來排列參數(shù),把不容易變化的參數(shù)通過柯里化固定起來,將需要處理的參數(shù)放到最后一位。
延遲執(zhí)行
上面例子我們每次調(diào)用函數(shù)都只傳入了一個(gè)參數(shù)
如果我想固定Hello 帥哥 ,前兩個(gè)參數(shù),那么第一行的函數(shù)就顯得有點(diǎn)多余。
如果有100個(gè)參數(shù),那不就要?jiǎng)?chuàng)建100個(gè)函數(shù)一個(gè)參數(shù)一個(gè)參數(shù)的傳進(jìn)去調(diào)用?這樣不僅麻煩,而且每個(gè)函數(shù)產(chǎn)生的閉包都會(huì)占用內(nèi)存,那么如果我想讓函數(shù)這樣傳參能夠執(zhí)行:
const curriedSay = curry(say); // 把函數(shù)柯里化
curriedSay('Tom', '帥哥', 'Hello'); // "Hello, 帥哥 Tom!"
const curriedSay2 = curry(say,'Tom','帥哥')
curriedSay2('Hey'); // "Hey, 帥哥 Tom!"
這樣紙,就可以讓你養(yǎng)成拆分函數(shù),并給函數(shù)良好命名的習(xí)慣,以及更好的處理和抽象代碼的邏輯。
那么curry函數(shù)是怎么實(shí)現(xiàn)的呢?
首先要修改say函數(shù) :
const say = (name, prefix, greeting) => `${greeting}, ${prefix} ${name}!`;
那么curry函數(shù):
function curry(fn) {
let outerArgs = Array.prototype.slice.call(arguments, 1);
return function() {
let innerArgs = Array.prototype.slice.call(arguments),
finalArgs = [...outerArgs,...innerArgs];
return fn.apply(null, finalArgs);
};
}
首先第一個(gè)參數(shù)是傳入的函數(shù)。并獲取函數(shù)后面,想要固定參數(shù),轉(zhuǎn)換成數(shù)組賦值到變量outerArgs。
返回的函數(shù)中,獲取非固定參數(shù)賦值給變量innerArgs,并把轉(zhuǎn)換成數(shù)組賦值到變量outerArgs和innerArgs合并起來,一起傳入到被柯里化的函數(shù)執(zhí)行
返回的函數(shù)中有對(duì)outerArgs的引用,所以后面使用curry,也不會(huì)改變outerArgs的值,
參與者模式
參與者模式說白了,也就是給被柯里化的函數(shù)綁定this:
function curryBind(fn,context) {
var outerArgs = Array.prototype.slice.call(arguments, 2);
return function() {
var innerArgs = Array.prototype.slice.call(arguments),
finalArgs = [...outerArgs,...innerArgs]
return fn.apply(context, finalArgs);
};
}
因?yàn)?,函?shù)最少要接受兩個(gè)參數(shù) 被柯里化的函數(shù) 和 綁定對(duì)象
所以,outerArgs要從第三個(gè)參數(shù)開始截取
把a(bǔ)pply中第一個(gè)參數(shù)的null改成context即可
Ramda / Lodash
查閱資料發(fā)現(xiàn)還有專門柯里化函數(shù)的庫(kù),Ramda / Lodash
有興趣的童鞋,可以前去閱讀
