标准答案

  1. Symbol 是一种原始类型,用 Symbol() 创建的值是唯一的。即使描述相同,两个 Symbol 也不相等。
  2. Symbol 常用作对象属性键,减少和普通字符串属性名冲突的风险,也可以保存不希望被常规枚举看到的内部字段。
  3. Symbol.for 会使用全局注册表。相同 key 会拿到同一个 Symbol,适合跨模块共享约定;Symbol() 每次都会创建全新的唯一值。
  4. 内置 Symbol 用来定制语言协议。Symbol.iterator 定义可迭代行为,Symbol.toStringTag 影响 Object.prototype.toString,Symbol.toPrimitive 定义对象转原始值,Symbol.hasInstance 可以定制 instanceof。

题目解析

Symbol 的价值有两层:一层是唯一属性键,另一层是语言协议入口。只说“唯一值”会漏掉 for...of、类型标签和类型转换这些常见用法。

Symbol 属性不是绝对私有。Object.keys、for...in、JSON.stringify 默认看不到 Symbol 键,但 Object.getOwnPropertySymbols 和 Reflect.ownKeys 可以拿到。

Symbol.for 适合全局共享标识,但它也意味着 key 会进入全局注册表。只需要局部唯一值时,用 Symbol() 更清楚。

代码示例

下面示例展示 Symbol 属性键、全局注册表和 Symbol.iterator 协议。

JavaScript
const localId = Symbol('id')
const anotherLocalId = Symbol('id')

localId === anotherLocalId // false

const sharedA = Symbol.for('app.user')
const sharedB = Symbol.for('app.user')
sharedA === sharedB // true

const user = {
  name: 'Alice',
  [localId]: 1001
}

Object.keys(user) // ['name']
Reflect.ownKeys(user) // ['name', Symbol(id)]

const range = {
  start: 1,
  end: 3,
  *[Symbol.iterator]() {
    for (let value = this.start; value <= this.end; value += 1) {
      yield value
    }
  }
}

[...range] // [1, 2, 3]

常见误区

  • 误以为 Symbol 属性完全私有。它只是默认枚举不容易看到。
  • 混淆 Symbol() 和 Symbol.for()。
  • 忘记 Symbol 是原始类型,不是对象。

作者信息