Symbol 是 ES6 新增的一种原始类型,表示独一无二的值。
基本特点
- 每次调用
Symbol()都会生成一个全新的值 - 即使传入相同的描述,两个
Symbol也不相等 - 常用来作为对象属性名,避免命名冲突
const s1 = Symbol()
const s2 = Symbol()
console.log(s1 === s2) // false
const a = Symbol("id")
const b = Symbol("id")
console.log(a === b) // false这里的 "id" 只是描述信息,方便调试,不会影响唯一性。
为什么要有 Symbol
对象的普通属性名通常是字符串,如果多人协作或者多个库同时往同一个对象上挂属性,就容易重名。
const obj = {
name: "mio"
}
obj.id = 1
obj.id = 2
console.log(obj.id) // 2如果用 Symbol 作为 key,就不会轻易和别的属性冲突。
const key1 = Symbol("id")
const key2 = Symbol("id")
const obj = {}
obj[key1] = 1
obj[key2] = 2
console.log(obj[key1]) // 1
console.log(obj[key2]) // 2作为对象属性名
const name = Symbol("name")
const user = {
[name]: "mio"
}
console.log(user[name]) // "mio"注意,定义 Symbol 属性时要用 []。
遍历时的特点
Symbol 类型的属性默认不会出现在一些常见遍历里:
const key = Symbol("secret")
const obj = {
name: "mio",
[key]: 123
}
console.log(Object.keys(obj)) // ["name"]
for (const k in obj) {
console.log(k) // name
}但它并不是“真正的私有属性”,只是普通遍历默认拿不到。
如果想拿到对象自身的 Symbol 属性,可以用:
Object.getOwnPropertySymbols(obj)
Reflect.ownKeys(obj)Symbol.for()
Symbol() 每次都会创建新值,而 Symbol.for() 会去全局注册表里查找。
const a = Symbol.for("token")
const b = Symbol.for("token")
console.log(a === b) // trueSymbol("x"):每次都是新的Symbol.for("x"):相同 key 会复用同一个 Symbol
常见用途
1. 防止对象属性名冲突
这是最常见的用途。
2. 给对象挂“临时方法”或“内部标记”
例如手写 call / apply / bind 时,通常需要临时把函数挂到目标对象上执行,这时就可以用 Symbol 避免覆盖原有属性。
const fnKey = Symbol("fn")
context[fnKey] = this
const result = context[fnKey](...args)
delete context[fnKey]这也是 Function_apply 里使用 Symbol 的原因。
3. 定义内置行为
JavaScript 里还有一些内置 Symbol,比如:
Symbol.iteratorSymbol.toStringTagSymbol.hasInstance
它们可以影响对象的一些底层行为。
在 Function_apply 里怎么用
手写 apply 时,一般思路是:
- 把当前函数临时挂到
context对象上 - 让
context调用这个函数,这样函数里的this就指向context - 调用结束后再删掉这个临时属性
问题在于:如果直接写成 context.fn = this,就可能覆盖掉对象原本的 fn 属性。
context.fn = this
const result = context.fn(...args)
delete context.fn这会有属性冲突风险。
所以更好的写法是:
const fnSymbol = Symbol("fn")
context[fnSymbol] = this
const result = context[fnSymbol](...args)
delete context[fnSymbol]因为 fnSymbol 是唯一的,几乎不可能和 context 上已有属性重名。
一句话总结
Symbol 的核心价值就是“制造一个不会撞名的唯一标识”,所以它特别适合做对象的隐藏键、内部标记和临时属性键。