函数柯里化
什么是函数柯里化(curry)
函数柯里化(curry)是函数式编程里面的概念。curry的概念很简单:只传递给函数一部分参数来调用它,让它返回一个函数去处理剩下的参数。
简单点来说就是:每次调用函数时,它只接受一部分参数,并返回一个函数,直到传递所有参数为止。
维基百科中定义的总结:
- 输入是一个函数,并且这个函数拥有n个参数
- 输出也是一个函数,并且可以使用
fn()()()
这种方式调用 - 参数被柯里化过程中的函数被拆分
函数柯里化的作用
参数复用
最简单的例子,如果我们有一个拼接网址的函数
function joinWebSite(protocol, companyName, domain) {
return `${protocol}://${companyName}.${domain}`;
}
console.log(joinWebSite('http', 'baidu', 'com')); // http://baidu.com
现在看起来这个函数好像可以满足我的需求,但是其实我们很多请求都会是http
或者https
,这样每次都要写这个参数的话就会很麻烦,所以我们用柯里化改写
function createCurry(protocol) {
return function (companyName, domain) {
return `${protocol}://${companyName}.${domain}`;
}
}
let httpProtocal = createCurry('http');
let httpsProtocal = createCurry('https');
console.log(httpProtocal('tencent', 'com')); // http://tencent.com
console.log(httpProtocal('baidu', 'com')); // http://baidu.com
console.log(httpsProtocal('tencent', 'com')); // https://tencent.com
console.log(httpsProtocal('baidu', 'com')); // https://baidu.com
我们这里使用闭包,将我们的protocal
存在外层函数,返回内层函数,这样内层函数就都能访问得到protocal
这个参数,也就实现了我们的参数复用。
提前返回
最简单的例子就是我们在做浏览器兼容的时候去兼容ie
的事件处理函数的时候要做不同处理。如果我们每次去做事件监听的时候都去判断的话就会很麻烦,我们这边使用一个立即执行函数,在浏览器加载到这一部分代码的时候就会去执行这一段代码,这边做了判读之后就会把满足这个浏览器的事件监听函数返回过来,我们之后直接使用addEvent
这个我们封装好的函数就行了。
var addEvent = (function(){
if (window.addEventListener) {
return function(el, sType, fn, capture) {
el.addEventListener(sType, function(e) {
fn.call(el, e);
}, (capture));
};
} else if (window.attachEvent) {
return function(el, sType, fn, capture) {
el.attachEvent("on" + sType, function(e) {
fn.call(el, e);
});
};
}
})();
延迟执行
function add(x, y, z, a) {
return x + y + z + a;
}
function curry(context, args) {
var args = args || [];
return function() {
_args = [...args, ...arguments];
if (_args.length >= context.length) {
// 参数收集完毕,则执行func
return context(..._args);
} else {
// 如果参数个数小于最初的func.length,则递归调用,继续收集参数
return curry(context, _args);
}
}
}
let addEnhanceByCurry = curry(add);
console.log(addEnhanceByCurry(1)(2)(3)(4));
console.log(addEnhanceByCurry(1, 2, 3, 4));
console.log(addEnhanceByCurry(1, 2)(3, 4));
console.log(addEnhanceByCurry(1)(2)(3, 4));
实现一个加法函数,但是我的参数不是一次传完的,我们只知道总的参数长度,所以我们这里用柯里化改写这个函数,通过柯里化函数curry
,如果参数不够就继续返回函数,如果参数够了的话就执行我们传进去的函数
function creatCurry() {
let _arg = [...arguments];
var _add = function() {
_arg = [..._arg, ...arguments];
return _add;
}
_add.toString = function() {
return _arg.reduce((a, b) => {return a + b}, 0);
}
return _add;
}
console.log(creatCurry(1, 2, 3, 4, 5)); // f 15
console.log(creatCurry(1, 2, 3, 4, 5) + 0); // 15
console.log(creatCurry(1, 2, 3, 4)(5)); // f 15
console.log(creatCurry(1)(2)(3)(4)(5)); // f 15
上一个因为我们的参数里面有一个函数,所以我们可以通过这个函数所需要的参数长度来确定我们什么时候可以去执行函数而不是返回函数。但是我们现在这种情况我们是不知道我们到底需要多少个参数的,这样的话我们可以通过改写toString()
这个方法或者valueOf()
去实现我们的功能,因为我们如果直接打印一个函数的话,默认会调用这个函数的toString
方法。这样当我们不需要调用我们的函数的时候,给我们的返回值就是调用toString
之后的值。真的妙不可言