前端面试问到闭包的作用,一般会说模块化及变量封装以及延长局部变量生命周期。函数柯里化一般需要延长局部变量生命周期,就用到闭包。今天面试官问以下问题:
sum(1, 2) // 3
sum(1)(2) // 3
sum(1, 2)(3)(4)// 10
//怎样的柯里化能实现这个sum函数
开始我知道柯里化需要保存中间值,于是写出以下的代码。
function curry() {
let reduce = 0;
return function sum() {
let len = arguments.length;
for (let i = 0; i < len; i++) {
reduce += arguments[i];
}
return reduce;
}
}
let sum = curry()
console.log(sum(1, 2));// 3
sum(1)(2) // 3
sum(1, 2)(3)(4)// 10
运行结果:
结果显示这个函数不能循环调用。可是如果返回argument.callee那么能循环调用,可是不能知道每次调用完最终的值。结果是这题无解。
函数柯里化(function currying)。currying 的概念最早由俄国数学家 Moses
Schönfinkel 发明,而后由著名的数理逻辑学家 Haskell Curry 将其丰富和发展,currying 由此得名。
currying 又称部分求值。一个 currying 的函数首先会接受一些参数,接受了这些参数之后,该函数并不会立即求值,而是继续返回另外一个函数,刚才传入的参数在函数形成的闭包中被保存起来。待到函数被真正需要求值的时候,之前传入的所有参数都会被一次性用于求值。
var currying = function (fn) {
var args = [];
return function () {
if (arguments.length === 0) {
return fn.apply(this, args);
} else {
[].push.apply(args, arguments);
return arguments.callee;
}
};
};
var sum = (function () {
var reduce = 0;
return function () {
for (var i = 0, l = arguments.length; i < l; i++) {
reduce += arguments[i];
}
return reduce;
};
})();
var sum = currying(sum); // 转化成 currying 函数
sum(100,300)(400)(100); // 未真正求值
sum(200); // 未真正求值
sum(300); // 未真正求值
console.log(sum());// 求值并输出:1400
我们完成了一个 currying 函数的编写。当调用 sum()时,如果明确地带上了一些参数,表示此时并不进行真正的求值计算,而是把这些参数保存起来,此时让 sum 函数返回另外一个函数。只有当我们以不带参数的形式执行 sum()时,才利用前面保存的所有参数,真正开始进行求值计算。