JavaScript Symbol

A Symbol is a primitive data type introduced in ES6 that creates a guaranteed unique identifier. Every time a Symbol is created, it is unique — even if two symbols have the same description.

Symbols are mainly used as unique property keys on objects to avoid name collisions, and to define custom behavior through well-known symbols.

Creating a Symbol

let id1 = Symbol("id");
let id2 = Symbol("id");

console.log(id1 === id2);      // false — always unique!
console.log(typeof id1);       // symbol
console.log(id1.toString());   // Symbol(id)
console.log(id1.description);  // id

Symbols as Object Property Keys

Symbols are ideal for adding properties to objects without risk of overwriting existing keys — especially important when working with third-party objects or shared libraries.

let idKey = Symbol("id");
let roleKey = Symbol("role");

let user = {
  name: "Ritika",
  [idKey]: 101,
  [roleKey]: "admin"
};

console.log(user.name);       // Ritika
console.log(user[idKey]);     // 101
console.log(user[roleKey]);   // admin

// Symbol keys are NOT visible in regular object enumeration
console.log(Object.keys(user));       // ["name"]
console.log(Object.values(user));     // ["Ritika"]
console.log(JSON.stringify(user));    // {"name":"Ritika"} — symbols excluded

Symbols Are Hidden in Iteration

Symbol-keyed properties are skipped by for...in, Object.keys(), and JSON.stringify(). This makes them useful for metadata that should not appear in normal object output.

let config = Symbol("config");

let app = {
  name: "eStudy247",
  version: "2.0",
  [config]: { debug: true, maxUsers: 500 }
};

for (let key in app) {
  console.log(key); // name, version — config symbol is hidden
}

// Access symbol properties explicitly
console.log(app[config].debug); // true

Getting Symbol Keys — Object.getOwnPropertySymbols()

let symbols = Object.getOwnPropertySymbols(app);
console.log(symbols); // [Symbol(config)]
console.log(app[symbols[0]]); // { debug: true, maxUsers: 500 }

Global Symbol Registry — Symbol.for()

Normally, every Symbol() call creates a new unique symbol. Symbol.for() creates (or retrieves) a symbol in a global registry, allowing the same symbol to be shared across different parts of an application.

let s1 = Symbol.for("sharedKey");
let s2 = Symbol.for("sharedKey");

console.log(s1 === s2); // true — same symbol from registry

// Get the key of a registered symbol
console.log(Symbol.keyFor(s1)); // "sharedKey"

Well-Known Symbols

JavaScript uses a set of built-in symbols (called well-known symbols) to allow objects to customize their behavior with built-in language operations.

Symbol.iterator — Custom Iteration

Defines how an object is iterated in a for...of loop.

let range = {
  from: 1,
  to: 5,
  [Symbol.iterator]() {
    let current = this.from;
    let last    = this.to;
    return {
      next() {
        return current <= last
          ? { value: current++, done: false }
          : { value: undefined, done: true };
      }
    };
  }
};

for (let n of range) {
  console.log(n); // 1, 2, 3, 4, 5
}

Symbol.toPrimitive — Custom Type Conversion

Controls how an object is converted to a primitive value.

let temperature = {
  celsius: 37,
  [Symbol.toPrimitive](hint) {
    if (hint === "number") return this.celsius;
    if (hint === "string") return `${this.celsius}°C`;
    return this.celsius;
  }
};

console.log(+temperature);        // 37     (number hint)
console.log(`Temp: ${temperature}`); // Temp: 37°C (string hint)
console.log(temperature + 0);     // 37     (default hint)

Symbol.hasInstance — Custom instanceof Behavior

class EvenNumber {
  static [Symbol.hasInstance](num) {
    return typeof num === "number" && num % 2 === 0;
  }
}

console.log(4 instanceof EvenNumber);   // true
console.log(7 instanceof EvenNumber);   // false

Symbol.toStringTag — Custom Object Label

class Product {
  get [Symbol.toStringTag]() {
    return "Product";
  }
}

let p = new Product();
console.log(Object.prototype.toString.call(p)); // [object Product]

Symbol.species — Custom Return Type for Inherited Methods

class MyArray extends Array {
  static get [Symbol.species]() {
    return Array;  // map/filter return plain Array, not MyArray
  }
}

let arr = new MyArray(1, 2, 3);
let mapped = arr.map(x => x * 2);

console.log(mapped instanceof MyArray);  // false
console.log(mapped instanceof Array);    // true

Symbol Use Cases Summary

Use CaseDescription
Unique property keysAvoid naming conflicts in objects
Hidden metadataStore internal data not visible in normal iteration
Library/framework hooksAllow users to customize behavior without key conflicts
Custom iteration (Symbol.iterator)Make any object work with for...of
Custom type conversionControl how object converts to number/string

Key Points to Remember

  • A Symbol is always unique — two symbols with the same description are not equal
  • Symbols are used as unique property keys to avoid naming collisions
  • Symbol-keyed properties are hidden from Object.keys(), for...in, and JSON.stringify()
  • Use Object.getOwnPropertySymbols() to retrieve symbol-keyed properties
  • Symbol.for(key) creates or retrieves a global shared symbol — useful across modules
  • Well-known symbols like Symbol.iterator and Symbol.toPrimitive customize built-in JavaScript behavior
  • Symbol.iterator is what makes objects compatible with for...of and spread syntax

Leave a Comment

Your email address will not be published. Required fields are marked *