symbol 通过创建唯一、不可枚举的属性键实现轻量级内部状态封装,支持 symbol() 实例隔离、symbol.for() 跨模块共享及 weakmap 彻底私有化,但仅防无意访问而非强安全隔离。

Symbol 本身不能真正“隐藏”属性,但它能创建**唯一、不可枚举、不被常规遍历捕获**的属性键,从而实现一种轻量级的“作用域隔离”或“内部状态封装”,常用于避免命名冲突和限制外部直接访问。
用 Symbol 作为私有属性键
通过 Symbol() 创建唯一符号,赋值给对象属性,该属性不会出现在 for...in、Object.keys()、Object.getOwnPropertyNames() 中,仅可通过 Object.getOwnPropertySymbols() 或明确知道 Symbol 的代码访问。
例如:
const _id = Symbol('id');
const _name = Symbol('name');
class User {
constructor(id, name) {
this[_id] = id;
this[_name] = name;
}
getId() { return this[_id]; }
getName() { return this[_name]; }
}
const u = new User(123, 'Alice');
console.log(u.id); // undefined
console.log(u[_id]); // 123(需显式使用 Symbol)
console.log(Object.keys(u)); // [](不暴露)
console.log(Object.getOwnPropertyNames(u)); // [](仍为空)
console.log(Object.getOwnPropertySymbols(u)); // [Symbol(id), Symbol(name)]
用 Symbol.for() 实现跨模块共享的“受控私有键”
若多个文件/模块需要协作访问同一内部属性(如框架插件机制),可用 Symbol.for(key) 注册全局符号。它保证相同字符串返回同一个 Symbol,但依然不参与常规枚举,比字符串键更安全。
- ✅ 同名复用:不同模块调用
Symbol.for('cache')得到同一 Symbol - ⚠️ 注意:不是完全私有——知道 key 就能访问,适合“约定式私有”场景(如库内部状态)
配合 WeakMap 实现真正的实例级私有数据
Symbol 键 + 对象属性仍有被反射获取的风险;更健壮的方式是用 WeakMap 存储私有数据,以实例为键,彻底隔绝外部访问路径:
const privateData = new WeakMap();
class BankAccount {
constructor(balance) {
privateData.set(this, { balance }); // 数据只存 WeakMap 中
}
getBalance() {
return privateData.get(this).balance;
}
deposit(amount) {
privateData.get(this).balance += amount;
}
}
const acc = new BankAccount(100);
console.log(privateData.get(acc)); // { balance: 100 }(但外部无法拿到 privateData 引用)
console.log(acc.balance); // undefined(无公开属性)
这种模式下,即使有人拿到 Symbol,也无法绕过 WeakMap 的封装边界。
注意事项与局限
- Symbol 属性仍可通过
Reflect.ownKeys()或Object.getOwnPropertySymbols()发现——它防的是“无意访问”,不是强安全隔离 - 调试时可在控制台输入
Object.getOwnPropertySymbols(obj)查看 Symbol 键 - JSON 序列化会自动忽略 Symbol 键,无需额外处理
- TypeScript 中需启用
lib: ["ES2015"]以上,并注意类型声明(如const _x: unique symbol = Symbol()提升类型安全性)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











