javascript高阶函数应用——柯里化
高阶函数:
在数学和计算机科学中,高阶函数是至少满足下列一个条件的函数:
1.接受一个或多个函数作为输入:forEach sort map filter reduce
2.输出一个函数:lodash.curry
不过它也可以同时满足两个条件:Function.prototype.bind
这里对高阶函数不过多讲解,这节我们来学习柯里化。
在计算机科学中,柯里化(Currying)是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数且返回结果的新函数的技术 。在直觉上,柯里化声称“如果你固定某些参数,你将得到接受余下参数的一个函数”。所以对于有两个变量的函数yx,如果固定了 y = 2,则得到有一个变量的函数 2x——柯里化·百度百科
由以上,我们来一段代码
// 假设我们要写一个+1的函数
function sum(x, y){
return x+y
}
//此时,这个sum就称为对于x和y的函数
sum(1, 2) // 3
//由柯里化的概念,我们可以固定一个参数,使后续操作可以只传一个参数
function addOne(y){
return 1+y
}
addOne(2) //3
addOne(7) //8
//可如果,又有一个+2的需求呢,那好,我再写一遍
function addTwo(a){
return a+2
}
addTwo(3) //5
//那再有+3,+4的需求呢?
这时,我们就可以使用柯里化来简便这个过程,柯里化是惰性求值的,即在你需要的到值的时候,再取值。
这里借鉴一下《JavaScript设计模式与开发实践》书中的例子
假设我们要编写一个计算每月开销的函数。在每天结束之前,我们都要记录今天花掉了多少钱。代码如下:
var monthlyCost = 0;
var cost = function( money ){
monthlyCost += money;
};
cost( 100 ); // 第 1 天开销
cost( 200 ); // 第 2 天开销
cost( 300 ); // 第 3 天开销
//cost( 700 ); // 第 30 天开销
alert ( monthlyCost ); // 输出:600
通过这段代码可以看到,每天结束后我们都会记录并计算到今天为止花掉的钱。但我们其实并不太关心每天花掉了多少钱,而只想知道到月底的时候会花掉多少钱。也就是说,实际上只需要在月底计算一次。如果在每个月的前 29 天,我们都只是保存好当天的开销,直到第 30 天才进行求值计算,这
样就达到了我们的要求。
以上需求,我们就可以利用柯里化的思想来惰性求值,先将一部分参数存起来,当我们实际需要值的时候再来求值,虽然下面的 cost 函数还不是一个 currying 函数的完整实现,但有助于 我们了解其思想。
var cost = (function(){
var args = [];
return function(){
if ( arguments.length === 0 ){
var money = 0;
for ( var i = 0, l = args.length; i < l; i++ ){
money += args[ i ];
}
return money;
}else{
[].push.apply(args, arguments)
//或者 args = args.concat([].slice.call(arguments))
}
}
})()
cost(100)
cost(253)
cost() // 353
接下来,我们要写一个正式的curry函数
// 第一个参数是我们需要柯里化的原函数,同时,在它后边你还可以传入一些初始的值。
//这里,你可以注明形参(只传fn的话),因为考虑到初始值的问题,我们就以arguments来代替了
var currying = function( fn ){ // 参数为原函数
var args = [];
return function(){ // 柯里化后的新函数
if ( arguments.length === 0 ){ // 参数数数量为0时,意为求值
return fn.apply( this, args );
}else{ // 否则,将这些值保存起来
[].push.apply( args, arguments );
return arguments.callee;
}
}
};
var cost = (function(){
var money = 0;
return function(){
for ( var i = 0, l = arguments.length; i < l; i++ ){
money += arguments[ i ];
}
return money;
}
})();
var cost = currying( cost ); // 转化成 currying 函数
cost( 100 ); // 未真正求值
cost( 200 ); // 未真正求值
cost( 300 ); // 未真正求值
alert ( cost() ); // 求值并输出:600
至此,我们就完成了对柯里化的初步了解