Symbols
JavaScript's unique identifier primitive — Symbol(), well-known symbols, Symbol.iterator, Symbol.for, and using symbols as private-ish object keys.
What it is, in plain English: A Symbol is a primitive value that is guaranteed to be unique — no two Symbols are ever equal, even if created with the same description. Symbols are used as object property keys that can't accidentally collide with other keys. Well-known Symbols (Symbol.iterator, Symbol.toPrimitive, Symbol.hasInstance...) let you hook into JavaScript's built-in behaviours.
Unique keys that never collide
JavaScript objects can have properties keyed by strings or Symbols.
String keys can clash — if two pieces of code both add a "id" property,
one overwrites the other. A Symbol is guaranteed unique — even two Symbols
created with the same description are different values.
Creating Symbols
const sym1 = Symbol("description"); // the description is just for debugging
const sym2 = Symbol("description");
console.log(sym1 === sym2); // false — ALWAYS different
console.log(typeof sym1); // "symbol"
console.log(sym1.description); // "description" — the label you gave it
console.log(sym1.toString()); // "Symbol(description)"
// You cannot new Symbol() — it's not a constructor:
// new Symbol() // TypeError: Symbol is not a constructorUsing Symbols as object keys
const SECRET = Symbol("secret");
const ID = Symbol("id");
const user = {
name: "Alice",
[SECRET]: "password123", // computed property syntax for Symbol keys
[ID]: 42,
};
// Access with the Symbol reference:
console.log(user[SECRET]); // "password123"
console.log(user[ID]); // 42
console.log(user.name); // "Alice"
// Symbol keys are hidden from normal enumeration:
console.log(Object.keys(user)); // ["name"] — no symbols
console.log(JSON.stringify(user)); // {"name":"Alice"} — symbols dropped
// You need the actual Symbol reference to access it:
// user["secret"] → undefined (that's a string key, not the Symbol)The main uses of Symbols
// 1. Unique constants (no collision with other code):
const STATUS_PENDING = Symbol("pending");
const STATUS_DONE = Symbol("done");
const STATUS_FAILED = Symbol("failed");
function process(task) {
task.status = STATUS_PENDING;
// ... do work ...
task.status = STATUS_DONE;
}
// 2. Adding metadata to objects you don't own:
const META = Symbol("meta");
const thirdPartyObject = { data: [1, 2, 3] };
thirdPartyObject[META] = { addedAt: Date.now() }; // won't clash with any existing key
// 3. The global registry — share a Symbol across modules:
const shared1 = Symbol.for("app.user.id");
const shared2 = Symbol.for("app.user.id");
console.log(shared1 === shared2); // true — same symbol from registry
Try It Yourself
Official Sources
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.