概念:只傳遞給函數一部分參數來調用它,讓它返回一個函數去處理剩下的參數;
先看一個簡單例子,add
函數接受 2 個參數(或者多個),addX
函數接受 1 個參數。換而言之,所謂"柯里化",就是把一個多參數的函數,轉化為單參數函數。將一個函數轉換為一個新的函數:
// 非柯里化 function add(x, y) { return x + y; } add(1, 2) === 3; // true // 柯里化 function addX(y) { return function(x) { return x + y; }; } addX(2)(1) == 3; // true
如何實現柯里化函數 curry
在下面的例子中我們對 add 進行了柯里化,從結果上可以看到當參數為 1 個時返回的是個函數,當參數為 2 個的時候返回函數,當參數為 3 個的時候返回函數執行結果
。
var _ = require("ramda"); var add = function(a, b, c) { return a, b, c; }; var curry_add = _.curry(add); console.log(curry_add(1)); // 輸出函數 console.log(curry_add(1)(2)); // 輸出函數 console.log(curry_add(1)(2)(3)); // 輸出結果
根據上述的小栗子,可以得到,柯里化后的函數
如果接受到全部參數則返回函數執行結果
,否則返回一個柯里化函數
。
很容易想到以下偽代碼
var curry = function(fn) { return function() { // 假設柯里化的函數叫 curry_fn // if "curry_fn接受到的參數數量等於fn接受參數的數量" // return "fn的執行結果" // else return "一個柯里化函數" }; };
上述偽代碼是不是很像遞歸?
- 遞歸出口:curry_fn接受到的參數數量等於fn接受參數的數量
- 重復邏輯:return "一個柯里化函數"
於是有了以下簡單實現柯里化的代碼
var curry = function(fn) { var limit = fn.length; // fn接受的參數個數 var params = []; // 存儲遞歸過程的所有參數,用於遞歸出口計算值 return function _curry(...args) { params = params.concat(args); // 收集遞歸參數 if (limit <= params.length) { // 返回函數執行結果 return fn.apply(null, params); } else { // 返回一個柯里化函數 return _curry; } }; };