思考:执行add(1,2,3)(2)()
就能输出1+2+3+2=8
。
const adder = function () {
let _args = [];
return function () {
if (arguments.length === 0) {
return _args.reduce(function (a, b) {
return a + b;
});
}
[].push.apply(_args, [].slice.call(arguments));
return arguments.callee;
}
};
const sum = adder();
console.log(sum); // Function
sum(100, 200)(300); // 调用形式灵活,一次调用可输入一个或者多个参数,并且支持链式调用
sum(400);
console.log(sum()); // 1000 (加总计算)
柯里化(curring)
柯里化又称部分求值,字面意思就是不会立刻求值,而是到了需要的时候再去求值。
栗子1
我们有这样一个场景,记录程序员一个月的加班总时间,那么好,我们首先要做的是记录程序员每天加班的时间,然后把一个月中每天的加班的时间相加,就得到了一个月的加班总时间。原文
let monthTime = 0;
function overtime(time) {
return monthTime += time;
}
overtime(3.5); // 第一天
overtime(4.5); // 第二天
overtime(2.1); // 第三天
//...
console.log(monthTime); // 10.1
每次传入加班时间都进行累加,这样当然没问题,但你知道,如果数据量很大的情况下,这样会大大牺牲性能。
那怎么办?这就是柯里化要解决的问题。
其实我们不必每天都计算加班时间,只需要保存好每天的加班时间,在月底时计算这个月总共的加班时间,所以,其实只需要在月底计算一次就行。
下面的overtime
函数还不是一个柯里化函数的完整实现,但可以帮助我们了解其核心思想:
const overtime = (function () {
let args = []
return function () {
if (arguments.length === 0) {
let time = 0
for (let i = 0; i < args.length; i++) {
time += args[i]
}
return time
} else {
[].push.apply(args, arguments) // apply 将数组转化为一个个参数传入 push 中
}
}
})()
overtime(3.5); // 第一天
overtime(4.5); // 第二天
overtime(2.1); // 第三天
//...
console.log( overtime() ); // 10.1
柯里化的作用
- 延迟计算。上面的例子已经比较好低说明了。
- 参数复用。当在多次调用同一个函数,并且传递的-参数绝大多数是相同的,那么该函数可能是一个很好的柯里化候选。
- 动态创建函数。这可以是在部分计算出结果后,在此基础上动态生成新的函数处理后面的业务,这样省略了重复计算。或者可以通过将要传入调用函数的参数子集,部分应用到函数中,从而动态创造出一个新函数,这个新函数保存了重复传入的参数(以后不必每次都传)。
栗子2
const 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)
})
}
}
每次添加事件处理都要执行一遍
if...else...
,其实在一个浏览器中只要一次判定就可以了,把根据一次判定之后的结果动态生成新的函数,以后就不必重新计算。
const addEvent = (function () {
if (window.addEventListener) {
return function (el, sType, fn, capture) {
el.addEventListener(sType, function (e) {
fn.call(el, e)
}, (capture))
}
} else if (window.attachEvent) {
return function (el, sType, fn, capture) {
el.attachEvent("on" + sType, function (e) {
fn.call(el, e)
})
}
}
})()
反柯里化(uncurring)
反柯里化的作用是,当我们调用某个方法,不用考虑这个对象在被设计时,是否拥有这个方法,只要这个方法适用于它,我们就可以对这个对象使用它。
Function.prototype.uncurring = function () {
const self = this
return function () {
const obj = Array.prototype.shift.call(arguments)
return self.apply(obj, arguments)
}
}
const push = Array.prototype.push.uncurring();
//测试一下
(function () {
push(arguments, 4);
console.log(arguments); //[1, 2, 3, 4]
参考文章
浅析 JavaScript 中的 函数 uncurrying 反柯里化
浅析 JavaScript 中的 函数 uncurrying 柯里化
简单理解JavaScript中的柯里化和反柯里化
网友评论