Frontend Development › JavaScript & TypeScript
Symbol
A unique, non-string property key.
Also known as: symbol, javascript symbol, well-known symbols
Symbol creates unique identifiers usable as property keys — guaranteed never to collide with strings or other symbols. Beyond private-ish keys, well-known symbols are the language’s protocol hooks: Symbol.iterator, Symbol.asyncIterator, Symbol.toPrimitive, Symbol.dispose let objects plug into for...of, spreading, coercion and cleanup.
const id = Symbol('id'); // unique key, invisible to JSON/for-in
obj[Symbol.iterator] = function* () { … }; // now iterable with for...of
Libraries use symbols for metadata that must not clash with user keys; the language uses well-known symbols to make protocols extensible without new syntax.
The classic mistakes:
- Symbols for privacy. They’re non-enumerable, not private —
Object.getOwnPropertySymbolsand Reflect expose them. Real privacy is#fieldsor closures. - Global registry confusion.
Symbol.for('x')shares across realms (intentional);Symbol('x')is unique per call. Mixing them up shares what should be private or splits what should be shared. - JSON invisibility. Symbol-keyed properties vanish in
JSON.stringifyand spread of expectations — fine for metadata, fatal for data. Keep data in string keys. - Overriding well-known symbols carelessly. A broken
Symbol.iteratorortoPrimitivecorrupts coercion, spreading and logging in baffling ways. Implement protocols exactly. - Debugging obscurity. Symbol keys print opaquely; overuse makes state inspection painful. Reserve symbols for framework/metadata roles, not everyday fields.
- Assuming cross-realm identity. Symbols (non-registry) differ across iframes/realms — protocol checks spanning realms need the registry or string fallbacks.
- Polyfill gaps. Well-known symbol behaviour in older engines needs careful polyfilling; protocol support is engine-deep, not syntax sugar.
How to use them: symbols for non-colliding metadata and protocol implementation; strings for data; registry for deliberate cross-realm sharing. Small primitive, load-bearing in framework and library design.