symbol 是 javaScript 的原始类型,用于创建唯一不可变值;可避免属性名冲突、作为内置行为钩子(如 Symbol.iterator),但非私有字段替代品,且 jsON 序列化会忽略。

Symbol 是 javascript 中的原始类型,用来创建唯一、不可变的值
它不是“对象”,也不是“字符串别名”,而是一种全新的原始类型(和 String、number 并列)。每次调用 Symbol() 都返回一个**全新且不相等**的值,哪怕描述相同:
Symbol('foo') === Symbol('foo') // false
这种唯一性是核心价值,不是为了“看起来高级”,而是为了解决真实冲突问题。
为什么需要 Symbol:避免属性名碰撞
当多个库、模块或框架往同一个对象上挂方法或属性时,容易无意覆盖。比如两个第三方工具都加 cache 属性,就可能互相破坏。用 Symbol 可彻底隔离:
const cacheKey = Symbol('cache'); obj[cacheKey] = new Map(); // 不会被 obj.cache 或其他 Symbol('cache') 干扰
-
Symbol.for('cache')是全局注册表,同名返回同一 Symbol;普通Symbol('cache')每次都新造 - Symbol 属性不会被
for...in、Object.keys()、json.stringify()遍历或序列化 - 但可通过
Object.getOwnPropertySymbols()或Reflect.ownKeys()获取
Symbol 作为内置行为钩子:改变对象底层行为
JavaScript 引擎在特定操作(如遍历、转字符串)时,会查找对象上是否存在对应名称的 Symbol 方法。这是“魔法”的来源,不是语法糖:
-
obj[Symbol.iterator]决定for...of怎么遍历它 -
obj[Symbol.toStringTag]影响Object.prototype.toString.call(obj)的输出 -
obj[Symbol.hasInstance]控制instanceof的判定逻辑
这些不是可选插件,而是语言级协议——你实现它们,引擎才按你的规则运行。
常见误用和坑点
Symbol 不是私有字段的替代品:
- 它不能阻止访问(
obj[mySym]仍可读写),只是“隐形”于常规遍历 - 它不提供封装或访问控制,typescript 的
private或 JS 的#field才是真私有 - 用作对象键时,必须始终用方括号
obj[mySym],点号obj.mySym会找字符串键"mySym" - JSON 序列化会直接丢弃 Symbol 键及对应值,不要指望它过网络传过去
真正该用 Symbol 的地方很明确:需要全局唯一键、或要定制语言内置行为协议。