JavaScript ES6 Symbols
Tags: javascript
Published: 2020-08-01 03:37:25

---
title: JavaScript ES6 Symbols
published: true
description: JavaScript Symbols are a primitive data type with unique values. They can be used as identifiers or as properties that don't overlap with other libraries or modules.
tags: JavaScript
cover_image: https://images.ctfassets.net/f1aa79r6xovt/4aoVkkhVwCX0065LwEPdnt/6497b674b7acf3ad79fe6f7a843ad910/Screen_Shot_2020-07-31_at_8.28.01_PM.png
canonical_url: https://cole.codes/posts/javascript-es6-symbols-a-metaprogramming-primitive
---
  
  
The JavaScript [Symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol) is a primitive data structure that has a unique value. They can be used as identifiers since no two symbols are the same. Unlike strings, Symbols can be used to create properties that don&#39;t overlap with other libraries or modules.

## Example

```javascript
const sym = Symbol();
const namedSymbol = Symbol('javascript');

sym === namedSymbol // false
typeof sym // "symbol"

console.log(namedSymbol); // Symbol(javascript)
console.log(namedSymbol.description); // javascript
```

Pretty neat, now our JavaScript app can uniquely identify properties without any risk of colliding with another identifier. But what if we want to share symbols across the codebase?

### Shared Symbols
```javascript
const sym1 = Symbol.for('javascript');
const sym2 = Symbol.for('javascript');

sym1 === sym2 // true
```

When we use Symbol.for we can leverage shared symbols that are available in the global symbol registry for our codebase.



## Why use Symbols?
Now that we understand that Symbols are unique identifiers, we can understand the potential for what a software engineer can do with them.

### Symbols can be used for metaprogramming

```javascript
const UserType = Symbol('userType');
const Administrator = Symbol('administrator');
const Guest = Symbol('guest');

const currentUser = {
  [UserType]: Administrator,
  id: 1,
  name: "Cole Turner"
};

console.log(currentUser); // {id: 1, name: "Cole Turner", Symbol(userType): Symbol(administrator)}

console.log(JSON.stringify(currentUser)); // {"id":1,"name":"Cole Turner"}

currentUser[UserType] == Administrator; // true
currentUser[UserType] == Guest; // false
```

In the example above, a symbol is used to type the object. The property is only available when referenced through the symbol reflection. This is great for when we want to add properties to an object that we don&#39;t want to appear in non-symbol reflection, such as JSON formatting or object iteration.

### Symbols are separate from string keys

```javascript
const languages = {
  javascript: 'JavaScript';
};

// Extend an object without conflict
const isLocal = Symbol('local');
const localLanguages = {
  ...languages,
  [isLocal]: true
};

// Detect if we're using local or the original languages object
[languages, localLanguages].map(obj => {
  if (obj[isLocal]) {
    console.log('Local languages:', obj);
  } else {
    console.log('Original languages:', obj);
  }
});
```

In the example above, we can extend objects without conflict with their original properties. This also means that when we are stringifying, the symbols are not included.

### Symbols can be used as ENUM
A great use case for Symbols is when there is a need for enumerated values. 

**JavaScript Symbols - ENUM example

```javascript
const Tree = Symbol('🌴');
const Flower = Symbol('🌻');
const Leaf = Symbol('🍁');
const Mushroom = Symbol('🍄');

const plantTypes = [Tree, Flower, Leaf, Mushroom];

function createPlant(type) {
  if (!plantTypes.includes(type)) {
    throw new Error('Invalid plant type!');
  }
}
```

Here we are using Symbols to control behaviors without those properties leaking into the typical reflection, and preventing runtime errors from typos.



## JavaScript Metaprogramming with Symbols
With Symbols we can dive deep into low-level JavaScript to change behaviors for various use cases. This lets us create powerful objects that can do more than meets the eye. Here are some examples of how we can use Symbols for JavaScript metaprogramming.

### [Symbol.asyncIterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator)

```javascript
const tenIntegers = {
  async* [Symbol.asyncIterator]() {
    for (let i = 1; i <= 10; i++) {
      yield i;
    }
  }
}

for await (const i of tenIntegers) {
  console.log(i);
  //  1
  //  ...
  //  10
}
```



### [Symbol.hasInstance](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/hasInstance)

```javascript
const Loggable = Symbol('loggable');

class LoggableError extends Error {
  static [Symbol.hasInstance](instance) {
    return instance instanceof LoggableError || instance[Loggable] === true;
  }
}

class ApplicationError extends Error {
  [Loggable] = true;

  logError() {
    if (this instanceof LoggableError) {
      return;
    }

    fetch('/log', { message: this.message });
  }
}

class DatabaseError extends ApplicationError {
    [Loggable] = false;
}
```



### [Symbol.iterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator)

```javascript
const users = {
  1: { name: 'Cole Turner' },
  2: { name: 'Anonymous' },
};

function toValuesArray(obj) {
  return {
    ...obj,

    [Symbol.iterator]: function* () {
      for (const value of Object.values(this)) {
        yield value;
      }
    },
  };
}

// toValuesArray will now change the spread of our object
const arrayOfUsers = [...toValuesArray(users)];
```



## Conclusion
The Symbol is a new primitive that can unlock a lot of potential with metaprogramming in JavaScript. They make great enumerated values, allow software engineers to extend objects without collision, and can separate concerns when working with data across the codebase.

For more information, [check out the MDN documentation on Symbols](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol).