Gyaan

Symbols

advanced Symbol ES6 primitives iterators

Symbol is a primitive type introduced in ES6. Every Symbol is unique — even if two Symbols have the same description, they’re not equal. The main use case is creating property keys that are guaranteed to never collide with anything else.

Creating Symbols

const s1 = Symbol();
const s2 = Symbol();
console.log(s1 === s2); // false (every Symbol is unique)

// Description is just a label for debugging
const id = Symbol('id');
console.log(id.toString()); // 'Symbol(id)'
console.log(id.description); // 'id'

Note that we don’t use new Symbol() — Symbol is a primitive, not an object.

Symbols as Object Keys

This is the main use case. When we use a Symbol as a key, it won’t collide with any string key or any other Symbol key.

const ID = Symbol('id');
const user = {
  name: 'Manish',
  [ID]: 12345  // Symbol key — uses computed property syntax
};

console.log(user[ID]);    // 12345
console.log(user.name);   // 'Manish'

This is useful in libraries — if we add a Symbol property to a user’s object, we won’t accidentally overwrite any of their existing properties.

Symbols Are Not Enumerable

Symbols don’t show up in for...in, Object.keys(), or JSON.stringify(). This makes them great for “hidden” metadata properties.

const secret = Symbol('secret');
const obj = { visible: true, [secret]: 'hidden value' };

console.log(Object.keys(obj));          // ['visible']
console.log(JSON.stringify(obj));        // '{"visible":true}'
for (let key in obj) console.log(key);  // 'visible'

// But we CAN access them if we know how
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(secret)]

Symbol.for() — Global Symbol Registry

Symbol.for() creates a Symbol in a global registry. If a Symbol with that key already exists, it returns the same one. This is how we share Symbols across files or modules.

const s1 = Symbol.for('app.id');
const s2 = Symbol.for('app.id');
console.log(s1 === s2); // true (same Symbol from registry)

// Regular Symbol() always creates a new one
const s3 = Symbol('app.id');
console.log(s1 === s3); // false

Well-Known Symbols

JavaScript has built-in Symbols that let us customize how objects behave with language features. The most important ones:

Symbol.iterator

Makes an object iterable (usable in for...of loops and spread syntax).

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

console.log([...range]); // [1, 2, 3]
for (const n of range) console.log(n); // 1, 2, 3

Symbol.toPrimitive

Controls how an object is converted to a primitive value.

const money = {
  amount: 100,
  currency: 'INR',
  [Symbol.toPrimitive](hint) {
    if (hint === 'number') return this.amount;
    if (hint === 'string') return `${this.amount} ${this.currency}`;
    return this.amount; // default
  }
};

console.log(+money);        // 100
console.log(`${money}`);    // '100 INR'
console.log(money + 50);    // 150

In simple language, Symbols are like guaranteed-unique IDs. We use them when we need a property key that can never accidentally clash with anything else — and the well-known symbols let us hook into JavaScript’s own behavior.

References