前言
什么是柯里化,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。柯里化是函数式编程的一个重要概念。它既能减少代码冗余,也能增加可读性。
柯里化的好处
- 参数复用(比如闭包和高阶函数的特性,防抖节流等函数的原理)
- 延迟执行(例如js中的bind方法,实现的机制就是currying)
代码实战
放在以前,我们实现一个带多个参数的函数是这样的
function getUrl(protocol,hostname,pathname){
return `${protocol}://${hostname}/${pathname}`;
}
getUrl('https','www.taobao.com','detail'); // https://www.taobao.com/detail
getUrl('https','www.jd.com','contact');// https://www.jd.com/contact
//前面的https参数每次都要重复传
如果我们考虑用柯里化去解决参数重复问题,通过简单柯里化,我们可以把第一个参数抽离出来,在调用方法后返回一个新的函数,后面调用方法的时候就可以只传剩下的需要变更的参数即可。
funtion getUrl(protocol = 'http'){
return function(hostname,pathname){
return `${protocol}://${hostname}/${pathname}`;
}
}
let joinParams = getUrl('https');
joinParams('www.taobao.com','detail'); // https://www.taobao.com/detail
joinParams('www.jd.com','contact'); // https://www.jd.com/contact
再来一个正则的校验函数的柯里化
//如果不使用柯里化的时候是这么写的,每次使用都需要传入两个参数
function check(reg,str){
return reg.test(str);
}
check(/\d+/,'123'); //true
check(/\w+/,'ronald') //true
//如果我们使用柯里化的话,则可以做到函数方法的复用,在我们写工具库的时候尤为明显
function curryCheck(reg){
return function(str){
return reg.test(str);
}
}
let checkNum = curryCheck(/\d+/);
let checkStr = curryCheck(/\w+/);
checkNum(123);// true
checkStr('ronald'); //false
再比如实现bind方法的重写,bind函数就是使用了延迟执行
Function.prototype.myBind = function (fn, ...args) {
return (...rest) => {
return this.call(fn, ...args, ...rest);
};
};
let info = {
name: "ronald",
age: 18
};
function checkInfo(...rest) {
console.log(this.name, this.age, ...rest);
}
let newCheck = checkInfo.myBind(info, "哈", "噢");
newCheck("高", "富", "帅"); //ronald 18 哈 噢 高 富 帅
柯里化的性能问题
创建大量的嵌套作用域和闭包函数确实会带来额外的开销,但是在现代的浏览器中,主要的性能瓶颈更多的在于在dom操作中,所以这点js性能损耗可以忽略不计,所以合理化的使用currying函数绝对是利大于弊的。
通用的柯里化函数
我们来尝试实现一个通过的柯里化函数
function curry(fn, ...args) {
//先判断一下传进来的参数数量是否和fn里面的参数个数一致,是的话就直接执行fn方法
if (args.length >= fn.length) {
return fn(...args);
}
//否则需要合并参数,并且递归调用柯里化函数
return function (...rest) {
//利用展开符把函数拼接在一起
return curry(fn, ...args, ...rest);
};
}
function sum(x,y,z){
return x + y + z;
}
let sumFunc = curry(sum);
console.log(sumFunc(1)(2)(3));
console.log(sumFunc(1)(2,3));
console.log(sumFunc(1,2)(3));
//以上的结果都是输出6
//重点思想是,只要传入的参数数量大于或者等于传入的方法的参数数量,就可以执行参数。