Demystifying Symbols in Javascript

July 15, 2019 ☼ JS

The other day I was reading the source code of a LRU Cache implementation in Javascript and I noticed that the author was defining some variables using ES6 Symbols:

const LENGTH_CALCULATOR = Symbol('lengthCalculator')
const ALLOW_STALE = Symbol('allowStale')
const MAX_AGE = Symbol('maxAge')
const DISPOSE = Symbol('dispose')
const NO_DISPOSE_ON_SET = Symbol('noDisposeOnSet')

I’ve never actually used them in production code and I was curious to find out more about their use cases and advantages over simple strings like const LENGTH_CALCULATOR = 'lengthCalculator' for example.

What is a Symbol

The data type symbol is a primitive data type. Every symbol value returned from Symbol() is unique. A symbol value may be used as an identifier for object properties; this is the data type’s only purpose.

Source: MDN

Mmm… ok. What does it actually mean? Why should I use them?

Let’s break down the definition:

1) The data type symbol is a primitive data type.

A primitive data type” in JS is a type of data that is not an Object and doesn’t have any methods on its own. For example Number, String, and Boolean are primitives.

// VM186:1 Uncaught SyntaxError: Invalid or unexpected token

2) Every symbol value returned from Symbol() is unique.

Every time you create a new Symbol it will hold a unique value. Without the reference to that Symbol you won’t be able to use it and if you compare two Symbol created with the same description they will never be equal.

const MAX_AGE = Symbol('maxAge');
const MAX_AGE_2 = Symbol('maxAge'); 

// If we try to log them out they seem to be equal

console.log(MAX_AGE) // Symbol(maxAge)
console.log(MAX_AGE_2) // Symbol(maxAge)

// But...
MAX_AGE === MAX_AGE_2 // false

3) A symbol value may be used as an identifier for object properties

This is probably the most common use case for Symbols. They can be assigned as Object keys without worrying that these keys will never create any conflict with the normal String keys or other Symbols.

Another advantage is that they won’t come up when you iterate over an object with the following methods:

The only way to access them is to use Object.getOwnPropertySymbols.

const MAX_AGE = Symbol('maxAge');
const LRUCache = {};

LRUCache[MAX_AGE] = 0
console.log(LRUCache) // {Symbol(maxAge): 0}

// If I try to override the max age I simply can't

LRUCache['maxAge'] = 10;
console.log(LRUCache) // {maxAge: 10, Symbol(maxAge): 0}

Symbols allows you to create a hidden safe layer in your Objects.

Use cases

There are quite a few that I can think of:

const align = {
    LEFT: Symbol( 'LEFT' ),
    RIGHT: Symbol( 'RIGHT' ),
    UP: Symbol( 'UP' ),
    DOWN: Symbol( 'DOWN' ),

React actually uses them to prevent XSS attack (assuming you have an existing server hole).

  type: 'marquee',
  props: {
    bgcolor: '#ffa7c4',
    children: 'hi',
  key: null,
  ref: null,
  $$typeof: Symbol.for('react.element'),

Source: Why Do React Elements Have a $$typeof Property?

Final note

There are actually 3 types of Symbols:

If you have any suggestions, questions, corrections or if you want to add anything please DM or tweet me: @zanonnicola