A Practical Guide to ES6 Symbols

A Practical Guide to ES6 Symbols

ES6 introduced symbols as a new primitive type to act as unique identifiers. Here's a look at their uses.

Originally published by Valeri Karpov at http://thecodebarbarian.com

JavaScript introduced symbols in ES6 as a way to prevent property name collisions. As an added bonus, symbols also provide a way to simulate private properties in 2015-2019 JavaScript.


The simplest way to create a symbol in JavaScript is to call the Symbol() function. The 2 key properties that makes symbols so special are:

  1. Symbols can be used as object keys. Only strings and symbols can be used as object keys.
  2. No two symbols are ever equal.
const symbol1 = Symbol();
const symbol2 = Symbol();

symbol1 === symbol2; // false

const obj = {}; obj[symbol1] = 'Hello'; obj[symbol2] = 'World';

obj[symbol1]; // 'Hello' obj[symbol2]; // 'World'

Although the Symbol() call makes it look like symbols are objects, symbols are actually a primitive type in JavaScript. Using Symbol as a constructor with new throws an error.

const symbol1 = Symbol();

typeof symbol1; // 'symbol' symbol1 instanceof Object; // false

// Throws "TypeError: Symbol is not a constructor" new Symbol();


The Symbol() function takes a single parameter, the string description. The symbol's description is only for debugging purposes - the description shows up in the symbol's toString(). However, two symbols with the same description are not equal.

const symbol1 = Symbol('my symbol');
const symbol2 = Symbol('my symbol');

symbol1 === symbol2; // false console.log(symbol1); // 'Symbol(my symbol)'

There is also a global symbol registry. Creating a symbol using Symbol.for() adds a symbol to a global registry, keyed by the symbol's description. In other words, if you create two symbols with the same description using Symbol.for(), the two symbols will be equal.

const symbol1 = Symbol.for('test');
const symbol2 = Symbol.for('test');

symbol1 === symbol2; // true console.log(symbol1); // 'Symbol(test)'

Generally speaking, you shouldn't use the global symbol registry unless you have a very good reason to, because you might run into naming collisions.

Name Collisions

The first built-in symbol in JavaScript was the Symbol.iterator symbol in ES6. An object that has a Symbol.iterator function is considered an iterable. That means you can use that object as the right hand side of a for/of loop.

const fibonacci = {
 [Symbol.iterator]: function*() {
   let a = 1;
   let b = 1;
   let temp;

   yield b;

   while (true) {      temp = a;      a = a + b;      b = temp;      yield b;    }  } };

// Prints every Fibonacci number less than 100 for (const x of fibonacci) {  if (x >= 100) {    break;  }  console.log(x); }

Why is Symbol.iterator a symbol rather than a string? Suppose instead of using Symbol.iterator, the iterable spec checked for the presence of a string property 'iterator'. Furthermore, suppose you had the below class that was meant to be an iterable.

class MyClass {
 constructor(obj) {
   Object.assign(this, obj);

 iterator() {    const keys = Object.keys(this);    let i = 0;    return (function*() {      if (i >= keys.length) {        return;      }      yield keys[i++];    })();  } }

Instances of MyClass will be iterables that allow you to iterate over the object's keys. But the above class also has a potential flaw. Suppose a malicious user were to pass an object with an iterator property to MyClass.

const obj = new MyClass({ iterator: 'not a function' });

If you were to use for/of with obj, JavaScript would throw TypeError: obj is not iterable. That's because the user-specified iterator function would overwrite the class' iterator property. This is a similar security issue to prototype pollution, where naively copying user data may cause issues with special properties like proto and constructor.

The key pattern here is that symbols enable a clear separation between user data and program data in objects. Since symbols cannot be represented in JSON, there's no risk of data passed into an Express API having a bad Symbol.iterator property. In objects that mix user data with built-in functions and methods, like Mongoose models, you can use symbols to ensure that user data doesn't conflict with your built-in functionality.

Private Properties

Since no two symbols are ever equal, symbols are a convenient way to simulate private properties in JavaScript. Symbols don't show up in Object.keys(), and therefore, unless you explicitly export a symbol, no other code can access that property unless you explicitly go through the Object.getOwnPropertySymbols() function.

function getObj() {
 const symbol = Symbol('test');
 const obj = {};
 obj[symbol] = 'test';
 return obj;

const obj = getObj();

Object.keys(obj); // []

// Unless you explicitly have a reference to the symbol, you can't access the // symbol property. obj[Symbol('test')]; // undefined

// You can still get a reference to the symbol using getOwnPropertySymbols() const [symbol] = Object.getOwnPropertySymbols(obj); obj[symbol]; // 'test'

Symbols are also convenient for private properties because they do not show up in JSON.stringify() output. Specifically, JSON.stringify() silently ignores symbol keys and values.

const symbol = Symbol('test');
const obj = { [symbol]: 'test', test: symbol };

JSON.stringify(obj); // "{}"

Moving On

Symbols are a great tool for representing internal state in objects while ensuring that user data stays separate from program state. With symbols, there's no more need for conventions like prefixing program state properties with '$'. So next time you find yourself setting an object property to $$__internalFoo, consider using a symbol instead.

Thanks for reading

If you liked this post, please do share/like it with all of your programming buddies!

Follow us on Facebook | Twitter

Further reading

The Complete JavaScript Course 2019: Build Real Projects!

The Web Developer Bootcamp

JavaScript Programming Tutorial - Full JavaScript Course for Beginners

ES6 Arrow Functions Cheatsheet

Getting Started with ES6 Arrow Functions in JavaScript

The Complete Guide to ES6 Destructuring

Dropbox API / JavaScript ES6 Tutorial - Expense Organizer

Authenticate a Node ES6 API with JSON Web Tokens

javascript es6 web-development

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Hire Web Developer

Looking for an attractive & user-friendly web developer? HourlyDeveloper.io, a leading web, and mobile app development company, offers web developers for hire through flexible engagement models. You can **[Hire Web...

Why Web Development is Important for your Business

With the rapid development in technology, the old ways to do business have changed completely. A lot more advanced and developed ways are ...

Important Reasons to Hire a Professional Web Development Company

    You name the business and I will tell you how web development can help you promote your business. If it is a startup or you seeking some...

Hire Dedicated eCommerce Web Developers | Top eCommerce Web Designers

Build your eCommerce project by hiring our expert eCommerce Website developers. Our Dedicated Web Designers develop powerful & robust website in a short span of time.

How long does it take to develop/build an app?

This article covers A-Z about the mobile and web app development process and answers your question on how long does it take to develop/build an app.