ES6 Symbols and their Use Cases


This article is a section from the course ES6 in Practice. I created this course during the last couple of months, because there is an evident need for a resource that helps JavaScript developers put theory into practice.

This course won’t waste your time with long hours of video, or long pages of theory that you will never use in practice. We will instead focus on learning just enough theory to solve some exercises. Once you are done with the exercises, you can check the reference solutions to deepen your understanding. In fact, you will sometimes get a chance to discover some theoretical concepts by solving an exercise, then reading about the concept in the reference solution.

If you like this article, check out the course here.

Symbols: a new ES6 primitive type and its use cases

ES6 introduces a new primitive type for JavaScript: Symbols. A JavaScript symbol is created by the global Symbol() function. Each time the Symbol() function is called, a new unique symbol is returned.

Symbols don’t have a literal value. All you should know about the value of a symbol is that each symbol is treated as a unique value. In other words, no two symbols are equal.

Symbol is a new type in JavaScript.

Symbols are useful, because they act as unique object keys.

When console logging myObject, you can see that both symbol properties are stored in the object. The literal "Symbol()" is the return value of the toString() method called on the symbol. This value denotes the presence of a symbol key in the console. We can retrieve the corresponding values if we have access to the right symbol.

Properties with a symbol key don’t appear in the JSON representation of your object. Not even the for-in loop or Object.keys can enumerate them:

Even though properties with Symbol keys don’t appear in the above cases, these properties are not fully private in a strict sense. Object.getOwnPropertySymbols provides a way to retrieve the symbol keys of your objects:

If you choose to represent private variables with Symbol keys, make sure you don’t use Object.getOwnPropertySymbols to retrieve properties that are intended to be private. In this case, the only use cases for Object.getOwnPropertySymbols are testing and debugging.

As long as you respect the above rule, your object keys will be private from the perspective of developing your code. In practice however, be aware that others will be able to access your private values.

Even though symbol keys are not enumerated by for...of, the spread operator, or Object.keys, they still make it to shallow copies of our objects:

Naming your symbols properly is essential in indicating what your symbol is used for. If you need additional semantic guidance, it is also possible to attach a description to your symbol. The description of the symbol appears in the string value of the symbol.

Always provide a description for your symbols, and make your descriptions unique. If you use symbols for accessing private properties, treat their descriptions as if they were variable names.

Even if you pass the same description to two symbols, their value will still differ. Knowing the description does not make it possible for you to create the same symbol.

Global symbol registry

ES6 has a global resource for creating symbols: the symbol registry. The symbol registry provides us with a one-to-one relationship between strings and symbols. The registry returns symbols using Symbol.for( key ).

Symbol.for( key1 ) === Symbol.for( key2 ) whenever key1 === key2. This correspondance works even across service workers and iframes.

As there is a one-to-one correspondence between symbol values and their string keys in the symbol registry, it is also possible to retrieve the string key. Use the Symbol.keyFor method.

Symbols as semi-private property keys

Creating truly private properties and operations is feasible, but it’s not an obvious task in JavaScript. If it was as obvious as in Java, blog posts like this, this, this, this, and many more wouldn’t have emerged.

Check out Exercise 2 at the bottom of this article to find out more about how to simulate private variables in JavaScript to decide whether it’s worth for you.

Even though Symbols do not make attributes private, they can be used as a notation for private properties. You can use symbols to separate the enumeration of public and private properties, and the notation also makes it clear.

As long as you can hide the _width constant, you should be fine. One option to hide _width is to create a closure:

The advantage of this approach is that it becomes intentionally harder to access the private _width value of our objects. It is also evident which of our properties are intended to be public, an which are intended to be private. The solution is not bulletproof, but some developers do use this approach in favor of indicating privacy by starting a variable with underscore.

The drawbacks are also obvious:

  • By calling Object.getOwnPropertySymbols, we can get access to the symbol keys. Therefore, private fields are not truly private
  • developer experience is also worse, as you have to write more code. Accessing private properties is not as convenient as in Java or TypeScript for example

Some developers will express their opinion on using symbols for indicating privacy. In practice, your team has the freedom of deciding which practices to stick to, and which rules to follow. If you agree on using symbols as private keys, it is a working solution, as long as you don’t start writing workarounds to publicly access private field values.

If you use symbols to denote private fields, you have done your best to indicate that a property is not to be accessed publicly. When someone writes code violating this common sense intention, they should bear the consequences.

There are various methods for structuring your code such that you indicate that some of your variables are private in JavaScript. None of them looks as elegant as a private access modifier.

If you want true privacy, you can achieve it even without using ES6. Exercise 2 deals with this topic. Try to solve it, or read the reference solution.

The question is not whether it is possible to simulate private fields in JavaScript. The real question is whether you want to simulate them or not. Once you figure out that you don’t need truly private fields for development, you can agree whether you use symbols, weak maps (see later), closures, or a simple underscore prefix in front of your variables.

Creating enum types

Enums allow you to define constants with semantic names and unique values. Given that the values of symbols are different, they make excellent values for enumerated types.

Avoiding name clashes

When using symbols as identifiers for objects, we don’t have to set up a global registry of available identifiers. We also save creation of a new identifier, as all we need to do is create a Symbol().

Same holds for external libraries.

Well known symbols

There are some well known symbols defined to access and modify internal JavaScript behavior. You can do magic such as redefining built-in methods, operators, and loops.

It is cool to apply hacks to the language, but ask yourself, is this skill going to move you forward in your career?

We will not focus on well known symbols in this section. If there is a valid use case for it, I will signal it in the corresponding lesson. Otherwise, I suggest staying away from manipulating the expected behavior of your code.

Exercises

Exercise 1. What are the pros and cons of using an underscore prefix for expressing our intention that a field is private? Compare this approach with symbols!

Solution:

Pros:

  • notation and developer experience is simple, provided that your team spreads this practice
  • it does not result in a hard-to-read code structure, all you need is one more character

Cons:

  • the properties are not private in practice, they are just denoted as private, which opens up a possibility of hacking quick and dirty solutions
  • unlike symbols, there is no clear separation between public and private properties. Private properties appear in the public interface of an object and they are enumerated in for..of loops, using the spread operator, and Object.keys

Exercise 2. Find a way to simulate truly private fields in JavaScript!

Solution:

When it comes to constructor functions, private members can be declared inside a constructor function using var, let, or const.

In order to use the same idea for classes, we have to place the method definitions that use private properties in the constructor method in a scope where the private properties are accessible. We will use Object.assign to accomplish this goal. This solution was inspired by an article I read on this topic by Dr. Axel Rauschmayer on Managing private data of ES6 classes.

The field privateProperty is not accessible in the c object.

The solution also works when we extend the C class.

For the sake of completeness, there are two other ways for creating private variables:

  • Weak maps: we will introduce it in a later section. We can achieve true privacy with it, at the expense of writing less elegant code,
  • TypeScript: introduces compile time checks whether our code treats private variables as private.

News on ES6 in Practice

I have decided on moving the course to LeanPub. For simplicity, you can download the book and the workbook as one file, which is a change compared to the original package.

I will continuously update the Leanpub package. If you purchased the course from another platform, before releasing the next update, I will get in touch with you with a dedicated 100% off survey.

Talking about updates, expect an update on December 30th. There will also be a price increase on January 1st. The update on December 30th contains the following changes:

  • A revision of all existing chapters, exercises, and solutions
  • the Reflect API
  • Proxies
  • Extensions of the Math and Number objects
  • At least ten new exercises and solutions

Following the philosophy behind Leanpub, by purchasing ES6 in Practice, you will get all future updates free of charge.

Would you like to learn ES6?
Strengthen your JavaScript knowledge with marketable skills!
Get the Course "ES6 in Practice"!

Learn Marketable Skills.

Verify your knowledge with real world exercises.

Thank you for your subscription.
Please check your inbox to access the first lesson.
  • WebReflection

    There is a lot of misunderstanding resulting in FUD in this post.

    > “Even though symbol keys are not enumerable …”

    Symbols are enumerable by default. To crete non enumerable Symbols you need to explicitly use Object.defineProperty(target, sym1, {value});

    In that case Object.assign won’t pass them around.

    > “Creating truly private properties and operations is not an obvious task in JavaScript …”

    Indeed you failed there too. If you can use an outer class scope accessible variable like const _width = Symbol('width'); you can also have a const _private = new WeakMap(); and a class such:

    js
    const _private = new WeakMap();
    class Square {
    constructor( width ) {
    _private.set(_private, {width});
    }
    getWidth() {
    return _private.get(this).width;
    }
    // for read only properties though
    // you have better mechanisms
    // such defineProperty or getters
    }

    Creating enum types, Avoiding name clashes, and Well known symbols are the only part of this post that didn’t misunderstand the features so please, unless you really read specs and got it, don’t write things like “Never use Object.getOwnPropertySymbols in your code. ”

    There’s also Reflect.ownKeys but that’s another story you should probably investigate a part.

    Regards

    • WebReflection

      I also forgot Object.getOwnPropertyDescriptors(source) for shallow copies, that will pass around enumerable and non enumerable symbols so … like I’ve said, Symbol were never born to give us private properties: it’s a huge misunderstanding!

    • Thank you for your feedback. I interpreted three improvement suggestions, which have been implemented in the text. These are:

      1. Regarding the enumeration of Symbol keys:

      I intended to state the following:


      let person = {
      [Symbol('age')]: 22,
      name: 'Zsolt'
      }

      Object.keys( person )
      > ["name"]

      Symbol keys are not enumerated by Object.keys.

      I did not make the statement that Symbols are not enumerable in a *mathematical* sense. The original statement referred to Symbol keys of objects.

      You do have a point that even Symbol keys are enumerable, and they are also retrievable in a way suggested by both you and the article:


      Object.getOwnPropertySymbols( person )
      > [Symbol(age)]

      For this reason, the statement that “symbol keys are not enumerable” has to be rewritten, thank you for pointing it out, it was a clear inconsistency on my end.

      2. Original statement: “Creating truly private properties and operations is not an obvious task in JavaScript.”

      Your response: “Indeed you failed there too.” Then you detailed a way to create private variables.

      While I respect your opinion that you think that creating private variables is obvious in JavaScript, I reserve the right to interpret it as a non-obvious task. View the topic from a perspective of someone who learns about symbols or JavaScript for the first time. The statement was not about whether creating truly private fields is possible, but whether it is obvious. If it was as obvious as in Java for instance, blog posts like [this](http://www.crockford.com/javascript/private.html), [this](https://philipwalton.com/articles/implementing-private-and-protected-members-in-javascript/), [this](https://curiosity-driven.org/private-properties-in-javascript), [this](http://www.2ality.com/2016/01/private-data-classes.html), and many more wouldn’t have emerged.

      In the original text, there was a reference to an exercise to create private variables, and a similar solution was described. I have included the solutions of the exercises in the post, and I have also mentioned and linked to some external blog posts for further research. This will reduce FUD (fear, uncertainty, and doubt).

      3. Object.getOwnPropertySymbols: I will make my statement on its usage a personal recommendation and give it a context in which this recommendation applies.

      Obviously, Object.getOwnPropertySymbols is in the public API of JavaScript, it is not deprecated, therefore, if a developer finds it useful, it makes no sense in banning its use.

      However, on condition that you use Symbols for denoting private variables (intended privacy, not safe privacy), it makes perfect sense to apply this restriction in your code base, following an agreement with your development team.

      Whether or not using Symbol keys for denoting our intention to mark variables as private is a good idea, it’s beyond the scope of this article. It is not safe (see Reflect.ownKeys or Object.getOwnPropertySymbols), but it is a possible notation that could work with your team, just like starting your private variable names with _.

      The text of this article now reflects this opinion.

      • It’s funny that both of us wrote in Markdown, even while knowing that Discus doesn’t interpret it. šŸ™‚

        • WebReflection

          that’s because the best part of MD is that is human-readable friendly regardless it’s ignored by Discous that, honestly, should really start thinking about it …

      • WebReflection

        Once again, thinking Symbols are for private properties is a mistake. Until you’ll realise that, I don’t think we (meaning you and the ECMAScript standard) have any agreement or middle point.

        > Symbol keys are not enumerated by Object.keys

        That’s because Symbols, like most of everything else new in JS, don’t want to break backward (with old code) so that previous ES6/2015 era iteraction should not expose Symbols such for/in loops, and Object.keys.

        However, if you have s = Symbol() and Object.getOwnPropertyDescriptor({[s]:1}, s).enumerable the result is *true*, not false, because … drum rolls … Symbols *are* set as enumerable by default.

        That is why stuff born when Symbols were born too, like Object.assign, moves them around.

        Accordingly, if you suggest to not use Object.getOwnPropertySymbols and you think it should be deprecated, while it’s fresh new ES 5.1 stuff that won’t go anywhere and most likely “forever”, and you suggest to avoid it because it exposes your misinterpretation of “enumerable” accordingly to ECMAScript specifications, you should rather investigate and learn more about Symbols and realise if you use them as private fields for classes instances, these will be exposes all over the place via Object.assign unless you use Object.defineProperty(this, Symbol(), {value}) as previously mention.

        Moreover, the shallow copy allowed by latest specifications, which is

        Object.defineProperties(target, Object.getOwnpropertyDescriptors(source)) <== mind the plural

        will copy over Symbols all over again, either enumerable or not, so that once again, Symbols are *really* not for private properties.

        Did I mention Symbols were not born to set private properties?

        Wherever you learned that, please erase that memory, or at least, don't propagate this wrong misconceptions of a well defined standard.

        http://www.ecma-international.org/ecma-262/6.0/#sec-properties-of-symbol-instances

        Thank You

  • You could make an interactive version of this (excellent) article with the klipse plugin https://github.com/viebel/klipse.
    Look for example at this interactive article about destructuring http://untangled.io/in-depth-es6-destructuring-with-assembled-avengers/

    • Thank you, Viebel. I created a Trello card for myself, your repository looks like a tool I wanted to implement.