Understanding symbols in JavaScript
Tags: featuredposts, javascript
Published: 2020-05-22 17:44:12

---
title: Understanding symbols in JavaScript
published: true
date: 2020-05-05 17:30:25 UTC
tags: Featured Posts,javascript
canonical_url: https://blog.logrocket.com/understanding-symbols-in-javascript/
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/6udg4rqsli2dk82543sp.png
---
**Written by [Eslam Hefnawy](https://blog.logrocket.com/author/eslamhefnawy/)**✏️

Before symbols were introduced in ES6 as a new type of primitive, JavaScript used seven main types of data, grouped into two categories:

1. Primitives, including the string, number, bigint, boolean, null, and undefined data types
2. Objects, including more complex data structures, such as arrays, functions, and regular JS objects

Starting with ES6, symbols were added to the primitives group. Like all other primitives, they are immutable and have no methods of their own.

The original purpose of symbols was to provide globally unique values that were kept private and for internal use only. However, in the final implementation of this primitive type, symbols ended up not being private, but they did keep their value uniqueness.

We’ll address the privacy issue a bit later. As for the uniqueness of symbols, if you create two different symbols using the factory function `Symbol()`, their values will not be equal.

```jsx
const symbol1 = Symbol('1');
const symbol2 = Symbol('2');

console.log(symbol1 === symbol2); // Outputs False
```

The data type for `symbol1` and `symbol2` is `symbol`. You can check it by logging it into your console.

```jsx
console.log(typeof(symbol1)); // Outputs symbol
console.log(typeof(symbol2)); // Outputs symbol
```

The `Symbol()` function can take a string parameter, but this parameter has no effect on the value of the symbol; it’s there just for descriptive purposes. So this string is useful for debugging since it provides you with a reference when you print the symbol, but it’s nothing but a label.

```jsx
console.log(symbol1); // Outputs Symbol(symbol1)
console.log(symbol2); // Outputs Symbol(symbol1)
```

You may be wondering why the `Symbol()` function doesn’t use the new keyword to create a new symbol. You wouldn’t write `const symbol = new Symbol()` because `Symbol()` is a function, not a constructor.

```jsx
const symbol3 = new Symbol('symbol3');

// Outputs: Uncaught TypeError: Symbol is not a constructor 
```

Since symbols are primitives and thus immutable, the value of a symbol cannot be changed, just like the value of a number-type primitive can’t be changed.

Here’s a practical example, first with a number primitive:

```jsx
let prim1 = 10;
console.log(prim1); // Outputs 10

prim1 = 20;
console.log(prim1); // Outputs 20

10 = 20 // Outputs: Uncaught ReferenceError: Invalid left-hand side in assignment

10 == 20 // Outputs: False
```

We’re assigning the `prim1` variable the value `10`, which is a number primitive. We can reassign the variable `prim1` with a different value, so we can say that we want our `prim1` variable to have the value of `20` instead of `10`.

However, we cannot assign the value `20` to the number primitive `10`. Both `10` and `20` are number-type primitives, so they can’t be mutated.

The same applies to symbols. We can reassign a variable that has a symbol value to another symbol value, but we cannot mutate the value of the actual symbol primitive.

```jsx
let symb4 = Symbol('4');
let symb5 = Symbol('5');

symb4 = symb5; 
console.log(symb4); // Outputs Symbol(5)

Symbol(4) = Symbol(5); // Outputs: ReferenceError: Invalid left-hand side in assignment
```

With most primitives, the value is always exactly equal to other primitives with an equivalent value.

```jsx
const a = 10;
const b = 10;

a == b; // Outputs True
a === b; // Outputs True

const str1 = 'abc';
const str2 = 'abc';

str1 == str2; // Outputs True
str1 === str2; // Outputs True
```

However, object data types are never equal to other object types; they each have their own identity.

```jsx
let obj1 = { 'id': 1 };
let obj2 = { 'id': 1 };

obj1 == obj2; // Outputs False
obj1 === obj2; // Outputs False
```

You would expect symbols to behave like number- or string-type primitives, but they behave like objects from this point of view because each symbol has a unique identity.

```jsx
let symbol1 = Symbol('1');
let symbol2 = Symbol('2');

symbol1 == symbol2; // Outputs False
symbol1 === symbol2; // Outputs False
```

So what makes symbols unique? They are primitives, but they behave like objects when it comes to their value. This is extremely important to keep in mind when discussing the practical uses of symbols.

[![LogRocket Free Trial Banner](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/f760c-1gpjapknnuyhu8esa3z0jga.png?resize=1200%2C280&ssl=1)](https://logrocket.com/signup/)

## When and how are symbols used in real life?

As mentioned earlier, symbols were intended to b **e** unique, private values. However, they ended up not being private. You can see them if you print the object or use the `Object.getOwnPropertySymbols()` method.

This method returns an array of all the symbol properties found in the object.

```jsx
let obj = {};
let sym = Symbol();

obj['name'] = 'name';
obj[sym] = 'symbol';

console.log(obj);
```

![Array of Symbol Properties in an Object](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/05/array-symbol-properties-in-object.png?resize=408%2C529&ssl=1)

However, notice that the symbol is not visible to the `for` loop, so it’s skipped when the iteration takes place.

```jsx
for (let item in obj) { 
   console.log(item) 
}; // Outputs name

Object.getOwnPropertySymbols(obj);
```

In the same way, symbols are not part of the `Object.keys()` or `Object.getOwnPropertyNames()` results.

![No Symbols in Object Results](https://i1.wp.com/blog.logrocket.com/wp-content/uploads/2020/05/object-keys-getownpropertynames-results.png?resize=340%2C242&ssl=1)

Also, if you try to convert the object to a JSON string, the symbol will be skipped.

```jsx
let obj = {};
let sym = Symbol();

obj['name'] = 'name';
obj[sym] = 'symbol';

console.log(obj);
console.log(JSON.stringify(obj));
```

![Converting an Object to a JSON String](https://i0.wp.com/blog.logrocket.com/wp-content/uploads/2020/05/convert-object-to-json-string.png?resize=397%2C348&ssl=1)

So symbols aren’t quite private, but they can only be accessed in certain ways. Are they still useful? When and how are they used in real life?

Most commonly, symbols are used in two cases:

1. Unique property values that you don’t want users to overwrite by mistake
2. Unique keys for identifying object properties

Let’s see what each scenario looks like in practice.

### 1. Unique property values

For this use case, we’ll do a simple exercise in which we pretend to be a national travel advisory that issues travel safety recommendations. You can see the code [here](https://jsfiddle.net/7kshdgye/).

Let’s say we have a color-coded system to represent the various danger levels for a particular region.

- Code Red is the highest level; people should not travel to this region
- Code Orange is a high level; people should only travel to this region if really necessary
- Code Yellow represents a medium level of danger; people should remain vigilant when traveling to this region
- Code Green means no danger; people can safely travel to this region

We don’t want these codes and their values to be mistakenly overwritten, so we’ll define the following variables.

```jsx
const id = Symbol('id');

const RED = Symbol('Red');
const ORANGE = Symbol('Orange');
const YELLOW = Symbol('Yellow');
const GREEN = Symbol('Green');

const redMsg = Symbol('Do not travel');
const orangeMsg = Symbol('Only travel if necessary');
const yellowMsg = Symbol('Travel, but be careful');
const greenMsg = Symbol('Travel, and enjoy your trip');

let colorCodes = [{
    [id]: RED,
    name: RED.description,
    message: redMsg.description,
  },
  {
    [id]: ORANGE,
    name: ORANGE.description,
    message: orangeMsg.description,
  },
  {
    [id]: YELLOW,
    name: YELLOW.description,
    message: yellowMsg.description,
  },
  {
    [id]: GREEN,
    name: GREEN.description,
    message: greenMsg.description,
  }
]

let alerts = colorCodes.map(element => {
  return (`It is Code ${element.name}. Our recommendation for this region: ${element.message}.`);
});

let ul = document.getElementById("msgList");

for (let elem in alerts) {
  let msg = alerts[elem];
  let li = document.createElement('li');
  li.appendChild(document.createTextNode(msg));
  ul.appendChild(li);
}
```

The corresponding HTML and SCSS fragments for this exercise are as follows.

```jsx
<div>
  <h1>Alert messages</h1>
  <ul id="msgList"></ul>
</div>



ul {
  list-style: none;
  display: flex;
  flex: row wrap;
  justify-content: center;
  align-items: stretch;
  align-content: center;
}

li {
  flex-basis: 25%;
  margin: 10px;
  padding: 10px;

  &:nth-child(1) {
    background-color: red;
  }

  &:nth-child(2) {
    background-color: orange;
  }

  &:nth-child(3) {
    background-color: yellow;
  }

  &:nth-child(4) {
    background-color: green;
  }
}
```

If you log `colorCodes`, you’ll see that the ID and its value are both symbols, so they’re not displayed when retrieving the data as JSON.

It’s therefore extremely hard to mistakenly overwrite the ID of this color code or the value itself unless you know that they are there or you retrieve them, as described earlier.

### 2. Unique keys for identifying object properties

Before symbols were introduced, object keys were always strings, so they were easy to overwrite. Also, it was common to have name conflicts when using multiple libraries.

Imagine you have an application with two different libraries trying to add properties to an object. Or, maybe you’re using JSON data from a third party and you want to attach a unique `userID` property to each object.

If your object already has a key called `userID`, you’ll end up overwriting it and thus losing the original value. In the example below, the `userID` had an initial value that was overwritten.

```jsx
let user = {};

user.userName = 'User name';
user.userID = 123123123;

let hiddenID = Symbol();
user[hiddenID] = 9998763;

console.log(user);
```

![Username Value Overwritten](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/05/userid-value-overwritten.png?resize=625%2C497&ssl=1)

If you look at the user object above, you’ll see that it also has a \*\*`Symbol(): 9998763` property. This is the `[hiddenID]` key, which is actually a symbol. Since this doesn’t show up in the JSON, it’s hard to overwrite it. Also, you can’t overwrite this value when there’s no description attached to the symbol as string.

```jsx
user[] = 'overwritten?'; // Outputs SyntaxError: Unexpected token ]

user[Symbol()] = 'overwritten?'; 

console.log(user);
```

![JavaScript Symbol Property](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2020/05/symbol-property.png?resize=606%2C287&ssl=1)

Both symbols were added to this object, so our attempt to overwrite the original symbol with the value `99987` failed.

## Symbols are unique — until they aren’t

There’s one more caveat that makes symbols less useful than they were meant to be originally. If you declare a new `Symbol()`, the value is unique indeed, but if you use the `Symbol.for()` method, you’ll create a new value in the global symbol registry.

This value can be retrieved by simply calling the method `Symbol.for(key)`, if it already exists. If you check the uniqueness of the variables assigned such values, you’ll see that they’re not actually unique.

```jsx
let unique1 = Symbol.for('unique1');
let unique2 = Symbol.for('unique1');

unique1 == unique2; // Outputs True
unique1 == unique2; // Outputs True

Symbol.for('unique1') == Symbol.for('unique1'); // Outputs True
Symbol.for('unique1') === Symbol.for('unique1'); // Outputs True
```

Moreover, if you have two different variables that have equal values and you assign `Symbol.for()` methods to both of them, you’ll still get equality.

```jsx
let fstKey = 1;
let secKey = 1;

Symbol.for(fstKey) == Symbol.for(secKey); // Outputs True
Symbol.for(fstKey) === Symbol.for(secKey); // Outputs True
```

This can be beneficial when you want to use the same values for variables such as IDs and share them between applications, or if you want to define some protocols that apply only to variables sharing the same key.

You should now have a basic understanding of when and where you can use symbols. Be aware that even if they’re not directly visible or retrievable in JSON format, they can still be read since symbols don’t provide real property privacy or security.

* * * 
## Plug: [LogRocket](https://logrocket.com/signup/), a DVR for web apps
 
![LogRocket Dashboard Free Trial Banner](https://i2.wp.com/blog.logrocket.com/wp-content/uploads/2017/03/1d0cd-1s_rmyo6nbrasp-xtvbaxfg.png?resize=1200%2C677&ssl=1)
 
[LogRocket](https://logrocket.com/signup/) is a frontend logging tool that lets you replay problems as if they happened in your own browser. Instead of guessing why errors happen, or asking users for screenshots and log dumps, LogRocket lets you replay the session to quickly understand what went wrong. It works perfectly with any app, regardless of framework, and has plugins to log additional context from Redux, Vuex, and @ngrx/store.
 
In addition to logging Redux actions and state, LogRocket records console logs, JavaScript errors, stacktraces, network requests/responses with headers + bodies, browser metadata, and custom logs. It also instruments the DOM to record the HTML and CSS on the page, recreating pixel-perfect videos of even the most complex single-page apps.
 
[Try it for free](https://logrocket.com/signup/).
* * *

The post [Understanding symbols in JavaScript](https://blog.logrocket.com/understanding-symbols-in-javascript/) appeared first on [LogRocket Blog](https://blog.logrocket.com).