js学习笔记:柯里化

柯里化是指将使用多个参数的函数转换成一系列使用一个参数的函数的技术。

柯里化的用途主要是参数复用,例如:

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);
    }
}
  • 首先将参数进行分割,也就是将除了func之外的参数存进args。
  • 返回的函数接受新传入的参数并与之前的参数合并,从而将所有的参数传入函数中,并执行真正的函数。
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

这样这个函数可以接受任意个数的参数,被调用任意次。
调用过程:

  • add(1),返回:
    function(){
        var newArgs = [1].concat([].slice.call(arguments));
        return add.apply(null,newArgs);
    } 

同时返回值为此函数的toString结果1。

  • 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函数,并计算当前为止的结果。

其实这个函数没有什么通用性,通常用于封装特定的函数。还是前面两版柯里化函数比较通用。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值