在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术。
2.释义
function sum(a,b,c){
return a+b+c;
}
function sumCurry(a){
return function(b){
return function(c){
return a+b+c;
}
}
}存在一个一般求和函数:sum,函数调用时需将sum所需的参数一次性传入,即sum(1,2,3),返回求和的结果。而经过柯里化的函数则允许一次传递一个参数,即sumCurry(1)(2)(3),直到参数全部传入后再返回最终的执行结果。
在这里只需要用js闭包的思想去理解就可以,最后返回的function(c){}中可以访问sumCurry中的变量a,以及function(b){}中的变量b。
3.用例
(1)存储变量,方便复用
function styles(color,fontSize){
return {
color:color,
fontSize:fontSize
}
}
var style1 = styles('red',13);//{color:'red',fontSize:13}
var style2 = styles('red',14);//{color:'red',fontSize:14}
var style3 = styles('green',13);//{color:'green',fontSize:13}
var style4 = styles('green',14);//{color:'green',fontSize:14}每次生成一个新的样式都需要传递两个参数,color和fontSize。若在一个页面中,样式的color不变,只是fontSize变化,则每次都需要传递两个参数,略显麻烦。
function stylesCurry(color){
return function(fontSize){
return {
color:color,
fontSize:fontSize
}
}
}
var redStyle = stylesCurry('red');
var greenStyle = stylesCurry('green');
var style1 = redStyle(13);//{color:'red',fontSize:13}
var style2 = redStyle(14);//{color:'red',fontSize:14}
var style3 = greenStyle(13);//{color:'green',fontSize:13}
var style4 = greenStyle(14);//{color:'green',fontSize:14}函数经过柯里化之后,保存了color变量的值,方便了相同color值的复用。存储变量的常见用法,如存储正则表达式,方便复用。
(2)提前返回
var addEvent = function(el, type, fn, capture) {
if (window.addEventListener) {
el.addEventListener(type, function(e) {
fn.call(el, e);
}, capture);
} else if (window.attachEvent) {
el.attachEvent("on" + type, function(e) {
fn.call(el, e);
});
}
};每次调用addEvent都要进行if-else判断
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);
});
};
}
})();而addEvent经过柯里化之后,返回了一个确定的结果,再次调用addEvent时不会再进行if-else的判断。
(3)延迟执行
var money = 0;
function total(m){
money = money+m;
return money;
}
total(1); //1
total(2); //3
total(3); //6每一次调用total函数都会进行一次计算。
function curry(fn){
var _args = [];
return function(){
if(arguments.length===0){
return fn.apply(null,_args);
}else{
_args = _args.concat([].slice.apply(arguments));
}
}
}
var money = 0;
var totalCurry = curry(function(){
for(var i=0;i<arguments.length;i++){
money = money+arguments[i];
}
return money;
})
totalCurry(1);
totalCurry(2);
totalCurry(3);
totalCurry();//6 到这里计算4.通用柯里化函数
常见形式的通用柯里化函数:
var s = 0;
function sum(){
for(var i=0;i<arguments.length;i++){
s = s+arguments[i];
}
return s;
}
var currying = function(fn) {
// args 获取第一个方法内的全部参数
var args = Array.prototype.slice.call(arguments, 1)
return function() {
// 将后面方法里的全部参数和args进行合并
var newArgs = args.concat(Array.prototype.slice.call(arguments))
// 把合并后的参数通过apply作为fn的参数并执行
return fn.apply(this, newArgs)
}
}
var _sum = currying(sum);
_sum(1,2);//3
_sum(4,5);//12 (3+4+5)该方式的柯里化函数存在缺陷,不支持_sum(1)(2)这种调用方式
优化版:
function sum(a,b,c){
return a+b+c;
}
var curry = function(fn,args){
//将数组的slice方法保存下来方便其他类数组对象调用
var slice = Array.prototype.slice;
//fn函数接收的参数个数
var len = fn.length;
//将fn以外的其他参数保存起来
var _args = args ? slice.call(args) : [];
return function () {
//将以前存储的参数与新传递的参数结合起来
var argsNew = _args.concat(slice.call(arguments));
//参数数量大于fn需要的参数数量时执行函数,否则继续存储参数
if (argsNew.length >= len) {
return fn.apply(this, argsNew);
} else {
return curry.call(this, fn, ...argsNew);
}
}
}
var _sum = curry(sum);
_sum(1)(2)(3); //6
_sum(1,2,3); //6
_sum(1,2)(3); //6该通用柯里化函数仅适用于参数数量确定的函数。
5.无限参数的柯里化
常见面试题:实现一个add方法,使计算结果能够满足如下预期:
add(1)(2)(3) = 6;
add(1, 2, 3)(4) = 10;
add(1)(2)(3)(4)(5) = 15;
function add() {
// 第一次执行时,定义一个数组专门用来存储所有的参数
var _args = [].slice.call(arguments);
// 在内部声明一个函数,利用闭包的特性保存_args并收集所有的参数值
var adder = function () {
var _adder = function () {
[].push.apply(_args, [].slice.call(arguments));
return _adder;
};
// 利用隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
_adder.toString = function () {
return _args.reduce(function (a, b) {
return a + b;
});
}
return _adder;
}
return adder.apply(null, [].slice.call(arguments));
}
add(1)(2)(3);//6
add(1)(2)(3)(4);//10
add(1,2,3,4)(5);//156.参考链接
www.zhangxinxu.com/wordpress/2…