JavaScript Symbols

Use unique Symbol values as collision-free object keys and customize built-in object behavior.

TL;DR

  1. 01Instantiate unique primitive values with the built-in Symbol factory.
  2. 02Configure unique collision-free keys hidden from standard object loop enumerations.
  3. 03Use well-known symbols to customize core language behaviors like iteration.

Tips

  1. 01Use local symbols to declare properties that will never collide with third-party keys.
  2. 02Retrieve shared symbol values across realms using the global Symbol.for() registry.

Warnings

  1. 01Avoid calling Symbol using new because symbols are primitives rather than constructible classes.
  2. 02Remember that symbol properties are omitted by standard operations like JSON.stringify and loops.

What Symbols Are

    Unique primitive

    Generates unique primitive values on every function call.

    const a = Symbol("id");
    const b = Symbol("id");
    console.log(a === b); // false
    Description debugging

    Assigns debugging descriptions which do not affect symbol identity.

    const s = Symbol("label");
    console.log(s.description); // "label"
    Primitives type

    Returns symbol from typeof checks.

    console.log(typeof Symbol()); // "symbol"

Object Keys

    Symbol properties

    Assigns properties using symbol keys to guarantee collision-free attributes.

    const KEY = Symbol("key");
    const user = { name: "Ada", [KEY]: 42 };
    Bracket accesses

    Queries symbol keys using bracket notation rather than dot accesses.

    console.log(user[KEY]); // 42
    Descriptors listing

    Lists symbol keys using Reflect methods.

    Reflect.ownKeys(user); // ["name", Symbol(key)]

Omitted Enumeration

    Loop exclusion

    Excludes symbol properties from keys listings and loops.

    const obj = { name: "A", [Symbol("id")]: 1 };
    console.log(Object.keys(obj)); // ["name"]
    JSON serialization

    Omits symbol keys during stringify operations.

    console.log(JSON.stringify(obj)); // '{"name":"A"}'
    Explicit queries

    Queries symbols using specialized getOwnPropertySymbols calls.

    Object.getOwnPropertySymbols(obj); // [Symbol(id)]

Well-Known Symbols

    Symbol.iterator

    Enables custom iteration behavior on plain objects.

    const range = {
      [Symbol.iterator]() {
        return { next: () => ({ done: true }) };
      }
    };
    [...range];
    Symbol.toPrimitive

    Enforces custom conversions into primitives.

    const cash = {
      amount: 50,
      [Symbol.toPrimitive](hint) {
        return hint === "string" ? `$${this.amount}` : this.amount;
      }
    };
    Symbol.hasInstance

    Overrides instanceof check mechanics for target configurations.

    class Even {
      static [Symbol.hasInstance](num) {
        return num % 2 === 0;
      }
    }
    console.log(4 instanceof Even); // true

Global Registry

    Symbol.for

    Registers shared symbol instances in a global scope index.

    const a = Symbol.for("app.id");
    const b = Symbol.for("app.id");
    console.log(a === b); // true
    Symbol.keyFor

    Returns registered index strings matching active shared symbols.

    console.log(Symbol.keyFor(a)); // "app.id"

In Practice

FAQ