必须用WeakMap而非Map或普通对象,因其键为弱引用,实例销毁后私有数据自动清除;闭包封装WeakMap防止外部访问,确保物理级隔离。

用闭包配合 WeakMap 实现类实例私有属性的硬隔离,核心在于:**外部既无法读取私有数据,也无法绕过方法访问它;同时,实例销毁后私有数据自动消失,不留痕迹**。这不是靠命名约定或运行时检查,而是靠作用域封闭 + 弱引用机制共同达成的“物理级隔离”。
为什么必须是 WeakMap 而不是 Map 或普通对象
WeakMap 是唯一能保证“实例一销毁、私有数据立刻清空”的结构:
- 普通对象或 Map 的键是强引用——哪怕实例已无任何外部引用,只要还在 Map 里,GC 就不会回收它,私有数据长期滞留内存
- WeakMap 的键只能是对象,且对键是弱引用:一旦实例对象本身被 GC 回收,对应条目自动从 WeakMap 中移除
- WeakMap 不可枚举、无 keys()/values() 方法,外部连“遍历看看有没有私有字段”都做不到
闭包的作用:封死 WeakMap 的访问入口
WeakMap 本身不提供私有性,它只是个容器。真正实现“不可访问”的是闭包:
- WeakMap 必须声明在类定义外层的函数作用域中(如模块顶层、IIFE 内),不能暴露给外部变量
- 所有类方法通过闭包捕获这个 WeakMap 实例,但外部代码拿不到引用,也就无法 set/get
- 如果把 WeakMap 放在 class 内部或导出为模块变量,就等于把钥匙交出去了
确保每个实例独占一份私有状态
私有数据必须按实例严格隔离,不能共享:
- WeakMap 实例要定义在类之外、但又不在全局作用域——推荐用立即执行函数包裹整个类定义
- 每个需要私有字段的类,单独配一个 WeakMap,不要多个类共用同一个
- 构造函数中必须调用 WeakMap.set(this, { … }) 初始化,漏掉会导致后续 get 返回 undefined
- 私有数据建议统一存为对象字面量,比如 { _id: 1, _token: 'abc' },便于扩展字段
防御性写法:避免 this 失效导致的访问失败
WeakMap.get(this) 返回 undefined,90% 是因为 this 指向错误,不是 WeakMap 有问题:
- 所有读取私有数据的方法开头加判断:if (!PrivateData.has(this)) throw new Error('Invalid instance')
- 避免直接解构方法:const getName = user.getName; 会丢失 this,应改用 user.getName.bind(user) 或 () => user.getName()
- 传给 setTimeout、addEventListener 等异步/回调场景时,显式绑定 this 或用箭头函数包装

















