你不知道的 Symbol.for() 方法

222 阅读2分钟

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 类型的值:

pic51.png

使用 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 类型的值不能作为 WeakMapWeakSetWeakRefFinalizationRegistry 的 key 。

WeakMap 的示例:

pic52.png

WeakSet 的示例:

pic53.png

WeakRef 的示例:

pic54.png

FinalizationRegistry 的示例:

pic55.png

可以看到,使用 Symbol.for() 方法创建的 symbol 类型的值作为 WeakMapWeakSetWeakRefFinalizationRegistry 的 key 时,都会报错。

但是 Symbol() 函数创建的 symbol 类型的值,可以作为 WeakMapWeakSetWeakRefFinalizationRegistry 的 key ,因为使用 Symbol() 函数创建的 symbol 类型的值可以被垃圾回收。

WeakMap 的示例:

pic56.png

WeakSet 的示例:

pic57.png

WeakRef 的示例:

pic58.png

FinalizationRegistry 的示例:

pic59.png

总结

Symbol()Symbol.for() 的区别在于:

  • Symbol() 即使重复定义,也不会产生重复的 symbol 类型的值

  • Symbol.for() 在创建 symbol 类型的值之前会先使用 key 在全局 symbol 注册表中查找是否存在 symbol 值,如果存在,则返回在全局 symbol 注册表中的 symbol 值。即 Symbol.for() 不会重复定义 symbol 值,如果重复定义,则返回之前定义过的 symbol 值