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.