提高代码质量——函数柯里化currying(带案例讲解)

一起来实现一个需求

需求描述

角色:
	老百姓
行为:
	老百姓可以挣钱但是也会花钱
需求:
	统计老百姓有多少钱

简单实现

也许有人看见这个需求,会很快编出下面的代码
// 老百姓
let commonPeoper = {
    // 存款
    monthlyCost: 100,
    cost: function(money) {
        this.monthlyCost = this.monthlyCost + money;
    }
};

commonPeoper.cost(100);
commonPeoper.cost(200);
commonPeoper.cost(-50);
console.log(commonPeoper.monthlyCost); // 350

减少性能的消耗

没错,十分的简单。但是实际情况并不是每个人在自己收入一笔钱或者
消费一笔钱都会立马去计算自己还有多少钱。而我们的实现,每次收入
或者消费都会进行运算,是会消耗性能的。
我们可不可以在每次消费或者收入时,先把这个数目存起来,等
真正想知道自己还有多少钱的时候再去运算?
此时,我们可能会想出下面的实现。
let commonPeoper = {
    // 存款
    monthlyCost: 100,
    cost: (function(money) {
        let arr = [];
        return function(money) {
            if (money) {
                // 如果传入money值,就先用arr存起来
                [].push.call(arr, money);
            } else {
                // 如果不传入值,我们约定为老百姓要统计自己还有多少钱了
                for (const value of arr) {
                    this.monthlyCost += value;
                }
            }
        };
    })(),
};

commonPeoper.cost(100);
commonPeoper.cost(200);
commonPeoper.cost(-50);
// 统计余额
commonPeoper.cost();
console.log(commonPeoper.monthlyCost); // 350
用闭包实现,没问题。其实这就是函数柯里化。

增加可扩展性

实现了需求,我们大家完事了,但是真的完事了吗?别急,现在我问大家,实际开发时,业务会有这么简单嘛?会不会有类似于统计老百姓前段时间当笔操作金额最大的一笔是多少?前段时间是正收入还是负收入此时我们会去改代码,但是我猜大家也
能发现,改来改去不都是在改这段代码吗?
// 如果不传入值,我们约定为老百姓要统计自己还有多少钱了
for (const value of arr) {
    this.monthlyCost += value;
}
// 如果不传入值,统计老百姓前段时间单笔操作金额最大的一笔是多少
for (const value of arr) {
	// 请注意这是伪代码,我们应该在commonPeoper的身上手动添加一个max属性为0
	if(Math.abs(value) > this.max){
		this.max = Math.abs(value);
	}
}
// 如果不传入值,统计老百姓前段时间单笔操作金额最小的一笔是多少
for (const value of arr) {
	// 请注意这是伪代码,我们应该在commonPeoper的身上手动添加一个changeValue属性为0
		this.changeValue += value;
}

完整代码

let commonPeoper = {
    // 存款
    monthlyCost: 100,
    max: 0,
    changeValue: 0,
    cost: (function(money) {
        let arr = [];
        return function(money) {
            if (money) {
                // 如果传入money值,就先用arr存起来
                [].push.call(arr, money);
            } else {
                // 如果不传入值,我们约定为老百姓要统计自己还有多少钱了
                for (const value of arr) {
                    this.monthlyCost += value;
                }
            }
        };
    })(),
    costB: (function(money) {
        let arr = [];
        return function(money) {
            if (money) {
                // 如果传入money值,就先用arr存起来
                [].push.call(arr, money);
            } else {
                // 如果不传入值,我们约定为老百姓要统计单笔最大金额
                for (const value of arr) {
                    if (Math.abs(value) > this.max) {
                        this.max = Math.abs(value);
                    }
                }
            }
        };
    })(),
    costC: (function(money) {
        let arr = [];
        return function(money) {
            if (money) {
                // 如果传入money值,就先用arr存起来
                [].push.call(arr, money);
            } else {
                // 如果不传入值,我们约定为老百姓要统计收益
                for (const value of arr) {
                    this.changeValue += value;
                }
            }
        };
    })(),
};

commonPeoper.cost(100);
commonPeoper.cost(200);
commonPeoper.cost(-50);
commonPeoper.costB(100);
commonPeoper.costB(200);
commonPeoper.costB(-50);
commonPeoper.costC(100);
commonPeoper.costC(200);
commonPeoper.costC(-50);
// 统计余额
commonPeoper.cost();
// 统计单笔最大金额
commonPeoper.costB();
// 统计收益
commonPeoper.costC();
console.log(commonPeoper.monthlyCost); // 350
console.log(commonPeoper.max); // 200
console.log(commonPeoper.changeValue); // 250
我的天呐,这段代码的耦合度也太高了吧。大量的重复代码?
每一个方法都只有核心业务操作代码不一样罢了。其它的
都是一模一样。这个时候我们使用柯里化之后的好处就出来了,
我给大家书写一下,大家看看怎么样?
// 柯里化函数
const currying = function(fn) {
    let arr = [];
    return function(money) {
        if (money) {
            // 如果传入money值,就先用arr存起来
            [].push.call(arr, money);
        } else {
            // 如果不传入值,开始执行业务代码
            fn.call(this, arr);
        }
    };
}

let commonPeoper = {
    // 存款
    monthlyCost: 100,
    max: 0,
    changeValue: 0,
    cost: currying(function(arr) {
        for (const value of arr) {
            this.monthlyCost += value;
        }
    }),
    costB: currying(function(arr) {
        for (const value of arr) {
            if (Math.abs(value) > this.max) {
                this.max = Math.abs(value);
            }
        }
    }),
    costC: currying(function(arr) {
        for (const value of arr) {
            this.changeValue += value;
        }
    })
};

commonPeoper.cost(100);
commonPeoper.cost(200);
commonPeoper.cost(-50);
commonPeoper.costB(100);
commonPeoper.costB(200);
commonPeoper.costB(-50);
commonPeoper.costC(100);
commonPeoper.costC(200);
commonPeoper.costC(-50);
// 统计余额
commonPeoper.cost();
// 统计单笔最大金额
commonPeoper.costB();
// 统计收益
commonPeoper.costC();
console.log(commonPeoper.monthlyCost); // 350
console.log(commonPeoper.max); // 200
console.log(commonPeoper.changeValue); // 250
后续再有其它的需求业务,直接将业务代码作为参数调用这个柯里化函数是不是
很香呢?

柯里化的定义

前面铺垫了这么多,那么柯里化到底是什么呢?
维基百科:柯里化(英语:Currying),是把接受多个参数的函数
变换成接受一个单一参数(最初函数的第一个参数)的函数,并且
返回接受余下的参数而且返回结果的新函数的技术。

柯里化的作用

1、将多次传入的参数存起来,可以自己选择部分参数进行操作
2、可以避免多次执行相同的代码,手动选择执行。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LiuJie_Boom

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值