Symbol.for(string) in ECMAScript 6

2019-01-15 09:16发布

问题:

It took me a while but I finally figured out what the purpose of symbols in ECMAScript 6 is: avoiding name collision when attaching properties to shared objects - HTML elements e.g. (In case you're stuck on the same question, I recommend this article.)

But then I stumbled upon Symbol.for(). Apparently ECMAScript 6 will maintain a global symbol registry which you can query with this function by providing the symbol description. Come again? If I use symbols to avoid name collisions, why would I want code other than my own to use them? (*) And how would I avoid name collisions in that global registry? Sharing of symbols seems to completely subvert the concept and a global registry doubly so.

(*) Yes, I know symbols aren't truly private, but that's besides the point.

回答1:

If you don't want your symbols to be available in GlobalSymbolRegistry, just don't use Symbol.for.

Only use it if you want to allow other codes to use your symbol.

In the following example, I create a symbol to store data in DOM elements. And I may want every other code (e.g. internal raw uncompiled handlers) to read that data. So I make the symbol globally available.

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

It's like creating global variables: should be avoided in general, but has its advantages. But with symbols instead of strings.



回答2:

If I use symbols to avoid name collisions, why would I want code other than my own to use them?

That's not the only use case of symbols. The two most important other ones are:

  • they don't collide with string-keyed properties
  • they are not enumerated by the usual mechanics

Sharing of symbols seems to completely subvert the concept and a global registry doubly so.

Not necessarily. Right from that article you read: "The registry is useful when multiple web pages, or multiple modules within the same web page, need to share a symbol." The best example for these are the intrinsic symbols - they guarantee interoperability across realms, that's why the global symbol registry is more global than your global scope.

For example you might have a library that is loaded in a web page, an iframe and a web worker. If you share data between those environments (realms), all of the three instances of your library would want to use the same symbol.

There also is a real need interoperability between different libraries, which might not even know about each other. Good examples are transducers, algebraic structures or promises. Would ES6 already be in use, all of these would have agreed on common names in the global symbol registry, instead of relying on strings like these or the then method.

Another good example would be custom hooks defined by your engine, e.g. a Symbol.inspect = Symbol.for("inspect") that you can use to define custom stringification behavior to be used by console.log. Admittedly, that symbol does not necessarily need to be made available through the global symbol registry, it could as well be put on that specific library object (e.g. console.inspect = Symbole("console.inspect")).

And how would I avoid name collisions in that global registry?

Just like you previously did with properties, or global module objects: by using very long, very descriptive names - or by good faith. Also there are some naming conventions.



回答3:

I invented the most useful feature of Symbol.for() call. If there is using symbols in your code sometimes it is difficult to use conditional breakpoints while debugging. For example, you need to catch if the variable equals the value which is of symbol type and this value binded in the different module. The first difficult way is to use this value as a constant and export it from that module. In this case, the condition of the breakpoint will look:

catchedVariable === exportedSymbolConst

But the easiest way is to temporarily change the code inside the module adding .for to Symbol. Then you can write the condition:

catchedVariable === Symbol.for('string_key')

After the successful debugging you will be changing the code back just removing .for part.