有意思的参数柯里化

185 阅读2分钟

函数柯里化

前言

在js中,其实有许多的方法能让你的函数变得更加灵活,感觉上就如同成为一个新的函数一样,特别有意思,也特别好用,这次就来介绍一种能让参数固定的方式,函数柯里化 (๑•̀ㅂ•́)و✧

代码实例

   function a(a,b){
         return a+b
    }
    
     var q= curry(a);
     console.log(q(10,12));//22
     
     var q2=q(1);
     console.log(q2(3))  //4
     console.log(q2(-4)) //-3
从上面的这段代码中,可以看到 a方法经过curry方法后,返回了个函数,此时可以执行分开传值的操作,也可以进行一次全部传参直接执行的操作,感觉是不是很有意思 d=====( ̄▽ ̄*)b
相对于直接传入参数的形式来讲,这种形式能让你的函数灵活组合,而不必反复的去引用传参

以下是一个建议版本的两段式柯里化实现

     function curry(fn) {
        return function () {      
            var arg = [].slice.apply(arguments);
            if(fn.length ==arguments.length) return fn.apply(fn,arg); 
            return function(){
               return fn.apply(fn,arg.concat([].slice.apply(arguments)))
            }
        }
    }
我们可以看到,curry函数会将传入的function保存起来,在第一段的时候进行传入参数个数的判定,如果够了就直接执行,不够的话,保留本次的参数,再次返回一个函数

实例优化

从上面的代码中我们可以明显的看到,二段式的柯里化还是有许多的局限性的,例如想成三段,或者传入对象,都会有bug,那么接下来我们就来优化一下
function curry2(f, arg) {
        var arg = arg || [],
            isTrue = typeof arguments[arguments.length - 1] === "boolean" ?
            [].slice.apply(arguments).pop() : true;
        return function () {
            var newArg = isTrue ?
                JSON.parse(JSON.stringify(arg.concat([].slice.apply(arguments)))) :
                arg.concat([].slice.apply(arguments));
            return f.length <= newArg.length ? f.apply(f, newArg) :
                curry2.call(this, f, newArg, isTrue);
        }
    }
    
    var c = {}
    var a2 = curry2(a, [])
    var a3 = a2(c)
    a3(3, c) //{name: "my"} 3 {};
    
    var a2 = curry2(a, []);
    var a3 = a2(c);
    a3(3, c);//{name: "my"} ; {name: "my"}

经过此次优化后,我们可以看到,传入对象后,我们同样的可以选择修改或不修改原来的对象,选择true或不传第三参的话,我们的柯里化函数更加符合函数式编程中,去除副作用的思维方式,传参的话,用在面向对象的模式中会好许多

实际使用

在实际的使用层面来讲,简单举几个例子,例如将url分开组装,axios中可以对需要重复发送请求的的url进行柯里化,亦或者在vue中,我们可以对methodes中专门控制动画或者渲染的方法进行柯里化传参,达到不同的动画效果,还有在react中,函数返回一个组件的时候,我们可以对其传入的参数进行一个固定,达到不同的效果

当然,开发一个小型代码库的时候,同样可以在内部使用这些方式来对代码进行优化滴(o゚v゚)ノ