柯里化是指将使用多个参数的函数转换成一系列使用一个参数的函数的技术。
柯里化的用途主要是参数复用,例如:
function add(a, b) {
return a + b;
}
add(1,2) //3
在柯里化之后或许可以这样使用:
var addCurry = curry(add);
addCurry(1)(2); //3
或许针对这种简单的将两个数相加的场景,柯里化显得有点多余。但是如果我们想使用这个函数完成通用的事情,比如为所有的数加5,就可以使用addCurry(5)(x)
,使得将两个数相加的函数有了通用性。
var curry = function(func){
var args = [].slice.call(arguments,1);
return function(){
var newArgs = args.concat([].slice.call(arguments));
return func.apply(this,newArgs);
}
}
function add(a, b) {
return a + b;
}
var addCurry = curry(add,1,2);
addCurry(); //3
//或者
var addCurry = curry(add,1);
addCurry(2); //3
//或者
var addCurry = curry(add);
addCurry(1, 2) // 3
已经有柯里化的感觉了,但是还没有达到要求,即此柯里化之后的函数只能被调用一次,不能实现addCurry(1)(2)
这样的操作。
我们继续进行改进。
比如说add这个函数接受两个参数,那么针对柯里化之后的函数,若传入的参数没有到达两个的话,就继续调用curry,继续接受参数。若参数到达2个了,就直接调用add函数。
var curry = function(func,args){
var length = func.length;
args = args||[];
return function(){
newArgs = args.concat([].slice.call(arguments));
if(newArgs.length < length){
return curry.call(this,func,newArgs);
}else{
return func.apply(this,newArgs);
}
}
}
var addCurry = curry(add);
addCurry(1,2) //3
addCurry(1)(2) //3
但这一版柯里化函数仍然不能完全满足要求,因为它只针对有特定参数个数的函数适用。
在前端面试中有一个关于柯里化的面试题:
实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6
add(1, 2, 3)(4) = 10
add(1)(2)(3)(4)(5) = 15
我们之前写的柯里化是不能满足这个需求的,因为传入的参数个数是不固定的。
其实这里的需求是我们在柯里化的过程中既能返回一个函数继续接受剩下的参数,又能就此输出当前的一个结果。
这里就需要使用函数的toString来完成。
当我们返回函数的时候,会调用函数的toString来完成隐式转换,这样输出的就不是函数的字符串形式而是我们定义的toString返回的值。这样就既可以保持返回一个函数,又能够得到一个特定的值。
function add(){
var args = [].slice.call(arguments);
var fn = function(){
var newArgs = args.concat([].slice.call(arguments));
return add.apply(null,newArgs);
}
fn.toString = function(){
return args.reduce(function(a, b) {
return a + b;
})
}
return fn ;
}
add(1)(2,3) //6
add(1)(2)(3)(4)(5) //15
这样这个函数可以接受任意个数的参数,被调用任意次。
调用过程:
function(){
var newArgs = [1].concat([].slice.call(arguments));
return add.apply(null,newArgs);
}
同时返回值为此函数的toString结果1。
(function(){
var newArgs = args.concat([].slice.call(arguments));
return add.apply(null,newArgs);
})(2,3);
此时新参数newArgs为[1,2,3],同时再次调用add,返回函数:
function(){
var newArgs = [1,2,3].concat([].slice.call(arguments));
return add.apply(null,newArgs);
}
并且此函数的值为toString的结果即6,因此可以输出6。
其实就是每次都更新当前的参数,重新调用一下add函数,并计算当前为止的结果。
其实这个函数没有什么通用性,通常用于封装特定的函数。还是前面两版柯里化函数比较通用。