weakmap 实现真正私有属性的关键是模块顶层声明、构造函数中单次初始化、避免反向引用及封装访问器。它使数据依附实例生命周期,外部不可访问或删除,且自动随实例回收。

用 WeakMap 配合私有属性,不是加一层“锁”,而是让数据真正依附于实例而生、随实例而灭——外部既摸不到,也删不掉,更不会拖慢回收。关键不在语法炫技,而在结构设计。
WeakMap 必须藏在模块顶层,不能暴露
私有性第一道防线是作用域隔离:WeakMap 实例必须声明在类外部、模块顶层(或立即执行函数内),绝不能写在 class 内部、构造函数里,更不能导出或挂到全局。
- ✅ 正确:const privateData = new WeakMap() 放在文件最上方,所有类方法通过闭包访问它
- ❌ 错误:在 constructor 里 new WeakMap() → 每个实例都配一个 Map,彻底失效
- ❌ 错误:export const store = new WeakMap() → 外部可 set/get,私有变成公开
每个实例只初始化一次私有数据对象
构造函数中调用 set(this, {}) 是唯一且必需的初始化时机。后续所有读写都基于 get(this) 取出的对象操作,绝不重复 set —— 否则会覆盖状态,导致计数归零、配置丢失等静默故障。
- 初始化时存完整结构:privateData.set(this, { _token: '', _retryCount: 0, _isInitialized: false })
- 读取前做存在性检查:const data = privateData.get(this); if (!data) throw new Error('Not initialized')
- 避免返回私有对象引用:getConfig() 应返回 Object.assign({}, data.config),防止外部直接改内部字段
绑定生命周期,让数据自动消失
WeakMap 的弱引用机制,是内存安全的核心。只要实例不再被任何变量、闭包、事件监听器强持有,它就会被 GC 回收,对应条目同步从 WeakMap 中清除——无需手动 delete,也不怕遗忘。
- 适合场景:Web Components、React 自定义 Hook 封装、第三方库不可变对象元数据绑定
- 注意反向引用陷阱:私有数据里不要存 this 或其他强引用,否则会阻止实例回收
- 验证是否生效:Chrome DevTools → Memory tab → Take heap snapshot,搜索类名,确认无残留实例
配合 getter/setter 隐藏访问细节
把 get(this) 和字段读写封装进访问器,既能保持调用简洁(如 instance.value),又能集中处理防御逻辑、类型校验和副作用。
- getter 中用空值合并:return privateData.get(this)?.value ?? 0
- setter 中做类型守卫:if (typeof v !== 'number') throw new TypeError('value must be number')
- 支持链式调用:increment() { this.value += 1; return this; }











