對於函數的柯里化(currying)應該不陌生,簡單來講 Currying 技術是一種經過把多個參數填充到函數體中,實現將函數轉換爲一個新的通過簡化的(使之接受的參數更少)函數的技術。當發現正在調用同一個函數時,而且傳遞的參數絕大多數都是相同的,那麼用一個Curry化的函數是一個很好的選擇.數組
下面利用閉包實現一個curry化的加法函數, 咱們簡單理解一下 curry 化:閉包
function add(x, y){ if(x && y) return x + y; if(!x && !y) throw Error("Cannot calculate"); return function(newx){ return x + newx; }; } add(3)(4); //7 add(3, 4); //7 var newAdd = add(5); newAdd(8); //13 var add2000 = add(2000); add2000(100); //2100
這樣作其實很相似 bind:app
function add(a, b){ console.log(a+b); return a + b; } add(3, 4); //7 add.bind(null, 3)(4); //7 var newAdd = add.bind(null, 5); newAdd(8); //13 var add2000 = add.bind(null, 2000); add2000(100); //2100
同理也可使用 call 和 apply, 由於他們能夠實現 bind 的功能:函數
Function.prototype.bind = function(context){ var _this = this; var args = [].slice.call(arguments, 1); return function (){ innerArgs = [].slice.call(arguments); if(innerArgs && innerArgs.length > 0) args.push.apply(args, innerArgs); return _this.apply(context, args); } } add(3, 4); //7 add.bind(null, 3)(4); //7 var newAdd = add.bind(null, 5); newAdd(8); //13 var add2000 = add.bind(null, 2000); add2000(100); //2100
可是,若是看到了這個題:this
實現一個函數sum,運算結果能夠知足以下預期結果:
sum(1,2,3); //6 sum(2,3)(2); //7 sum(1)(2)(3)(4); //10 sum(2)(4,1)(2); //9
還以爲簡單麼?咱們理一下思路。首先試想一下這個 sum 函數的結構:prototype
function sum(){ return function(){ return function(){ //... } } }
這個函數返回的必定是個函數,但貌似須要寫無限個,這個不合理,咱們修改一下:code
function sum(){ function innerSum(){ //... return innerSum(); } return innerSum(); }
這樣一來每次調用就不須要定義無限個函數了。咱們完善裏面的代碼:io
//sum(1,2,3); //6 //sum(2,3)(2); //7 //sum(1)(2)(3)(4); //10 //sum(2)(4,1)(2); //9 function sum(){ var cur = [].slice.call(arguments).reduce(function(a,b){return a+b;},0); function innerSum(){ var next = [].slice.call(arguments).reduce(function(a,b){return a+b;},0); cur += next; return innerSum; } return innerSum; }
這樣 sum 函數的柯里化過程就完成了,可是這個函數的返回的老是一個函數,這樣咱們如何輸出數值呢?咱們能夠藉助隱式類型轉換須要的 toString 函數實現:console
function sum(){ var cur = [].slice.call(arguments).reduce(function(a,b){return a+b;},0); function innerSum(){ var next = [].slice.call(arguments).reduce(function(a,b){return a+b;},0); cur += next; return innerSum; } innerSum.toString = function(){ return cur; } return innerSum; } console.log(sum(1,2,3)); //6 console.log(sum(2,3)(2)); //7 console.log(sum(1)(2)(3)(4)); //10 console.log(sum(2)(4,1)(2)); //9
計算結果沒錯,咱們還能夠換做 valueOf 實現:function
function sum(){ var cur = [].slice.call(arguments).reduce(function(a,b){return a+b;},0); function innerSum(){ var next = [].slice.call(arguments).reduce(function(a,b){return a+b;},0); cur += next; return innerSum; } innerSum.valueOf = function(){ return cur; } return innerSum; } console.log(sum(1,2,3)); //6 console.log(sum(2,3)(2)); //7 console.log(sum(1)(2)(3)(4)); //10 console.log(sum(2)(4,1)(2)); //9
其實,若是同時存在 toString 和 valueOf 系統會先調用 toString, 而後調用valueOf,返回值天然是 valueOf 的返回值。這個很基礎,這裏就不提了。
通用柯里化方法
通用的柯里化寫法其實比以前的 sum 函數要簡單許多
var currying = function(fn) { // 主要仍是收集全部須要的參數到一個數組中,便於統一計算 var args = [].slice.call(arguments, 1); return function(){ var _args = args.concat([].slice.call(arguments)); return fn.apply(null, _args); } } var sum = function(){ var args = [].slice.call(arguments); return args.reduce(function(a, b) { return a + b; }); }; var sum10 = currying(sum, 10); console.log(sum10(20, 10)); // 40 console.log(sum10(10, 5)); // 25