一起来实现一个需求
需求描述
角色:
老百姓
行为:
老百姓可以挣钱但是也会花钱
需求:
统计老百姓有多少钱
简单实现
也许有人看见这个需求,会很快编出下面的代码
// 老百姓
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、可以避免多次执行相同的代码,手动选择执行。