Search code examples
javascriptecmascript-6symbols

Can I rely on the string representation of an ES6 `Symbol`?


I'm working on an ES6 app that sends some of its data over the network. Part of this involves identifiers that are implemented as ES6 Symbols. For example:

const FOO = Symbol('foo');

Calling Foo.toString() yields Symbol(foo). When I pass these over the network, I'd like to pass it as just foo. However, to my knowledge, there is no way to extract foo from Symbol(foo) other than to pull it out with a regular expression (specifically, /^Symbol\((.*)\)$/).

Should I rely on the regular expression always matching? Or is it possible that future updates to ES6 will break this? If I can't rely on the regex matching then I'll just send it over the wire as Symbol(foo).


Solution

  • According to the spec it’s always "Symbol(" + description + ")".

    Symbol.prototype.toString returns a string from an internal method call to SymbolDescriptiveString(sym):

    Let desc be sym’s [[Description]] value.
    If desc is undefined, let desc be the empty string.
    […]
    Return the string-concatenation of "Symbol(", desc, and ")".

    Now, updating this answer in 2019, you have two options:

    1. Use (or polyfill) Symbol.prototype.description, which is part of ECMAScript 2019 and is supported by all modern JavaScript engines:

      const foo = Symbol("foo"),
        bar = Symbol.for("bar"),
        iter = Symbol.iterator;
      
      console.log(foo.description); // "foo"
      console.log(bar.description); // "bar"
      console.log(Symbol.iterator.description); // "Symbol.iterator"

    2. Or use Symbol.prototype.toString like so:

      const foo = Symbol("foo"),
        bar = Symbol.for("bar"),
        iter = Symbol.iterator;
      
      console.log(foo.toString().slice(7, -1)); // "foo"
      console.log(bar.toString().slice(7, -1)); // "bar"
      console.log(iter.toString().slice(7, -1)); // "Symbol.iterator"
      
      // Or without “magic numbers”:
      console.log(foo.toString().slice("Symbol(".length, -")".length)); // "foo"
      console.log(bar.toString().slice("Symbol(".length, -")".length)); // "bar"
      console.log(iter.toString().slice("Symbol(".length, -")".length)); // "Symbol.iterator"

    Since the strings surrounding the description are fixed, slice is a good option to use, especially because a symbol description could itself contain parentheses, line breaks, the string "Symbol", etc, and the . in the regular expression won’t match line breaks for example.