作者:yanyige | 发布时间:2017-04-24 21:34

函数式编程

上次参加360前端星计划,被月影大大的JS进阶教程虐了一番之后,过程抽象这个概念就一直在我的脑中盘旋。“到底什么是过程抽象?” 月影老师提到,过程抽象是一种编程思路,我目前认为,过程抽象应该是函数式编程的一种。

在淘宝FED前端团队中一篇博客中提到:

函数式编程可以理解为,以函数作为主要载体的编程方式,用函数去拆解、抽象一般的表达式

说道用函数去拆解、抽象一般的表达式,在第一时间,我就想到了函数柯里化(currying)

函数柯里化(currying)

currying 又称部分求值。一个currying 的函数首先会接受一些参数,接受了这些参数之后, 该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。

假设我们要编写一个计算每月开销的函数。在每天结束之前,我们都要记录今天花掉了多少钱。代码如下:

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 );
        }
    }
})();

现在实现一个通用的currying。

var currying = function(fn) {
    var args = [];
    return function() {
        if(arguments.length == 0) {
            return fn.apply(this, arguments);
        } 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

这样也可以是一种方法:

function funnyPlus (num) {
	let sum = 0;
	const dosum = function (tosum) {
		if(arguments.length == 0) return sum;
		sum += tosum;
		return dosum;
	}
	return dosum(num);
}