Symbols in JavaScript

Introduction

A Symbol is a unique primitive value—ideal for property keys that should not collide with string keys like "name" or "id". Well-known symbols (Symbol.iterator, Symbol.toStringTag, etc.) let libraries hook into language behavior without polluting public APIs.

Prerequisites

Create Symbols

javascript
// Each Symbol() is unique
const a = Symbol("label");
const b = Symbol("label");
 
console.log(a === b);
console.log(typeof a);

Optional description helps debugging only—not equality.

Symbol Keys on Objects

javascript
// Hidden-ish metadata key
const id = Symbol("id");
const user = {
  name: "Ada",
  [id]: 9001,
};
 
console.log(user.name);
console.log(user[id]);

Symbol keys are skipped by Object.keys and for...in unless you use Object.getOwnPropertySymbols.

Global Symbol Registry

javascript
// Same symbol across files/realms
const shared = Symbol.for("app.config");
const again = Symbol.for("app.config");
 
console.log(shared === again);
console.log(Symbol.keyFor(shared));

Well-Known Symbols

javascript
// Customize iteration
const collection = {
  items: [1, 2, 3],
  [Symbol.iterator]() {
    let i = 0;
    const data = this.items;
    return {
      next() {
        if (i < data.length) return { value: data[i++], done: false };
        return { done: true };
      },
    };
  },
};
 
for (const x of collection) {
  console.log(x);
}

Other examples: Symbol.toStringTag, Symbol.hasInstance.

Symbol.iterator on Built-ins

Arrays, strings, Map, and Set already implement it—custom objects opt in as above.

Mini Example: Plugin Hook Key

javascript
const HOOK = Symbol("plugin.hook");
 
const plugins = [
  {
    name: "logger",
    [HOOK](ctx) {
      console.log("before", ctx.step);
    },
  },
];
 
function run(step) {
  const ctx = { step };
  for (const plugin of plugins) {
    const fn = plugin[HOOK];
    if (fn) fn(ctx);
  }
}
 
run("save");

FAQ

Symbol vs string key?

Symbols avoid name clashes; strings are normal public fields.

JSON and Symbols?

JSON.stringify ignores symbol-keyed properties.

Can I coerce Symbol to string?

String(sym) works; implicit + concatenation throws.

What comes next?

Tagged template literals.