Execute Program

Modern JavaScript: Symbol Basics

Welcome to the Symbol Basics lesson!

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • Normally, object keys are strings. When an object has the key 'name', we can access it with obj.name or with obj['name'].

  • >
    const obj = {name: 'Amir'};
    obj['name'];
    Result:
    'Amir'Pass Icon
  • Most JavaScript data types can't be used as object keys. For example, if we try to use true as a key, it gets converted into the string 'true'.

  • >
    const obj = {};
    obj[true] = 1;
    Object.keys(obj);
    Result:
  • If an object already has the key 'true', and we try to add true as a key, we'll end up overwriting the value that already exists at 'true'. The object will still only have one 'true' key at the end.

  • >
    const obj = {
    'true': 1
    };
    obj[true] = 2;
    obj;
    Result:
    {true: 2}Pass Icon
  • Modern versions of JavaScript have alleviated this limitation by adding symbols. Symbols are a new data type that can be used in object keys. We'll begin by exploring the basics of how symbols work, then move on to using them as keys.

  • We can define a symbol by giving it a description: Symbol('name') or Symbol('dataIsSynced'). The description should say what the symbol is used for.

  • >
    Symbol('name').description;
    Result:
    'name'Pass Icon
  • Symbol equality works like array equality. A given array is always equal to itself. Likewise, a given symbol is always equal to itself.

  • >
    const catArray = ['cat'];
    catArray == catArray;
    Result:
    truePass Icon
  • >
    const dogArray = ['dog'];
    dogArray === dogArray;
    Result:
    truePass Icon
  • >
    const horseSymbol = Symbol('horse');
    horseSymbol == horseSymbol;
    Result:
    truePass Icon
  • >
    const pigSymbol = Symbol('pig');
    pigSymbol === pigSymbol;
    Result:
    truePass Icon
  • Two arrays are never equal to each other, even if they contain the same elements. Likewise, two symbols are never equal to each other, even if they have the same description.

  • >
    ['cat'] == ['cat'];
    Result:
    falsePass Icon
  • >
    ['dog'] === ['dog'];
    Result:
    falsePass Icon
  • >
    Symbol('cat') == Symbol('dog');
    Result:
    falsePass Icon
  • >
    Symbol('horse') === Symbol('cow');
    Result:
    falsePass Icon
  • >
    Symbol('cat') == Symbol('cat');
    Result:
    falsePass Icon
  • >
    Symbol('dog') === Symbol('dog');
    Result:
    falsePass Icon
  • >
    const symbol1 = Symbol('cat');
    const symbol2 = Symbol('cat');
    [symbol1 === symbol1, symbol1 === symbol2, symbol2 === symbol2];
    Result:
    [true, false, true]Pass Icon
  • Now that we've seen the basics of symbols, we can start using them as object keys. But how exactly do we get a symbol key into an object? If we store it in a nameSymbol variable and write {nameSymbol: 1}, then the unquoted nameSymbol key is just a string. The key is 'nameSymbol' and the nameSymbol variable isn't referenced at all.

  • >
    const nameSymbol = Symbol('name');
    const obj = {
    nameSymbol: 1
    };
    Object.keys(obj);
    Result:
  • To use the symbol itself as a key, we can use computed property syntax: {[nameSymbol]: ...}. Then, to access a symbol key, we can do user[nameSymbol]. (Trying to access it with user.nameSymbol would have the same problem as before: it would look up the value at the string key 'nameSymbol'.)

  • Be careful in these next examples: we intentionally mix up symbols and strings.

  • >
    // For reference: accessing a key that doesn't exist gives undefined.
    const user = {};
    user.propertyThatDoesntExist;
    Result:
  • >
    const nameSymbol = Symbol('name');
    const user = {[nameSymbol]: 'Amir'};
    user[nameSymbol];
    Result:
    'Amir'Pass Icon
  • >
    const nameSymbol = Symbol('name');
    const user = {[nameSymbol]: 'Amir'};
    user.nameSymbol;
    Result:
    undefinedPass Icon
  • >
    const nameSymbol = Symbol('name');
    const user = {nameSymbol: 'Amir'};
    user[nameSymbol];
    Result:
    undefinedPass Icon
  • Symbol descriptions are usually strings, so it's tempting to think of symbols as just a special kind of string. But that's a mistake! Here's an example of why:

  • We can use the string 'name' as an object key. We can also use the symbol Symbol('name') as an object key. An object can have both of those keys at once, with each key holding a different value!

  • >
    const nameString = 'name';
    const nameSymbol = Symbol('name');
    const user = {
    [nameString]: 'Amir',
    [nameSymbol]: 'Betty'
    };
    [user['name'], user[nameSymbol]];
    Result:
    ['Amir', 'Betty']Pass Icon
  • The example above hints at why symbols exist. They allow us to add properties to objects without affecting the "normal" properties. We'll see examples of that in future lessons.