一. 函數柯裡化的定義
函數柯裡化,Function currying,又稱部分求值,指的是逐步傳參,逐步求解的過程。
二. 函數柯裡化的實現
一個Curry函數首先會接受一些參數,接受參數之後,該函數並不會立即求值,而是繼續返回另一個函數,剛才傳入的參數在函數形成的閉包中被儲存起來。待到函數被真正需要求值的時候,之前傳入的所有參數都會被一次性用於求值。
如下:
var add = (function() {
var numArr = [];
return function() {
if(arguments.length === 0) {
var result = 0;
for(var i = 0, len = numArr.length; i < len; i++) {
result += numArr[i];
}
return result;
} else {
// numArr.push(arguments);
Array.prototype.push.apply(numArr, arguments);
}
}
})();
/* =============== 用戶端調用 =============== */
add(21);
add(2);
console.log(add()); // 23
三. 通用的柯裡化函數
柯裡化的操作可以概括為:
接受一個函數;
返回一個只接收一個參數的函數。
通用的柯裡化函數可以如下:
var currying = function(fn) {
var args = [];
return function() {
if(arguments.length === 0) {
// 進行真正的求值計算
return fn.apply(this, args);
} else {
// 儲存調用的所有參數
Array.prototype.push.apply(args, arguments);
return arguments.callee;
}
}
};
那麼上面的add函數可以改寫成如下:
var add = (function() {
var result = 0;
return function() {
for(var i = 0, len = arguments.length; i < len; i++) {
result += arguments[i];
}
return result;
}
})();
調用結果如下:
/* =============== 用戶端調用 =============== */
var addCurrying = currying(add); // 轉換成currying函數
addCurrying(21);
addCurrying(2);
console.log(addCurrying()); // 23
四. 反柯裡化
反柯裡化與柯裡化相反,就是把原來已經固定的參數或者this上下文等當作參數延遲到未來傳遞。
反柯裡化的作用在與擴大函數的適用性,使本來作為特定對象所擁有的功能的函數可以被任意對象所用。更通俗的解釋說反柯裡化是 函數的借用,是函數能夠接受處理其他對象,通過借用泛化、擴大了函數的使用範圍。
反柯裡化的三種寫法如下:
4.1 寫法一:
var uncurrying= function (fn) {
return function () {
var context = [].shift.call(arguments);
return fn.apply(context, arguments);
}
};
4.2 寫法二:
var uncurrying = function(fn) {
return function() {
return Function.prototype.call.apply(fn, arguments);
}
}
4.3 寫法三:
var uncurrying = Function.prototype.bind.bind(Function.prototype.call);
4.4 反柯裡化的實際調用樣本:
/* =============== 用戶端調用 =============== */
var push = uncurrying(Array.prototype.push); // 反柯裡化
var arr = {};
push( arr , ["Bob" , "Jenny" , "Tom"] );
console.log(arr); // Object {0: "Bob", 1: "Jenny", 2: "Tom", length: 3}
五. 柯裡化的適用情境
當一個函數傳遞的參數絕大多數是相同的,且函數結果的可以延遲到參數耗盡時才計算,那麼該函數就適合使用柯裡化。
六. 柯裡化的效能分析
柯裡化肯定會有一些效能開銷,具體效能分析如下:
存取 arguments 對象通常要比存取具名引數要慢一些;
一些老版本的瀏覽器在 arguments.length 的實現上相當慢;
使用 fn.apply() 和 fn.call() 要比直接調用 fn() 要慢點;
建立大量嵌套範圍和閉包會帶來開銷。
七. 總結
函數柯裡化允許和鼓勵你分隔複雜功能變成更小更容易分析的部分。這些小的邏輯單元顯然是更容易理解和測試的,然後你的應用就會變成乾淨而整潔的組合,由一些小單元組成的組合。
但是函數柯裡化也會帶來一些效能開銷。在項目開發中斟酌使用。