闭包与模块系统互补:闭包封装私有状态防污染,模块定义边界控依赖;组合使用实现可测试契约,但需警惕闭包致内存泄漏。

闭包和模块系统在大型项目中不是对立关系,而是互补工具:闭包用于封装私有状态与行为,模块系统用于组织代码边界与依赖关系。真正解耦的关键,在于用闭包守住“内部不变”,用模块暴露“外部契约”。
用闭包隔离可变状态,避免全局污染
大型项目中,频繁共享或误改状态是耦合根源。闭包天然提供词法作用域保护,让变量不被外部直接访问。
- 把配置、缓存、计数器等易变数据放在闭包内,只通过函数接口读写
- 避免使用var声明全局变量,改用立即执行函数(IIFE)包裹初始化逻辑
- 例如:一个请求重试控制器,内部维护失败次数和退避时间,外部只能调用retry()或reset(),无法直接修改attemptCount
模块系统定义清晰边界,控制依赖流向
ES Module(import/export)或 CommonJS 提供静态/动态的依赖声明机制,强制开发者显式声明“我需要什么”和“我提供什么”。
- 每个模块只负责单一职责,导出的 API 应稳定、语义明确(如createApiClient()而非init())
- 避免深层嵌套导入(import ... from 'utils/deep/nested/helper'),通过入口模块统一 re-export
- 利用模块的顶层作用域特性,自然实现单例——同一模块多次import仍共享同一份闭包环境
组合使用:闭包 + 模块 = 可测试的私有实现
模块导出的是接口,闭包封装的是实现细节。这种组合让单元测试聚焦契约,而不受内部变化影响。
立即学习“Java免费学习笔记(深入)”;
- 模块内定义闭包工厂函数(如createLogger(level)),返回带私有上下文的对象
- 测试时只验证导出函数的行为(是否按 level 过滤日志),不关心内部用Map还是WeakMap存 session
- 重构闭包内部逻辑(比如替换缓存策略)无需改动模块导出签名,下游无感知
警惕闭包滥用导致的内存泄漏
闭包本身不是问题,但不当持有大对象或 DOM 引用会阻碍垃圾回收,尤其在模块长期存活的 SPA 中。
- 避免在事件监听器闭包中长期引用整个组件实例,改用弱引用或手动清理(removeEventListener)
- 定时器、WebSocket 回调中的闭包若引用了已卸载页面的元素,需在销毁阶段清除
- 模块级闭包中缓存数据应设上限并支持主动清理(如导出clearCache())


















