Symbol 是 JavaScript 原始类型,用于创建唯一不可变值以解决属性名冲突;其天然唯一性、不可枚举性及内置 Symbol 的语言级行为支持,使其不可被字符串替代。

Symbol 是 JavaScript 中的原始类型,用来创建唯一、不可变的值,主要解决对象属性名冲突问题。
Symbol 为什么不能用字符串替代?
字符串作为对象键时容易意外覆盖或被枚举到,而 Symbol 天然唯一(即使描述相同),且默认不参与 for...in、Object.keys()、JSON.stringify() 等遍历和序列化操作。
-
Symbol('a') !== Symbol('a')—— 每次调用都生成新值 -
Symbol.for('a') === Symbol.for('a')—— 全局注册表可复用,但需主动使用 - 非全局 Symbol 不会被
Object.getOwnPropertyNames()或Reflect.ownKeys()漏掉,但需显式用Object.getOwnPropertySymbols()获取
Symbol 作为对象私有属性的典型用法
用 Symbol 声明“逻辑私有”字段,避免外部直接访问或误覆盖,尤其适合库作者封装内部状态。
const _id = Symbol('id');
const _name = Symbol('name');
class User {
constructor(id, name) {
this[_id] = id;
this[_name] = name;
}
getId() { return this[_id]; }
}
const u = new User(123, 'Alice');
console.log(u[_id]); // 123
console.log(Object.keys(u)); // [] —— 不暴露
console.log(Object.getOwnPropertySymbols(u)); // [Symbol(id), Symbol(name)]
- 注意:这不是真正私有(
u[Symbol('id')]仍可读,只是不会撞上) - 若需强隔离,应配合
#privateField(ES2022)或闭包 - 不要把
Symbol当作权限控制手段,它只提供命名隔离
内置 Symbol(如 Symbol.iterator)如何影响行为?
JavaScript 引擎通过识别特定名称的 Symbol 来启用语言级能力,比如迭代、类型转换、调试输出等。
立即学习“Java免费学习笔记(深入)”;
-
Symbol.iterator:使对象可被for...of遍历 -
Symbol.toStringTag:影响Object.prototype.toString.call(x)返回值 -
Symbol.hasInstance:自定义instanceof判定逻辑 -
Symbol.toPrimitive:控制对象转原始值(如 +x、== 比较时)
示例:让类支持 for...of
class Countdown {
constructor(n) { this.n = n; }
[Symbol.iterator]() {
return {
next: () => (this.n > 0 ? { value: this.n--, done: false } : { done: true })
};
}
}
for (const v of new Countdown(3)) console.log(v); // 3, 2, 1
Symbol 的坑与注意事项
实际写代码时最容易忽略的是 Symbol 的“不可发现性”带来的调试和兼容性代价。
- JSON 序列化会直接丢弃 Symbol 键及其值:
JSON.stringify({ [Symbol('x')]: 1 })→{} - Object.assign() 不会拷贝 Symbol 属性:
Object.assign({}, obj)只复制字符串键 - WeakMap 键必须是对象,但 Symbol 常被误以为能当弱引用键用 —— 实际不行,WeakMap 不接受原始值
- 服务端(如 Node.js)或跨 iframe 场景下,
Symbol.for()是唯一能共享 Symbol 的方式,但需确保 key 字符串全局唯一且可控
Symbol 不是银弹。该用 # 私有字段就别硬套 Symbol;该暴露的接口别藏在 Symbol 后面增加维护成本。











