对于函数的柯里化(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