模块模式靠闭包实现私有化,本质是用函数作用域“锁住”变量,只让特定函数能访问,外部既无法读取(console.log(module.count)为undefined)、也无法遍历(for...in不可见),调试时仅chrome devtools的closure面板可见。

模块模式靠闭包实现私有化,本质是用函数作用域“锁住”变量,只让特定函数能碰它,外部连名字都查不到。
用 IIFE 划出私有边界
IIFE 是最直接的方式:把所有变量和辅助函数写在立即执行函数里,它们自动变成私有成员。返回的对象只暴露几个方法,这些方法因为处在同一个词法环境里,可以自由读写内部变量;但外部既不能 console.log(module.count),也不能 for...in 遍历出来。
- 私有变量声明在函数体内,比如
let data = []或const config = { timeout: 5000 } - 返回对象里的方法(如
add()、get())形成闭包,持续持有对私有变量的引用 - 初始化逻辑可放在 IIFE 开头,只运行一次,比如预加载资源或绑定事件监听
工厂函数支持多实例隔离
如果需要多个独立模块(比如不同用户的会话管理器),就别用单例 IIFE,改用工厂函数。每次调用都生成一套全新的闭包环境,各实例的私有状态完全不共享。
- 函数接收参数(如初始值、配置项),用于差异化初始化
- 每个实例拥有自己的一份
token、cache或counter - 避免把私有变量直接挂到返回对象上(如
{ _count: count }),那是快照值,不是实时访问通道
封装行为比只藏数据更重要
模块不只是存一个变量,还能把校验、日志、格式化等辅助逻辑也收进去。这些函数定义在闭包内,外部根本看不见,公有方法只负责调度流程。
- 例如银行账户模块中,
withdraw()内部调用私有的validateBalance()和updateLedger() - 敏感操作(如签名计算、数据脱敏)全在私有层完成,对外只暴露语义清晰的接口
- 调试时 Chrome DevTools 的 Closure 面板能看到这些变量,但生产环境没有调试入口,就自然隔开了
注意它的能力边界
闭包提供的不是内存级防护,而是运行时访问控制。它防误用,不防恶意攻击。
- 若模块暴露了
debugDump()这类接口,私有性就失效了 - ES2022 的
#field是语法级私有,但闭包在兼容旧环境、精细控制访问逻辑上仍有优势 - Node.js 中多次
require同一 IIFE 模块,仍共享同一实例——这不是设计缺陷,而是常见副作用











