JS Symbol, what the heck?
Tags: javascript, tutorial, beginners
Published: 2021-10-27 13:04:14

Among the primitive data type, you may have already heard about `Symbol`. But you're asking yourself what is it? When is it useful? When are they currently used?
If it's the case, you are in the right place. It was my case few times ago :)

## What is it?

`Symbol` is a new primitive data type introduced with ES6. It can provide us unique value by using directly `Symbol(optionalDescriptiveText)` or can share Symbol through the global Symbol registry.
Thanks to it we can add property to object being sure it doesn't conflict with another one.


## Creation

### Unique value

I already spoiled it in the previous part, you can create a unique Symbol value by using `Symbol(optionalDescriptiveText)`:

```javascript
const myFirstSymbol = Symbol('This is my first symbol');
```

> **Watch out:** the optional descriptive text is here just to add a description to the Symbol. It will not transform the string into a Symbol:

As I said previously a Symbol is unique:

```javascript
// Will print false!!!
console.log(Symbol('Description') !== Symbol('Description'))
```

> **Warning:** you cannot instantiate it as you may do with `String`, `Boolean` or `Number`:

```javascript
// Will show you in the console something like
// Uncaught TypeError: Symbol is not a constructor
new Symbol('Trying to make an object');
```

-----


### Shared `Symbol`

You can also create `Symbol` to be shared through your application/library. 
You can do it with: `Symbol.for(key)`:

```javascript
// Create a shared Symbol
const sharedSymbol = Symbol.for('Shared Symbol');

// You can assert that you can get this Symbol

// Will print true
console.log(sharedSymbol === Symbol.for('Shared Symbol'));
```

> **Note:** You can create a shared Symbol with `undefined` / no key

```javascript
// Will print true
console.log(Symbol.for() === Symbol.for(undefined));
```

> **Note:** there is a `keyFor` method accessible from `Symbol` to retrieve the key of a **shared** Symbol:

```javascript
const sharedSymbol = Symbol.for("Key of shared symbol");

// Will print: "Key of shared symbol"
console.log(Symbol.keyFor(sharedSymbol));
```

Not gonna lie, I don't know in which situation we would like to retrieve the key of a shared Symbol. If you know some use cases, do not hesitate to put it in commentaries :)

----


Okay, now that we have seen how to create a `Symbol`, let's some properties that have `Symbol`s.

## Properties

### Not enumerable

When adding in objet a Symbol as key, the property will not be **enumerable**:

```javascript
const person = {
  firstName: "Bob",
  lastName: "Sponge",
  [Symbol("secret")]: "I was created by a marine biologist",
};

// Will print
// Key: "firstName" and value: "Bob"
// Key: "lastName" and value: "Sponge"
Object.entries(person).forEach(([key, value]) =>
  console.log(`Key: "${key}" and value: "${value}"`)
);
```

### Same value in `iframe`

There is something pretty unpredictable that happens. Each `iframe` has its own **realm** so its own instance of `Symbol`. However, shared `Symbol` are the same through realm.

Let's make an `iframe` in which we declare a shared `Symbol`:

```html
<iframe
  srcdoc="<script>
              var sharedSymbol = Symbol.for('Shared symbol');
              </script>"
></iframe>
```

Now let's get this `iframe` and get the **window** from it through the `contentWindow` property:

```javascript
const iframe = document.querySelector("iframe");
const iframeWindow = iframe.contentWindow;

// Will print false!
console.log(iframeWindow.Symbol === Symbol);

// But will print true!
console.log(
  iframeWindow.sharedSymbol === Symbol.for("Shared symbol")
);
```

> **Note:** Thanks to it features added by well-known Symbols work through realms. For example `for...of` on iterable objects.


> **Note:** `var` is accessible through the window object because it has global scope. Let's see my article [**Differences between var, let and const**](https://dev.to/romaintrotard/var-let-const-what-s-the-difference-2eh0) for more explanations.


## Current usage: well-known Symbols

There are some well-known `Symbol`s that are used to implement methods that you use everyday.

> **Note:** These well-known `Symbol`s are static properties of `Symbol` and are referenced with the notation `@@name` that corresponds to `Symbol.name`.

Let's see a few:

- `Symbol.iterator`: This symbol defines the default iterator for an object that will make the use of `for...of` possible. The object will be then **iterable**.

For example, if we have an `Array` of `Person` with the type:

```typescript
type Person = {
  firstName: string;
  lastName: string;
}
```

And when we loop on this `Array`, we want to get directly the template `${firstName} ${lastName}`. The code will be:

```javascript
const persons = [
  { lastName: "Spears", firstName: "Britney" },
  {
    lastName: "Grande",
    firstName: "Ariana",
  },
  {
    lastName: "Timberlake",
    firstName: "Justin",
  },
];

persons[Symbol.iterator] = function () {
  let index = 0;
  return {
    next: () => {
      const hasNext = this.length > index;

      if (hasNext) {
        const person = this[index++];

        return {
          done: false,
          value: `${person.firstName} ${person.lastName}`,
        };
      } else {
        return {
          done: true,
        };
      }
    },
  };
};

// This will print
// Britney Spears
// Ariana Grande
// Justin Timberlake
for (let person of persons) {
  console.log(person);
}
```

> **Note:** It's possible to implement it with generators to make it "simpler". But not to lost people I decided to code it "manually".

----

- `Symbol.hasInstance`: This symbol manages the configuration of the `instanceof` operator for a class.

For example, let's imagine we have two classes `Building` and `House`. 
We want `new House() instanceof Building` to return true. We can do this:

```javascript
class Building {
  constructor() {
    this.type = "building";
  }

  static [Symbol.hasInstance](instance) {
    return (
      instance.type === "house" ||
      instance.type === "building"
    );
  }
}

class House {
  constructor() {
    this.type = "house";
  }

  static [Symbol.hasInstance](instance) {
    return instance.type === "house";
  }
}

// Will print true
console.log(new House() instanceof Building);
// Will print false
console.log(new Building() instanceof House);
```

> **Note:** In real world, we would not do this but just:

```javascript
class Building {}

class House extends Building {}
```

----

- `Symbol.split`: This symbol can be used as a method and will be called by the `String`'s `split` method:

For example, we can define a `WordSplit` class that will split a phrase with space:

```javascript
class WordSplit {
  [Symbol.split](string) {
    return string.split(" ");
  }
}

console.log(
  "A phrase that will be splitted".split(new WordSplit())
);
```  

----

- `Symbol.toStringTag`: The symbol can be used to defined an object's property that returns a string that will be used to describe the object. This method is called by the `Object`'s `toString` method:

```javascript
class Computer {
  constructor() {
    this[Symbol.toStringTag] = "Computer";
  }
}

// Will print [object Computer]
console.log(new Computer().toString());
```

> **Note:** Otherwise it would print `[object Object]`

----

## Conclusion

We just see together what `Symbol` is, its properties and where they are currently used. I hope it's now clear for you what are `Symbol`s and where they are currently used in everyday life features. So do not tell yourself that `Symbol` are not useful anymore (if it was the case) :)

Fun fact, **React** uses `Symbol` to tag the type of elements through the property `$$typeof`: [see code](https://github.com/facebook/react/blob/main/packages/shared/ReactSymbols.js).


---- 


Do not hesitate to comment and if you want to see more, you can follow me on [Twitter](https://twitter.com/romain_trotard ) or go to my [Website](https://www.romaintrotard.com). :panda_face: