Symbol 解决什么问题?常见内置 Symbol 有什么用?
先理解原理和示例,再练习完整口述与追问。
回答要点
Symbol() 每次创建唯一值,适合做不会冲突的属性键。Symbol.for 按字符串在全局注册表复用。Symbol 不能被隐式转成字符串。内置 Symbol 用来定制语言协议,例如 iterator、toPrimitive、hasInstance、toStringTag。
原理拆解
普通字符串键会撞名。库往对象上挂元数据时,用 Symbol() 作键,外部 Object.keys 看不到,展开和 JSON.stringify 也会忽略 Symbol 键;真正要枚举时用 Object.getOwnPropertySymbols 或 Reflect.ownKeys。Symbol('id') === Symbol('id') 为 false,描述只用于调试。Symbol.for('id') 在全局注册表查找,同一描述返回同一个 Symbol,跨 realm 也能共享;它不适合做真正的私有字段,因为任何人用同一个描述都能取到。运行时私有用 #field,见宝典 50。
隐式转字符串会抛 TypeError,例如 `${sym}` 或 '' + sym。显式 String(sym) 和 sym.description 可以。obj[sym] = 1 则完全合法。
语言用一批众所周知的 Symbol 暴露钩子。Symbol.iterator 让对象支持 for...of,见宝典 2。Symbol.toPrimitive 决定对象转原始值时的结果,优先于 valueOf 和 toString。Symbol.hasInstance 改写 instanceof。Symbol.toStringTag 影响 Object.prototype.toString 的标签。Symbol.iterator 以外,这些钩子都是“定制语言行为”,不是业务数据。
示例与推演
const id = Symbol('id')
const user = { name: 'Ada', [id]: 7 }
console.log(Object.keys(user), JSON.stringify(user)) // ['name'] '{"name":"Ada"}'
console.log(user[id], Symbol.for('id') === Symbol.for('id')) // 7 true
const money = {
yuan: 12,
[Symbol.toPrimitive](hint) { return hint === 'string' ? `¥${this.yuan}` : this.yuan }
}
console.log(`${money}`, +money) // '¥12' 12
易错点
不要把 Symbol.for 当成私有。JSON.stringify 丢掉 Symbol 键,服务端收不到这些字段。给内置原型乱改 Symbol.iterator 会破坏 for...of。
参考回答可直接练习口述
Symbol() 每次都是新值,用来做不会和字符串键冲突的属性名,普通枚举和 JSON 序列化不会带上它。Symbol.for 会在全局注册表按描述复用,能跨窗口共享,也就不能当私有字段。Symbol 不能隐式拼进字符串。内置的 Symbol.iterator、toPrimitive、hasInstance、toStringTag 是语言留给对象的钩子,分别影响遍历、类型转换、instanceof 和 toString 标签。