闭包实现带状态校验的偏函数,本质是将谓词与处理器封装为私有上下文,调用时先校验再执行;其生命周期由引用可达性决定,非自动过期,内存是否回收取决于是否被全局或长生命周期对象持有。

闭包实现带内部状态校验的偏函数,本质是把“条件判断逻辑”和“处理逻辑”一起封进函数作用域;而标记清除算法是否回收该闭包及其捕获的变量,取决于它是否还被任何可达路径引用——也就是说,闭包不是自动“过期”的,它的生命周期由引用关系决定,不是由代码执行完就结束。
用闭包构造带状态校验的偏函数
这类偏函数不只固定部分参数,更关键的是在调用前主动验证输入是否满足预设条件(比如类型、范围、格式),只对合法输入响应,其余直接拒绝或忽略。这靠闭包天然支持:
- 外层函数接收谓词(predicate)和处理器(handler),两者被闭包捕获,形成私有上下文
- 返回的内层函数每次调用时先跑谓词,通过才执行 handler,否则可抛错、返回 undefined 或走 fallback
- 谓词本身可携带状态:比如记录已尝试次数、检查是否重复提交、比对缓存中的上一个值
示例:限制同一数值 5 秒内只能处理一次
const throttleByValue = (handler, interval = 5000) => {
const lastSeen = new Map();
return function(value) {
const now = Date.now();
const prev = lastSeen.get(value);
if (prev && now - prev throw new Error('Too frequent for this value');
}
lastSeen.set(value, now);
return handler(value);
};
};
这里 lastSeen 是闭包持有的内部状态,它让函数具备了“记忆+校验”能力,而不是无状态地转发参数。
标记清除算法如何判定闭包能否被回收
标记清除(Mark-and-Sweep)不看代码写了什么,只看运行时对象是否“可达”。闭包的存在会扩展 GC 的根可达集合:
- 全局变量、当前执行上下文、DOM 引用等是 GC 的“根”
- 只要一个闭包函数对象本身被根直接或间接引用(比如绑在 document 上、存进 window.cache、被定时器持有),它和它捕获的所有变量(包括大数组、DOM 节点、Map 实例)都会被标记为“存活”
- 即使外部函数早已执行完毕、AO(Activation Object)被销毁,只要闭包函数还活着,它封闭的自由变量就不会被清除
换句话说:闭包延长了变量的生命周期,不是因为它“特殊”,而是因为它把变量变成了“从根出发能走到的路径上的一环”。GC 不会主动推断“这个闭包其实不会再用了”,它只忠实地追踪引用链。
二者之间的物理关联:引用即寿命
闭包和标记清除之间没有抽象隔阂,是实实在在的内存引用关系:
- 你定义
const pf = partial(x => x > 100, fn),pf 这个函数对象持有一个闭包环境,里面存着谓词函数和 handler 函数的引用 - 如果 pf 被赋给
window.activeValidator,那它就从全局根可达 → 整个闭包环境不会被回收 - 如果 pf 只是临时传给某个 API 并立刻丢弃,且没被任何长生命周期对象持有,下一次 GC 就可能把它连同内部变量一并清理
- 所谓“闭包导致内存泄漏”,其实是开发者无意中让闭包长期挂在了可达路径上(如事件监听器未解绑、缓存未清理、定时器未清除)
所以优化方向很实在:控制谁持有闭包、何时释放引用、必要时主动置 null 断开强引用。GC 机制本身没问题,问题出在引用拓扑的设计上。










