Symbols
// Every Symbol is unique:
const a = Symbol("id");
const b = Symbol("id");
console.log(a === b); // false
// Symbol as object key (no collision):
const ID = Symbol("id");
const user = {
name: "Alice",
[ID]: 12345 // symbol key
};
user[ID]; // 12345
Object.keys(user); // ["name"] — symbols hidden!
JSON.stringify(user); // {"name":"Alice"} — symbols excluded
// Global symbol registry:
const sym1 = Symbol.for("shared");
const sym2 = Symbol.for("shared");
console.log(sym1 === sym2); // true
Well-Known Symbols
// Symbol.iterator — make objects iterable:
class Range {
constructor(start, end) {
this.start = start;
this.end = end;
}
[Symbol.iterator]() {
let n = this.start;
const end = this.end;
return {
next() {
return n <= end
? { value: n++, done: false }
: { value: undefined, done: true };
}
};
}
}
console.log([...new Range(1, 5)]); // [1,2,3,4,5]
for (const n of new Range(1, 3)) console.log(n); // 1, 2, 3
// Symbol.toPrimitive — control type conversion:
class Money {
constructor(amount, currency) {
this.amount = amount;
this.currency = currency;
}
[Symbol.toPrimitive](hint) {
if (hint === "number") return this.amount;
return this.amount + " " + this.currency;
}
}
const price = new Money(100, "USD");
console.log(+price); // 100
console.log("" + price); // "100 USD"
⚡ Key Takeaways
- Symbols are always unique — Symbol("x") !== Symbol("x")
- Symbol keys are hidden from Object.keys() and JSON.stringify()
- Symbol.for() creates/retrieves global symbols — shared across modules
- Well-known symbols customize built-in JS behavior
- Symbol.iterator makes custom objects work with for...of and spread
🎯 Practice Exercises
EXERCISE 1
Add Symbol.iterator to a LinkedList class so it works with for...of. Also add Symbol.toPrimitive to return the list length as a number.