WeakMap 是实现真正私有属性的可靠方式,因其以实例为 key、弱引用自动回收、不可枚举且无法外部访问。标准写法是将 WeakMap 声明在类外部作用域,通过 this 存取私有数据,避免 Symbol 等模拟方案的泄露风险。

WeakMap 是实现类中真正私有属性的可靠方式,它避免了属性名暴露、不被枚举、不可被外部访问,且能随实例自动回收——关键在于用实例本身作 key。
为什么 WeakMap 能实现“真正私有”
普通对象或 Map 存储私有数据时,key 是字符串(如 "_secret"),容易被反射手段(Object.keys、for...in、JSON.stringify)发现或意外覆盖;而 WeakMap 的 key 必须是对象,且仅支持对象引用作为 key,不接受字符串或原始值。更重要的是:
- WeakMap 的键是弱引用:当类实例被垃圾回收时,对应条目自动消失,无内存泄漏风险;
- WeakMap 的内容无法遍历,没有 keys()、values()、entries() 方法;
- 外部拿不到 WeakMap 实例(必须封闭在模块/作用域内),也无法通过实例反查私有数据。
标准写法:在类外部声明 WeakMap,内部闭包访问
私有数据容器不能挂在类上,也不能放在 constructor 里每次新建(否则失去复用性),正确做法是将 WeakMap 声明在类定义外的同一作用域:
const privateData = new WeakMap();
<p>class User {
constructor(name, email) {
// 用 this(实例)作 key,存一个普通对象容纳多个私有字段
privateData.set(this, { email, _token: null, _loginTime: Date.now() });</p><pre class='brush:php;toolbar:false;'>this.name = name; // 公有}
getEmail() { return privateData.get(this)?.email; }
setToken(token) { privateData.get(this)._token = token; }
getToken() { return privateData.get(this)?._token; } }
这样,privateData 不对外暴露,外部代码即使拿到 user 实例,也无法访问其私有字段——没有引用就查不到,查到了也仅限当前作用域内使用。
立即学习“Java免费学习笔记(深入)”;
进阶技巧:用 Symbol 模拟但不替代 WeakMap
有人用 Symbol 做私有 key(如 const _email = Symbol()),但它不是真正的私有:
- Object.getOwnPropertySymbols(obj) 可枚举所有 symbol key;
- Reflect.ownKeys(obj) 也能拿到;
- 一旦 symbol 泄露(比如导出、日志打印),私有性即失效。
WeakMap 不依赖 key 名,只认对象身份,因此更安全。Symbol 更适合做“约定私有”的公有属性命名防冲突,而非隐私保护。
注意事项与常见误区
- WeakMap 不能用
JSON.stringify(this)序列化私有数据——它根本不在实例属性中,这是优点也是限制;需手动定义toJSON或导出方法 - 子类继承时,只要子类构造函数里调用了
super(),父类 WeakMap 仍可正常存取(因为this是同一个实例对象) - 不要把 WeakMap 实例挂到类静态属性上并导出,否则破坏封装;应保持 WeakMap 在模块顶层、非导出变量
- TypeScript 中需配合
declare或private成员做类型提示,但运行时私有性仍靠 WeakMap 保障


















