闭包/柯里化棘手问题求解:sum()空调用场景的函数实现
实现支持无末尾调用的柯里化sum函数
要实现这个需求,核心是利用JavaScript中函数作为对象可以重写toString/valueOf方法的特性——当alert这类操作需要获取函数的原始值时,会自动调用toString方法,我们可以在这里返回当前的累加总和;同时让函数本身支持链式调用,每次接收参数后返回一个新的累加函数(或更新自身状态)。
方案实现(纯函数版,无副作用)
function sum() { // 工厂函数,创建带累加值的函数 function createAccumulator(total) { // 定义累加函数 function accumulator(num) { // 每次调用返回新的累加器,携带更新后的总和 return createAccumulator(total + num); } // 重写toString,返回当前总和的字符串形式 accumulator.toString = function() { return String(total); }; return accumulator; } // 初始总和为0,返回第一个累加器 return createAccumulator(0); }
验证场景
const s = sum(); alert(s); // 弹出0 alert(s(1)); // 弹出1 alert(s(1)(2)); // 弹出3 alert(sum()(3)(4)(5)); // 弹出12
原理说明
- 柯里化逻辑:
createAccumulator是一个工厂函数,每次接收当前总和total,返回一个新的accumulator函数。当调用accumulator(num)时,会创建并返回一个携带total + num总和的新累加器,实现链式调用。 - 隐式类型转换:当
alert函数尝试输出accumulator时,会触发隐式类型转换,调用我们重写的toString方法,返回当前的累加总和,而非默认的函数代码字符串。 - 无副作用:每个累加器的总和都是独立维护的,修改一个累加器不会影响其他实例,比如
s本身始终保持初始总和0,而s(1)返回的是一个新的总和为1的累加器。
另一种简洁实现(共享状态版)
如果不需要严格的纯函数,也可以用闭包共享同一个状态,代码更轻便:
function sum() { let total = 0; function accumulator(num) { if (typeof num === 'number') { total += num; } return accumulator; } accumulator.toString = () => String(total); return accumulator; }
这种实现下,同一个累加器实例的状态会被修改,比如调用 s(1) 后,再alert s 会弹出1,而非初始的0。如果你的场景不需要保留初始状态,这个版本更简洁。
内容的提问来源于stack exchange,提问作者Roma Kim
相关产品推荐
相关产品推荐

