理解 ES6 符号
Understanding ES6 Symbols
当谈到语言时,我一直处于困境,使用过从 C# 到 Lisp 到 Scala 再到 Haskell 的所有语言,并且在支持它们的每种语言中,符号的行为几乎相同;也就是说,任何两个具有相同名称的符号都保证是相同的,因为它们是单例对象。
球拍:(equal? 'foo 'foo)
正确
Common Lisp:(eq 'foo 'foo)
正确
Ruby: :foo == :foo
正确
斯卡拉:'foo == 'foo
正确
ES6: Symbol('foo') === Symbol('foo')
假
符号作为单例的好处是显而易见的:您可以在 maps/dictionaries 中使用它们,而不会冒您的密钥不等于您的输入的风险,因为语言突然决定以不同的方式散列它(看着您,Ruby)
那么为什么 ECMAScript 6 对此采取了不同的方法,我该如何解决呢?
您可以(某种程度上)通过使用已注册的(全局)符号来按名称获得符号 "knowable" 的效果:
var s1 = Symbol.for("foo");
var s2 = Symbol.for("foo");
s1 === s2; // true
当然,您也可以使用 Map 实例或普通对象创建自己的 Symbol 注册表。
edit — 我要补充一点,在创建新的 Symbol 实例时,可选字符串参数的目的是提供一种识别符号含义和用途的方法 给程序员。如果没有该字符串,Symbol 可以像 Symbol 一样正常工作,但是如果您在调试器中转储对象,则由此类匿名 Symbol 实例键入的属性只是值。如果您使用 Symbol 键在对象上保留数字属性,那么您只会看到一些数字,这会造成混淆。与 Symbol 实例关联的描述字符串为程序员提供了参考信息,而不会影响 Symbol 作为键值的唯一性。
最后,您始终可以比较在两个类似构造的 Symbol 实例上调用 .toString()
的结果。我怀疑这种做法会被认为是有问题的,但你当然可以做到。
编辑更多 — 我突然想到 JavaScript 中符号创建的 默认 行为使得输入 比 Erlang 中的原子或 Clojure 中的键更有用。因为该语言默认提供一个保证唯一的值,所以命名空间冲突的基本问题得到了很好的解决。仍然可以使用 "well-known" 符号,但是拥有可用的唯一值而不必担心与可能也希望避免冲突的其他代码发生冲突是很好的。 JavaScript 有一些独特的,当然是独特的普遍和不可控制的全局命名空间问题,它可能被程序员甚至不知道存在的代码污染,因为代码可能由于以下行为而在浏览器环境中发生冲突与任意数量的软件架构师不同且未知的一方。
当谈到语言时,我一直处于困境,使用过从 C# 到 Lisp 到 Scala 再到 Haskell 的所有语言,并且在支持它们的每种语言中,符号的行为几乎相同;也就是说,任何两个具有相同名称的符号都保证是相同的,因为它们是单例对象。
球拍:(equal? 'foo 'foo)
正确
Common Lisp:(eq 'foo 'foo)
正确
Ruby: :foo == :foo
正确
斯卡拉:'foo == 'foo
正确
ES6: Symbol('foo') === Symbol('foo')
假
符号作为单例的好处是显而易见的:您可以在 maps/dictionaries 中使用它们,而不会冒您的密钥不等于您的输入的风险,因为语言突然决定以不同的方式散列它(看着您,Ruby)
那么为什么 ECMAScript 6 对此采取了不同的方法,我该如何解决呢?
您可以(某种程度上)通过使用已注册的(全局)符号来按名称获得符号 "knowable" 的效果:
var s1 = Symbol.for("foo");
var s2 = Symbol.for("foo");
s1 === s2; // true
当然,您也可以使用 Map 实例或普通对象创建自己的 Symbol 注册表。
edit — 我要补充一点,在创建新的 Symbol 实例时,可选字符串参数的目的是提供一种识别符号含义和用途的方法 给程序员。如果没有该字符串,Symbol 可以像 Symbol 一样正常工作,但是如果您在调试器中转储对象,则由此类匿名 Symbol 实例键入的属性只是值。如果您使用 Symbol 键在对象上保留数字属性,那么您只会看到一些数字,这会造成混淆。与 Symbol 实例关联的描述字符串为程序员提供了参考信息,而不会影响 Symbol 作为键值的唯一性。
最后,您始终可以比较在两个类似构造的 Symbol 实例上调用 .toString()
的结果。我怀疑这种做法会被认为是有问题的,但你当然可以做到。
编辑更多 — 我突然想到 JavaScript 中符号创建的 默认 行为使得输入 比 Erlang 中的原子或 Clojure 中的键更有用。因为该语言默认提供一个保证唯一的值,所以命名空间冲突的基本问题得到了很好的解决。仍然可以使用 "well-known" 符号,但是拥有可用的唯一值而不必担心与可能也希望避免冲突的其他代码发生冲突是很好的。 JavaScript 有一些独特的,当然是独特的普遍和不可控制的全局命名空间问题,它可能被程序员甚至不知道存在的代码污染,因为代码可能由于以下行为而在浏览器环境中发生冲突与任意数量的软件架构师不同且未知的一方。