javascript中的函数柯里化

###柯里化 柯里化是函数式编程里的一个技巧,按照维基百科里的定义,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。听起来有点绕口,简单来讲就是给函数分步传参。每次传递部分参数给函数后,会返回一个接受剩余参数的函数。请看下述代码

	function addAll(a, b) {
		return a + b;
	}
	function addCurry(a) {
		return function addCurry1(b) {
				return a + b;
		}
	}
	console.log(addAll(2, 3));
	console.log(addCurry(2)(3));

一个简单的求和函数addAll,柯里化成addCurry。两者实现的功能是一样的,但是方式略有不同,调用addAll(2,3)求和得到5,调用addCurry(2)会返回一个新的函数,该函数接受剩余的参数3,再次调用返回的函数就得到最终结果5(这里利用的js的闭包)。 ###为什么使用柯里化 也许大家会问,我不用柯里化照样能实现功能,而且实现起来也并不麻烦,那我为什么要用柯里化呢?不急,我们来看看下面的几个例子。 ####非柯里化解题

  1. 实现一个求和函数,可以接受多个参数

    使用可变参数以及reduce可以很简单实现

	var add = function(a, b) {
		return a + b;
	}
	var total = function(...rest) {
		return rest.reduce(add);
	}
	console.log(total(2, 3, 4));
  1. 每个参数减1之后再求和

    使用map稍作修改也不难

	var add = function(a, b) {
		return a + b;
	}
	var minus = function(c, d) {
		return c - d;
	}
	var total = function(arg, ...rest) {
		return rest.map(function(item) {
			return minus(item, arg);
		}).reduce(add);
	}
	console.log(total(1, 2, 3, 4));
  1. 每个参数乘2之后再求和

    同上

	var add = function(a, b) {
		return a + b;
	}
	var multiplier = function(e, f) {
		return e * f;
	}
	var total = function(arg, ...rest) {
		return rest.map(function(item) {
			return multiplier(item, arg);
		}).reduce(add);
	}
	console.log(total(2, 2, 3, 4));

发现什么问题了么?在total函数中,我们没有办法同时执行multiplier和minus,代码2切换到代码3,我们需要修改total函数。

柯里化解题
	var add = function(a, b) {
		return a + b;
	}
	var minus = function(c) {
		return function curryMinus(d) {
			return d - c;
		}
	}
	var multiplier = function(e) {
		return function curryMultiplier(f) {
			return f * e;
		}
	}
	var total = function(curry, ...rest) {
		return rest.map(curry).reduce(add);
	}
	console.log(total(minus(1), 2, 3, 4));
	console.log(total(multiplier(2), 2, 3, 4));

将处理参数的函数提取出来(minus和multiplier)并作为参数传递给total,接口清晰了许多,修改起来也更加的方便了,不是吗?

这就是函数式编程的思想,用已有的函数进行组合,生成一个新的函数。柯里化就做到了这一点。 ###柯里化的应用场景

延迟计算
	function curryFunc(cb) {
		var args = [];
		return function curry(...rest) {
			if (rest.length == 0) {
				return cb(...args);
			} else {
				args.push(...rest);
				return curry;
			}
		}
	};
	function add(...rest) {
		return rest.reduce(function(a, b) {
			return a + b;
		});
	};
	var sum = curryFunc(add);
	console.log(sum(2));
	console.log(sum(3)(4));
	console.log(sum());

curryFunc进行了柯里化,传参调用的时候,会返回一个新的函数curry,新函数curry接受剩余参数,直到最后一次调用sum()会计算求和最终结果。 ####动态创建 例如浏览器添加事件监听,考虑到IE兼容性,我们会写下以下代码

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判断,而使用柯里化,我们只需要最开始判断一次就行了,根据判断结果动态生成addEventListener还是attachEvent。

var addEvent = (function() {
	if (window.addEventListener) {
		return function(el, type, fn, capture) {
			el.addEventListener(type, function(e) {
			 	fn.call(el, e);
			}, capture);
		}
	} else if (window.attachEvent) {
		return function(el, type, fn, capture) {
			el.attachEvent("on" + type, function(e) {
				fn.call(el, e);
			});
		}
	} 
})();

在部分计算出结果后(if判断),在此基础上动态生成新的函数处理后面的业务,这样省略了重复计算 ####参数复用 多次调用同一个函数,传递的参数大多数时候相同,那么可以将函数进行柯里化

	var obj = {
		name: 'test'
	};
	var foo = function(prefix, suffix) {
	    console.log(prefix + this.name + suffix);
	}.bind(obj, 'currying-');

	foo('-function1'); // currying-test-function1
	foo('-function2'); // currying-test-function2

bind方法将第一个参数obj设置为foo的执行环境,其他参数 'currying-'传递给foo(函数foo并未执行,其实也可以看成是延迟执行),动态创建返回一个新函数(也是一种动态创建),这个新函数保存了通用参数'currying-'。可以大致写出bind的实现方式:

	Function.prototype.bind = function(context, ...args) {
		return function(...rest) {
			return this.call(context, ...args, ...rest);
		}
	}

转载于:https://my.oschina.net/u/3643736/blog/1535955

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值