Symbols & Well-Known Symbols
📖 Concept
Symbol is a primitive type that creates unique, immutable identifiers. Every Symbol() call creates a completely unique value.
Use cases: Property keys that can't collide, defining protocols/interfaces, implementing well-known behaviors.
Well-Known Symbols are built-in Symbols that let you customize object behavior: Symbol.iterator, Symbol.toPrimitive, Symbol.toStringTag, Symbol.hasInstance, etc.
🏠 Real-world analogy: Symbols are like invisible ink labels. Two labels that say "id" in invisible ink are still different labels — even with the same description, each Symbol is unique.
💻 Code Example
1// Basic Symbols2const id1 = Symbol("id");3const id2 = Symbol("id");4console.log(id1 === id2); // false (every Symbol is unique)5console.log(typeof id1); // "symbol"67// As property keys (no collisions!)8const SECRET = Symbol("secret");9const user = {10 name: "Alice",11 [SECRET]: "hidden-value"12};13console.log(user[SECRET]); // "hidden-value"14console.log(Object.keys(user)); // ["name"] — Symbols are hidden!15console.log(JSON.stringify(user)); // '{"name":"Alice"}' — excluded!1617// Global Symbol registry18const s1 = Symbol.for("shared");19const s2 = Symbol.for("shared");20console.log(s1 === s2); // true (shared via registry)21console.log(Symbol.keyFor(s1)); // "shared"2223// Well-known Symbols24class Money {25 constructor(amount, currency) {26 this.amount = amount;27 this.currency = currency;28 }29 // Custom string conversion30 [Symbol.toPrimitive](hint) {31 if (hint === "number") return this.amount;32 if (hint === "string") return `${this.currency}${this.amount}`;33 return this.amount;34 }35 // Custom toString tag36 get [Symbol.toStringTag]() {37 return "Money";38 }39}40const price = new Money(42, "$");41console.log(+price); // 4242console.log(`${price}`); // "$42"43console.log(Object.prototype.toString.call(price)); // "[object Money]"4445// Symbol.iterator (make custom objects iterable)46class Range {47 constructor(start, end) {48 this.start = start;49 this.end = end;50 }51 [Symbol.iterator]() {52 let current = this.start;53 const end = this.end;54 return {55 next() {56 return current <= end57 ? { value: current++, done: false }58 : { done: true };59 }60 };61 }62}63console.log([...new Range(1, 5)]); // [1, 2, 3, 4, 5]
🏋️ Practice Exercise
Mini Exercise:
- Create a Symbol-based private property pattern for a class
- Implement
Symbol.iteratorto make a linked list iterable - Use
Symbol.toPrimitiveto make a custom class work with arithmetic - Create a plugin system where plugins register via Symbol keys
⚠️ Common Mistakes
Symbols are NOT strings —
Symbol('id')and'id'are completely differentSymbols are invisible to
for...in,Object.keys(), andJSON.stringify— useObject.getOwnPropertySymbols()Symbol()vsSymbol.for()—Symbol()always creates a new one;Symbol.for()uses a global registryCan't convert Symbol to a string implicitly —
'' + symbolthrows TypeError; usesymbol.toString()explicitlySymbols are not truly private — they're just non-enumerable.
Object.getOwnPropertySymbols()exposes them
💼 Interview Questions
🎤 Mock Interview
Mock interview is powered by AI for Symbols & Well-Known Symbols. Login to unlock this feature.