Adding a property to an object you do not own is a risky move: if one library writes obj.id and another does the same, the second wipes out the first without a sound.
The symbol settles that at the root, by handing out a key nobody else can produce by accident.
Definition
A Symbol is a unique, immutable primitive value created by calling Symbol(). Two symbols are never equal, even when created with the same description.
const a = Symbol("id");
const b = Symbol("id");
console.log(a === b); // false
console.log(a.description); // "id"
console.log(typeof a); // "symbol"The description only serves debugging: it shows up in messages and in the console, without ever taking part in the comparison. That is what separates a symbol from a string, where two identical values are the same key.
A discreet property key
A symbol is used as a key inside brackets. The property exists and reads back normally, yet it escapes the ordinary walks.
const INTERNAL = Symbol("internal");
const account = { name: "Ada", [INTERNAL]: "token" };
console.log(Object.keys(account)); // [ 'name' ]
console.log(JSON.stringify(account)); // {"name":"Ada"}
console.log(account[INTERNAL]); // "token"Neither Object.keys, nor an in loop, nor JSON.stringify sees that key. The data travels with the object without polluting its public shape, which is exactly what libraries are after.
The well-known symbols
The language reserves a few symbols for plugging an object into its own machinery. The most useful is Symbol.iterator, which makes an object walkable by of and unfoldable by Spread (...).
const week = {
days: ["monday", "tuesday", "wednesday"],
[Symbol.iterator]() {
let i = 0;
const { days } = this;
return {
next: () =>
i < days.length
? { value: days[i++], done: false }
: { value: undefined, done: true },
};
},
};
for (const day of week) console.log(day);
console.log([...week]); // [ 'monday', 'tuesday', 'wednesday' ]Frequently asked questions
Why does new Symbol() throw?
Because a symbol is a primitive, just like a number or a boolean, and not an object to be constructed. The new throws a TypeError saying Symbol is not a constructor. The function is called directly instead.
Is a symbol-keyed property genuinely private?
No, it is discreet. Object.getOwnPropertySymbols lists every one of them, and whoever holds the symbol reads the value. For real confidentiality, the private fields of a class, prefixed with a hash, are what to use.
What is Symbol.for for?
For sharing one symbol across separate pieces of code. Symbol.for("app.id") looks a global registry up and always hands back the same value, where Symbol("app.id") creates a new one on every call. Keep it for cases where sharing is intended.