一、什么是柯里化?
柯里化是一个转换过程,把一个接受多个参数的函数,转换成一个接受一个单一参数的函数,并且返回一个函数,返回的函数接受其余的参数,并返回结果。
举个例子:
// 无柯里化
var example1 = function (a, b, c) {
// do something with a, b, and c
};
// 柯里化
var example2 = function(a) {
return function (b) {
return function (c) {
// do something with a, b, and c
};
};
};
二、柯里化的实现
- 首先将参数分割,将除了func之外的参数存进args
- 返回的函数接受新传入的参数并与之前的参数合并,从而将所有的参数传入函数中,并执行真正的函数
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) //f 6
add(1)(2,3).toString() //6
add(1)(2)(3)(4)(5).toString() //15
这样这个函数可以接受任意个数的参数,被调用任意次。
调用过程:
- add(1),返回:
function(){
var newArgs = [1].concat([].slice.call(arguments));
return add.apply(null,newArgs);
}
- add(1)(2,3),相当于:
(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函数,并计算当前为止的结果。
其实这个函数没有什么通用性,通常用于封装特定的函数。还是前面两版柯里化函数比较通用。