JavaScript中的符号(Symbols)

发布时间 2023-08-25 07:18:50作者: 晓风晓浪

符号是在ES6中引入的一种原始类型,用作非字符串属性名。要理解符号,重要的是要知道在JavaScript中,基本类型Object是一个无序的属性集合,每个属性都有一个名称和一个值。属性名称通常(直到ES6之前)是字符串。然而,在ES6和之后的版本中,符号也可以用作属性名:

// 可用作属性名的字符串
let strname = "string name";
// 可用作属性名的符号
let symname = Symbol("propname");
//=> "string":strname是字符串
typeof strname
//=> "symbol":symname是符号
typeof symname

// 创建一个新对象
let o = {};
// 用字符串名称定义属性
o[strname] = 1;
// 用符号名称定义属性
o[symname] = 2;
//=> 1:访问具有字符串名称的属性
o[strname]
// => 2:访问具有符号名称的属性
o[symname]

符号没有字面量语法。要获取符号值,您需要调用Symbol()函数。该函数永远不会返回相同的值,即使每次都提供相同的参数。这意味着您可以安全地使用调用Symbol()的结果将新属性添加到对象中,而不必担心覆盖具有相同名称的现有属性。类似地,如果定义了一个符号属性并且不共享相应的符号,您可以确信代码的其他部分不会意外覆盖该属性。

(程序员的软技能:ke.qq.com/course/6034346)

在实践中,符号通常用作语言扩展的机制。ES6引入了for/of循环和可迭代对象的概念,这需要为类定义一种标准化的方式,使它们能够自身成为可迭代的。然而,将任何特定的字符串用作此迭代器方法的名称可能会与现有代码冲突。这就是符号的作用。Symbol.iterator是一个符号值,可以用作方法名以使对象成为可迭代的。

Symbol()函数可以选择接受一个字符串参数,并返回一个唯一的符号值。如果提供了字符串参数,调用返回的符号值上的toString()方法的结果将包含该字符串。然而,重要的是要注意,使用相同的字符串调用Symbol()两次会导致两个完全不同的符号值。

let s = Symbol("syn_x");
// => "Symbol(syn_x)"
s.toString()

符号值的唯一有趣方法是toString()。然而,还有两个与符号相关的函数值得了解。有时在使用符号时,您希望它们对您的代码保持私有,以确保您的属性不会与其他代码的属性冲突。但是在其他时候,您可能希望定义可以与其他代码共享的符号。例如,如果您定义了一个扩展并希望其他代码使用它,类似于前面提到的Symbol.iterator机制。

为了定义可以与其他代码共享的符号,JavaScript具有全局符号注册表。Symbol.for()函数接受一个字符串参数,并返回与该字符串关联的符号值。如果没有与字符串关联的符号,它将创建并返回一个新的符号;否则,它将返回现有的符号。换句话说,Symbol.for()Symbol()是完全不同的:Symbol()永远不会返回相同的值,而Symbol.for()在使用相同的字符串调用时总是返回相同的值。提供给Symbol.for()的字符串将出现在toString()的输出中(当返回符号值时)。此外,通过使用Symbol.keyFor()传递返回的符号,可以检索此字符串:

let s = Symbol.for("shared");
let t = Symbol.for("shared");
// => true
s === t
// => "Symbol(shared)"
s.toString()
// "shared"
Symbol.keyFor(t)

(程序员的软技能:ke.qq.com/course/6034346)