Javascript高级篇之函数柯里化

什么是函数柯里化?

函数柯里化是一种技术,一种将多入参函数变成单入参函数。

这样做会让函数变得更复杂,但同时也提升了函数的普适性。

举个例子 (例一)

//正常函数
function sum(a,b){
  console.log(a+b); 
}

sum(1,2);    //输出3
sum(1,3);    //输出4

//柯里化函数
function curry(a){
    return (b) =>{
        console.log(a+b)
    } 
}

const sum = curry(1);

sum(2);  //输出3
sum(3);  //输出4

例子里,为使用柯里化的函数在入参的时候即使在某一个入参是固定的情况下。也需要一样的去输入,那么这个输入就变得冗余了。

柯里化之后的函数可以省略掉一个固定的入参。

但到这里,还有一个问题。现在只是一层封装的柯里化。如果是四层,五层呢。

假设有这样一个场景(例二)

//柯里化之前
function sum(a,b,c,d,e){
    console.log(a+b+c+d+e)
}
sum(1,2,3,4,5);
//柯里化
function sum1(a){
    return function sum2(b){
        return function sum3(c){
             return function sum4(d){
                 return function sum5(e){
                    console.log(a+b+c+d+e)
                 }
             }
        }
    }
}

sum1(1)(2)(3)(4)(5);

多层柯里化的时候代码会不美观,可读性非常差。

但需求总是在的。我们总会需要多层柯里化的时候。

所有,我们可以封装一个函数来帮助我们完成函数向柯里化的转换。

(例三)

    //函数柯里化封装(这个封装可以直接复制走使用)
    function curry(fn, args) {
            var length = fn.length;
            var args = args || [];
            return function () {
                newArgs = args.concat(Array.prototype.slice.call(arguments));
                if (newArgs.length < length) {
                    return curry.call(this, fn, newArgs);
                } else {
                    return fn.apply(this, newArgs);
                }
            }
        }
        
        //需要被柯里化的函数
        function multiFn(a, b, c) {
            return a * b * c;
        }
        
        //multi是柯里化之后的函数
        var multi = curry(multiFn);
        console.log(multi(2)(3)(4));
        console.log(multi(2, 3, 4));
        console.log(multi(2)(3, 4));
        console.log(multi(2, 3)(4));

柯里化的应用场景

其实柯里化大多是情况下是为了减少重复传递的不变参数。

举个最简单的例子吧。手机号正则校验。

//校验手机号
function validatePhone(regExp,warn,phone){
  const reg = regExp;
  if (phone && reg.test(phone) === false) {
    return Promise.reject(warn);
  }
  return Promise.resolve();
}

//调用校验
validatePhone(/^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$/,"手机号格式不符",187****3311)

这种写法乍一看好像没什么问题。但是,如果你需要多次调用呢?

//调用校验
validatePhone(/^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$/,"手机号格式不符",137****1234)
//调用校验
validatePhone(/^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$/,"手机号格式不符",159****6204)
//调用校验
validatePhone(/^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$/,"手机号格式不符",137****2125)
//调用校验
validatePhone(/^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$/,"手机号格式不符",191****5236)

会发现,正则和提示入参是固定的。很冗余。

我们可以使用我们上面封装的柯里化工具(curry函数)进行如下修改。

//完成柯里化
const curryValid = curry(validatePhone);
const validatePhoneCurry  =curryValid(/^(13[0-9]|14[0-9]|15[0-9]|166|17[0-9]|18[0-9]|19[8|9])\d{8}$/,"手机号格式不符");

//调用柯里化之后的函数
validatePhoneCurry(159****6204);
validatePhoneCurry(137****1234);
validatePhoneCurry(137****2125);
validatePhoneCurry(191****5236);

如上,我们可以省略很多不必要的参数。

当然,柯里化的应用场景还有延时执行(闭包也可以实现,而且更简单)还有提前返回(主要针对IE,IE也马上退休了,这里不认为有赘述的意义)

总结

所谓的函数柯里化,最主要的场景就是减少不必要的固定入参。是函数的一种拓展技术。



posted @ 2023-08-30 14:28  当下是吾  阅读(187)  评论(0编辑  收藏  举报