Symbol.for() 是 Symbol 的静态方法,该方法会根据给定的键 key,来从运行时的 symbol 注册表中找到对应的 symbol,如果找到了,则返回它,否则,新建一个与该键关联的 symbol,并放入全局 symbol 注册表中。
Symbol.for(key);
和 Symbol() 不同的是,用 Symbol.for() 方法创建的 symbol 会被放入一个全局 symbol 注册表中。Symbol.for() 并不是每次都会创建一个新的 symbol,它会首先检查给定的 key 是否已经在注册表中了。假如是,则会直接返回上次存储的那个。否则,它会再新建一个。
因此 Symbol() 即使是传入相同的 key ,每次返回的都是新的 symbol 类型的值。而 Symbol.for() 传入相同的 key,返回的是同一个 symbol 类型的值:
使用 Symbol.for() 创建 symbol 类型的值时,为了防止命名冲突,比如与其他代码库的命名冲突,建议给传入 Symbol.for 方法的 key 加上前缀:
// packages/runtime-core/src/vnode.ts
export const Text: unique symbol = Symbol.for('v-txt')
export const Comment: unique symbol = Symbol.for('v-cmt')
export const Static: unique symbol = Symbol.for('v-stc')
上述代码摘自 Vue.js 3.5.5 版本
Symbol.for() 有个与之对应的兄弟方法 Symbol.keyFor()。
Symbol.keyFor() 方法用来获取全局 symbol 注册表中与某个 symbol 关联的 key 。因此可用该方法判断相关 symbol 值是否为用 Symbol.for() 创建的:
let a = Symbol("a")
let b = Symbol.for("a")
Symbol.keyFor(a) // undefined ,说明该 symbol 值不是用 Symbol.for 创建的
Symbol.keyFor(b) // 'a'
使用 Symbol.for() 方法创建的 symbol 类型的值会存储到全局的 symbol 注册表中,因此无法被垃圾回收,同时 Symbol.for() 方法创建的 symbol 类型的值不能作为 WeakMap、WeakSet、WeakRef 和 FinalizationRegistry 的 key 。
WeakMap 的示例:
WeakSet 的示例:
WeakRef 的示例:
FinalizationRegistry 的示例:
可以看到,使用 Symbol.for() 方法创建的 symbol 类型的值作为 WeakMap、WeakSet、WeakRef 和 FinalizationRegistry 的 key 时,都会报错。
但是 Symbol() 函数创建的 symbol 类型的值,可以作为 WeakMap、WeakSet、WeakRef 和 FinalizationRegistry 的 key ,因为使用 Symbol() 函数创建的 symbol 类型的值可以被垃圾回收。
WeakMap 的示例:
WeakSet 的示例:
WeakRef 的示例:
FinalizationRegistry 的示例:
总结
Symbol() 与 Symbol.for() 的区别在于:
-
Symbol()即使重复定义,也不会产生重复的 symbol 类型的值 -
Symbol.for()在创建 symbol 类型的值之前会先使用 key 在全局 symbol 注册表中查找是否存在 symbol 值,如果存在,则返回在全局 symbol 注册表中的 symbol 值。即Symbol.for()不会重复定义 symbol 值,如果重复定义,则返回之前定义过的 symbol 值。