← 返回题目列表

Symbol 有什么用?常见内置 Symbol 有哪些?

高频 中等 第 17 / 28 题 更新于 2026/07/27
ES6Symbol迭代协议

简化版

Symbol 是 ES6 新增的原始类型,用来创建唯一值,常用于避免对象属性名冲突。内置 Symbol 如 Symbol.iteratorSymbol.toStringTag 可以定制对象的语言行为。

详细版

基本用法:

const key = Symbol('id');
const obj = {
  [key]: 123,
};

每个 Symbol 都是唯一的:

Symbol('a') === Symbol('a'); // false

常见内置 Symbol:

  • Symbol.iterator:定义对象如何被迭代。
  • Symbol.toStringTag:定制 Object.prototype.toString 结果。
  • Symbol.hasInstance:定制 instanceof 行为。

Symbol 属性不会被普通 for...inObject.keys 枚举到,但可以用 Object.getOwnPropertySymbols 获取。

完整版教学

一、Symbol 解决属性名冲突

如果多个库都往同一个对象上挂属性,字符串 key 可能冲突。Symbol 每次创建都唯一,可以降低冲突概率。

const PRIVATE = Symbol('private');
obj[PRIVATE] = 'secret';

它不是绝对私有,只是不容易被普通枚举拿到。

二、全局 Symbol 注册表

Symbol.for() 会在全局注册表里查找或创建 Symbol:

Symbol.for('app') === Symbol.for('app'); // true

它适合跨模块共享同一个 Symbol。普通 Symbol() 每次都是新的。

三、Symbol.iterator 和可迭代对象

对象实现 Symbol.iterator 后,就可以被 for...of、展开运算符消费:

const range = {
  *[Symbol.iterator]() {
    yield 1;
    yield 2;
  },
};

这说明 Symbol 不只是唯一 key,还参与了 JS 的协议设计。

四、Symbol 属性的枚举特点

Symbol key 不会出现在 Object.keys 和 JSON 序列化中。如果要获取:

Object.getOwnPropertySymbols(obj);
Reflect.ownKeys(obj);

Reflect.ownKeys 会同时拿到字符串 key 和 Symbol key。

五、面试追问与工程落地

Symbol 常见追问是“Symbol 能不能保证私有”。它不能保证真正私有,因为可以通过 Object.getOwnPropertySymbolsReflect.ownKeys 拿到 Symbol 属性。它更多是避免命名冲突和普通枚举,不是安全边界。

还会问 Symbol.iterator 为什么重要。它定义了对象如何被 for...of、展开运算符、Array.from 消费。数组、字符串、Map、Set 都实现了这个协议,所以它们能被统一迭代。

工程里 Symbol 适合做库内部扩展点或元信息 key,减少和用户字段冲突。但业务数据字段一般不要用 Symbol,因为 JSON 序列化、接口传输、调试展示都不方便。

六、从唯一性、注册表和协议三个层次理解

每次调用 Symbol('id') 都创建新的 Symbol,描述字符串只用于调试,不参与相等判断;Symbol.for('id') 则查询全局 Symbol 注册表,相同键会取得同一个 Symbol。反向查询仅适用于注册表中的 Symbol,可用 Symbol.keyFor 获得注册键。

const localA = Symbol('id')
const localB = Symbol('id')
const sharedA = Symbol.for('id')
const sharedB = Symbol.for('id')
console.log(localA === localB)   // false
console.log(sharedA === sharedB) // true
console.log(Symbol.keyFor(localA)) // undefined
创建方式相同描述或键是否相等Symbol.keyFor
Symbol('x')undefined
Symbol.for('x')"x"
Symbol.iterator固定的内建 Symbol不属于注册表

Well-known Symbols 是语言预留的扩展协议。例如对象通过 [Symbol.iterator] 接入迭代协议,通过 [Symbol.toPrimitive] 控制原始值转换;它们的价值不是“隐藏字段”,而是避免协议名称与普通业务属性冲突。

枚举时要区分 API:Object.keysfor...inJSON.stringify 不处理 Symbol 键,Object.getOwnPropertySymbols 可单独取得,Reflect.ownKeys 则同时返回字符串键与 Symbol 键。假设对象有 2 个字符串键和 1 个 Symbol 键,Object.keys 长度是 2,而 Reflect.ownKeys 长度是 3。

Symbol 属性只是较不容易意外碰撞或被常规枚举发现,并不提供安全边界;拿到对象的人仍可通过反射 API 读取它们。

七、常见误区与追问

  • 误区:相同描述的 Symbol 一定相等。 Symbol('x') 每次都创建唯一值,描述不是身份标识。
  • 误区:Symbol 属性是真正私有属性。 Object.getOwnPropertySymbolsReflect.ownKeys 都能发现它,私有字段应使用 #field
  • 误区:Symbol 键不会被任何复制操作带走。 对象展开和 Object.assign 会复制自有可枚举 Symbol 属性。
  • 追问:Symbol.for 适合解决什么问题? 它让约定相同注册键的代码取得同一个 Symbol,适合跨模块共享标识。
  • 追问:为什么不能 new Symbol? Symbol 是原始值工厂,不是可构造函数,配合 new 会抛出 TypeError
  • 追问:JSON.stringify 如何处理 Symbol? 对象的 Symbol 键会被忽略,数组中的 Symbol 值通常序列化为 null,对象属性值为 Symbol 时该属性被忽略。
  • 追问:well-known Symbol 和 Symbol.for 有什么关系? 前者由语言规范定义并驱动内建协议,后者来自可按字符串键查询的全局注册表,两者用途不同。

八、加强记忆

Symbol 记成“唯一标识符”。普通 Symbol 每次不同,Symbol.for 可共享;它既能防属性冲突,也能通过内置 Symbol 接入迭代等语言协议。

最后用唯一 Symbol、注册表 Symbol、well-known Symbol 三类分别回答身份共享、跨模块约定和语言协议问题。