ECMAScript 6中的Symbol.for(string)函数

13

我花了一些时间,最终明白ECMAScript 6中符号的目的是什么:在将属性附加到共享对象(如HTML元素)时避免名称冲突。(如果你也在困惑同样的问题,我推荐阅读这篇文章。)

但接着我发现了Symbol.for()。显然ECMAScript 6将维护一个全局符号注册表,可以通过此函数提供符号描述来查询该注册表。再说一遍?如果我使用符号来避免名称冲突,为什么我希望除我自己之外的代码也使用它们呢?(*) 而且我怎么避免在那个全局注册表中出现名称冲突呢?共享符号似乎完全颠覆了这个概念,而全局注册表更是如此。

(*) 是的,我知道符号并不是真正的私有,但这并不重要。


2
由于您的问题不太清楚,因此使用Symbol('some description');并不会在全局注册表中注册内容。只有当您通过Symbol.for显式请求全局注册的符号时才会注册。 - loganfsmyth
是的,谢谢您的澄清! - user1813759
这里作者回答了您的问题:https://hacks.mozilla.org/2015/06/es6-in-depth-symbols/comment-page-1/#comment-17848 - Bergi
那不算一个很好的例子。仅仅说明代码可能需要访问跨库和/或iframes的属性,不能说明为什么符号注册表是答案。 - user1813759
3个回答

12
如果您不希望您的符号在GlobalSymbolRegistry中可用,请不要使用Symbol.for。只有在想让其他代码使用您的符号时才使用它。
在以下示例中,我创建了一个符号来存储DOM元素中的数据。我可能希望每个其他代码(例如内部未编译处理器)都可以读取该数据。因此,我使该符号全局可用。

var sym = Symbol.for('storeDataInDOM');
document.querySelector('button')[sym] = 'Hello, world!';
<button onclick="alert(this[Symbol.for('storeDataInDOM')])">Click me</button>

这就像创建全局变量一样:通常应该避免,但也有其优势。但是使用符号而不是字符串。


问题实际上是:为什么我要共享我的符号? - user1813759
@PeterCoester 我添加了一个例子。但问题是:为什么不呢?ES6提供了一种方法来实现,如果您感兴趣,请使用它,否则可以忽略它。 - Oriol
但是共享符号不会产生符号被创建来解决的问题吗?你的例子并没有完全展示出共享符号的用例。 - user1813759
@PeterCoester 是的,存在冲突的风险。但是全局变量也有同样的问题,人们仍然使用它们。而且在我的例子中:你如何访问内部原始未编译处理程序中的符号?引入GlobalSymbolRegistry可以在不污染全局对象的情况下实现这一点。 - Oriol
嗯,是的,我现在可以看到很多用例了...符号可以像jQuery中的.data()函数一样使用,您可以通过这种方式附加和操作UI控件。谢谢!这很有帮助。我已经为您的答案点赞了。 - user1813759

6
如果我使用符号来避免名称冲突,为什么我想让除了我自己的代码之外的代码使用它们呢?
这不是符号的唯一用例。另外两个最重要的用例是:
- 它们不会与以字符串为键的属性发生冲突 - 它们不会被通常的机制枚举
共享符号似乎完全颠覆了概念,而全局注册表则更是如此。
不一定。正如您所阅读的那篇文章中所述:“当多个网页或同一网页中的多个模块需要共享符号时,注册表非常有用。” 这些的最佳示例是内在符号 - 它们保证了跨域的互操作性,这就是为什么全局符号注册表比您的全局范围更全局的原因。
例如,您可能在网页、iframe和Web Worker中加载了一个库。如果在这些环境(领域)之间共享数据,则您的库的所有三个实例都希望使用相同的符号。
还有不同库之间的实际互操作性需求,这些库甚至可能互相不知道。很好的例子是transducersalgebraic structurespromises。如果ES6已经在使用中,所有这些库都将在全局符号注册表中达成共识,而不是依赖于像thesethen方法这样的字符串。

另一个很好的例子是由引擎定义的自定义钩子,例如Symbol.inspect = Symbol.for("inspect"),您可以使用它来定义自定义字符串化行为以供console.log使用。不可否认,该符号不一定需要通过全局符号注册表提供,它也可以放在特定的库对象上(例如console.inspect = Symbole("console.inspect"))。

那我如何避免在全局注册表中出现名称冲突?

就像您之前对属性或全局模块对象所做的那样:使用非常长且非常描述性的名称-或者信任良好。此外还有一些命名约定


0

我发明了Symbol.for()调用的最有用的功能。如果您的代码中有使用符号,有时在调试时使用条件断点会很困难。例如,您需要捕获变量是否等于符号类型的值,并且该值绑定在不同的模块中。第一种困难的方法是将此值用作常量并从该模块导出它。在这种情况下,断点的条件将如下所示:

catchedVariable === exportedSymbolConst

但最简单的方法是在模块内暂时更改代码,在Symbol后面添加.for。然后你可以编写条件:
catchedVariable === Symbol.for('string_key')

在成功调试后,您只需删除.for部分即可将代码更改回来。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接