By the end of this lesson, you will be able to create JavaScript Symbols and use them as collision-proof property keys for hidden metadata, private-ish object fields, and protocol hooks.
What it is
A Symbol is a primitive value created with Symbol(description). The description is only a label for debugging; it does not make the symbol equal to another symbol with the same description. Each call to Symbol() returns a new unique value, so two symbols can never collide unless you intentionally reuse the same symbol or use the global symbol registry with Symbol.for().
Think of a symbol as a secret key name. If you store data under a symbol key, ordinary string keys cannot accidentally read or overwrite it. Symbols are often used for object property keys, but they can also be used as values, map keys, or protocol markers such as Symbol.iterator.
Why it matters
- Prevents accidental property-name collisions when libraries add metadata to objects.
- Creates hidden object properties that are skipped by
Object.keys(),for...in, andJSON.stringify(). - Supports JavaScript protocols, such as iteration with
Symbol.iteratorand primitive conversion withSymbol.toPrimitive. - Provides a lightweight way to represent unique enum-like values without exposing them as strings.
Syntax or steps
- Create a unique symbol:
const id = Symbol("id");. - Use it as a computed property key:
{ [id]: value }. - Read it with bracket notation:
object[id]. - If you need the same symbol across modules or realms, use
Symbol.for("name")instead ofSymbol("name").
Example
const id = Symbol("id");
const role = Symbol("role");
const user = {
name: "Ada",
[id]: 42,
[role]: "admin"
};
console.log(user.name); // Ada
console.log(user[id]); // 42
console.log(Object.keys(user)); // ["name"]
console.log(Object.getOwnPropertySymbols(user));
// [ Symbol(id), Symbol(role) ]
const shared = Symbol.for("shared");
const alsoShared = Symbol.for("shared");
console.log(shared === alsoShared); // true
const unique = Symbol("shared");
console.log(shared === unique); // false
The object has one normal string property, name, and two symbol-keyed properties. Object.keys() returns only string keys, so the symbol properties are hidden from ordinary enumeration. Object.getOwnPropertySymbols() can reveal them if you have the symbol values. Symbol.for() looks up or creates a symbol in a global registry, so the same description returns the same symbol. Symbol() always creates a new symbol, even if the description matches.
Common mistakes
- Using
new Symbol(). Symbols are primitives and are not constructed withnew; useSymbol(). - Assuming the description makes symbols equal.
Symbol("id") === Symbol("id")isfalse. - Expecting symbol properties to appear in
JSON.stringify(). They are omitted, so use a normal string property if the data must be serialized. - Using
Symbol.for()when you need true uniqueness. The registry is shared, so two unrelated libraries using the same key can collide.
When to use it
Use symbols when you need a property key or marker that should not collide with ordinary string keys. Use strings when the key is part of the public data shape, needs to be serialized, or should be easy to inspect.
| Need | Use | Why |
|---|---|---|
| Hidden metadata on an object | Symbol() | Not enumerated by normal string-key APIs. |
| Shared marker across modules | Symbol.for() | Same registry key returns the same symbol. |
| Public JSON field | String key | JSON supports string keys, not symbol keys. |
| Custom iteration | Symbol.iterator | JavaScript looks for this built-in symbol. |
Practice
Guided exercise: create a symbol called secret, store it on an object with a visible name property, then print the object's string keys and symbol keys.
const secret = Symbol("secret");
const account = { name: "main", [secret]: "1234" };
console.log(Object.keys(account));
console.log(Object.getOwnPropertySymbols(account));
Expected output: ["name"], then an array containing Symbol(secret).
Challenge: create two symbols with the description "token" and prove they are different. Then create two Symbol.for("token") values and prove they are the same.
Quick check
Question: Why does Symbol("id") === Symbol("id") return false?
Answer: Because each call to Symbol() creates a new unique primitive. The description is only a debugging label, not an identity.
Summary
Symbols give JavaScript a way to create unique, non-colliding identifiers, especially useful as hidden property keys and protocol hooks. Use Symbol() for uniqueness and Symbol.for() only when you intentionally want a shared symbol.