Use unique Symbol values as collision-free object keys and customize built-in object behavior.
Symbol factory.Symbol.for() registry.Symbol using new because symbols are primitives rather than constructible classes.JSON.stringify and loops.Unique primitiveGenerates unique primitive values on every function call.
const a = Symbol("id");
const b = Symbol("id");
console.log(a === b); // falseDescription debuggingAssigns debugging descriptions which do not affect symbol identity.
const s = Symbol("label");
console.log(s.description); // "label"Primitives typeReturns symbol from typeof checks.
console.log(typeof Symbol()); // "symbol"Symbol propertiesAssigns properties using symbol keys to guarantee collision-free attributes.
const KEY = Symbol("key");
const user = { name: "Ada", [KEY]: 42 };Bracket accessesQueries symbol keys using bracket notation rather than dot accesses.
console.log(user[KEY]); // 42Descriptors listingLists symbol keys using Reflect methods.
Reflect.ownKeys(user); // ["name", Symbol(key)]Loop exclusionExcludes symbol properties from keys listings and loops.
const obj = { name: "A", [Symbol("id")]: 1 };
console.log(Object.keys(obj)); // ["name"]JSON serializationOmits symbol keys during stringify operations.
console.log(JSON.stringify(obj)); // '{"name":"A"}'Explicit queriesQueries symbols using specialized getOwnPropertySymbols calls.
Object.getOwnPropertySymbols(obj); // [Symbol(id)]Symbol.iteratorEnables custom iteration behavior on plain objects.
const range = {
[Symbol.iterator]() {
return { next: () => ({ done: true }) };
}
};
[...range];Symbol.toPrimitiveEnforces custom conversions into primitives.
const cash = {
amount: 50,
[Symbol.toPrimitive](hint) {
return hint === "string" ? `$${this.amount}` : this.amount;
}
};Symbol.hasInstanceOverrides instanceof check mechanics for target configurations.
class Even {
static [Symbol.hasInstance](num) {
return num % 2 === 0;
}
}
console.log(4 instanceof Even); // trueSymbol.forRegisters shared symbol instances in a global scope index.
const a = Symbol.for("app.id");
const b = Symbol.for("app.id");
console.log(a === b); // trueSymbol.keyForReturns registered index strings matching active shared symbols.
console.log(Symbol.keyFor(a)); // "app.id"A Symbol represents a unique primitive value that avoids key collisions. It allows adding custom properties to objects safely. Well-known symbols hook directly into core operations.
By design, serialization routines like JSON.stringify only process string-keyed properties. This restriction lets symbols act as hidden metadata boundaries. They remain safe from accidental log outputs.
The Symbol() factory guarantees unique instances per call. The Symbol.for() method queries the global symbol registry first. It retrieves matching instances if they already exist.
Well-known symbols are built-in hooks like Symbol.iterator. They enable objects to customize default language behaviors. This includes customizing string casting and enabling iteration protocols.
No, they are not. The Object.getOwnPropertySymbols() method returns all symbol keys from target instances. Anyone holding reference handles can access the property values.
Private Metadata Attachment
Uses local Symbols to attach internal metadata to objects safely, preventing collision with user properties.
function createMetadataSystem() {
const METADATA = Symbol("internal_metadata");
function setMetadata(obj, data) {
obj[METADATA] = data;
}
function getMetadata(obj) {
return obj[METADATA];
}
return { setMetadata, getMetadata };
}Symbol properties guarantee collision-free attributes, which makes them ideal for attaching internal metadata safely.