在計算機科學中,偏函數應用 是指將一些參數固定到一個函數,產生另外一個較小的函數的過程。javascript
舉個例子:java
// 咱們建立了一個作 乘法運算 的函數
function mult(a, b) {
return a * b;
};複製代碼
基於這個函數,咱們利用bind
創造一個新的函數 double
:編程
let double = mult.bind(null, 2);
console.log( double(3) ); // mult(2, 3) = 6;
console.log( double(4) ); // mult(2, 4) = 8;
console.log( double(5) ); // mult(2, 5) = 10; 複製代碼
mult.bind(null, 2)
創造了一個新函數 double
,傳遞調用到mult
函數,以null
爲context
,2
爲第一個參數。其餘參數等待傳入。api
這就是 偏函數應用 —— 咱們創造了一個新函數,同時將部分參數替換成特定值。app
剛剛咱們基於mult
建立了一個新的函數double
,這裏咱們再建立一個新的函數triple
:編程語言
let triple = mult.bind(null, 3);
console.log( triple(3) ); // mult(3, 3) = 9;
console.log( triple(4) ); // mult(3, 4) = 12;
console.log( triple(5) ); // mult(3, 5) = 15;複製代碼
使用 偏函數,咱們可以從中受益的是:咱們建立了一個獨立的非匿名函數(double
,triple
)。咱們可使用它,而不須要每次都傳入第一個參數,由於bind
幫咱們搞定了。函數式編程
在其餘的場景中,當咱們有一個很是通用的函數,而且想要方便地獲取它的特定變體,偏函數也是很是有用。函數
舉個例子,咱們擁有函數post(signature, api, data)
。在調用請求方法的時候有着相同的用戶簽名,這裏咱們想要使用它的偏函數變體:post(api, data)
,代表該請求發送自同一用戶簽名。post
有時候人們會把 偏函數應用 和另一個名爲 柯里化 的東西混淆,但那的確是另一個和函數有關的有趣技術。ui
在 函數式編程語言 和 其餘許多語言 中,柯里化(currying)提供了一種自動管理參數如何傳遞給函數和異常的方法。
簡單來講,currying
是一項將一個調用形式爲f(a, b, c)
的函數轉化爲調用形式f(a)(b)(c)
的技術。
舉個例子:
function currying(fn) {
return function(a) {
return function(b) {
return fn(a, b);
};
};
}
// 用法
function sum(a + b) {
return a + b;
}
let carriedSum = currying(sum);
console.log( carriedSum(1)(2) ); // 3複製代碼
從上面的例子能夠看到,carrying
的實現就是一系列的封裝。
currying(fn)
的結果就是一層封裝function(a)
。carriedSum(1)
這樣,它的參數被保存到 詞法環境 中,而後返回一層新的封裝function(b)
。carriedSum(1)(2)
調用function(b)
,傳入參數2
,它將調用傳遞給初始的多參數函數sum
。結合以前的 偏函數 知識,咱們能夠看到,sum
函數在 柯里化 以後,逐個傳遞參數的時候返回的那一層封裝:其實就是 sum
函數 的 偏函數變體。
這可能也是你們容易將這個兩個概念搞混的緣由之一吧。
高級的柯里化同時容許 函數正常調用 和 獲取偏函數。
咱們能夠實現一下 高級的柯里化:
function currying(fn) {
return function curried(...args) {
if(args.length>=fn.length) {
// 傳入的實參長度 >= 初始函數形參長度 的時候,則直接執行初始函數
return fn.apply(this, args);
} else {
// 不然 獲得一個 偏函數,遞歸carried方法,直到得到全部參數後,直接執行
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
}
}
function sum(a, b, c) {
return a + b + c;
}
let curriedSum = currying(sum);
// 常規調用
console.log( curriedSum(1, 2, 3) ); // 6
// 獲得 curriedSum(1)的偏函數,而後用另外兩個參數調用它
console.log( curriedSum(1)(2, 3) ); // 6
// 徹底柯里化調用
console.log( curriedSum(1)(2)(3) ); // 6複製代碼
從上面的 高級柯里化 實現的例子中能夠發現:
sum
函數 在 柯里化 以後對於使用並無任何影響,仍然能夠被正常調用。總得來講,柯里化的目的 就是在不影響 初始函數 的調用形式的狀況下,更方便的讓咱們得到初始函數的 偏函數 變體。
最後,歡迎各位小夥伴留言,相互討論。