Suggest an editImprove this articleRefine the answer for “Symbol in JavaScript”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`Symbol` is a primitive type introduced in ES6 whose every value is unique and immutable, even when the descriptions match.** Its main use is as a unique object property key: such a key is guaranteed not to clash with anything and does not show up in `for...in`, `Object.keys()` or `JSON.stringify()`, which makes it a good fit for "hidden" internal fields and metadata. Separately there are built-in well-known symbols (`Symbol.iterator`, `Symbol.toPrimitive` and others) that let you change the behaviour of standard language operations. ```javascript const id1 = Symbol('user'); const id2 = Symbol('user'); console.log(id1 === id2); // false, every Symbol is unique const ID = Symbol('id'); const user = { name: 'Tim', [ID]: 1 }; console.log(Object.keys(user)); // ["name"] console.log(user[ID]); // 1 ``` **Key point:** `Symbol()` always produces a new unique value, while `Symbol.for()` takes a shared one from the global registry.Shown above the full answer for quick recall.Answer (EN)Image**`Symbol` is a primitive data type introduced in ES6 that represents a unique and immutable value.** Every call to `Symbol()` creates a new unique value even when the description is the same, which is why symbols are used as property keys that are guaranteed not to clash with anything. ## Theory ### TL;DR - `Symbol()` creates a unique immutable value; two symbols with the same description are not equal. - The main use is unique object property keys with no risk of a name collision. - Symbol keys do not appear in `for...in`, `Object.keys()` or `JSON.stringify()`. - They can still be retrieved with `Object.getOwnPropertySymbols()`, so they are "hidden" rather than private fields. - Well-known symbols (`Symbol.iterator`, `Symbol.toPrimitive` and others) change the behaviour of built-in operations. - `Symbol.for(key)` takes a symbol from the global registry, so such symbols can be compared across modules. ### Quick example ```javascript const id1 = Symbol('user'); const id2 = Symbol('user'); console.log(id1 === id2); // false const ID = Symbol('id'); const user = { name: 'Tim', [ID]: 1 }; console.log(user[ID]); // 1 console.log(Object.keys(user)); // ["name"] ``` ### What `Symbol` is and why it is unique A symbol is created by calling the function: ```javascript const id = Symbol(); ``` Every call to `Symbol()` creates **a new unique value**, even when the descriptions match: ```javascript const id1 = Symbol('user'); const id2 = Symbol('user'); console.log(id1 === id2); // false ``` Although both symbols carry the same description `'user'`, they are **different**. The description is only a debugging hint, it has no effect on equality. ### What `Symbol` is for Symbols are most often used as **unique object property keys**, to avoid name collisions. Ordinary keys do collide: ```javascript const user = { name: 'Tim' }; user.id = 1; user.id = 2; // overwritten ``` With a symbol that cannot happen: ```javascript const ID = Symbol('id'); const user = { name: 'Tim', [ID]: 1 }; console.log(user[ID]); // 1 ``` Now the `[ID]` property is: - unique; - invisible during ordinary property enumeration. ### Symbols do not take part in ordinary enumeration ```javascript for (let key in user) { console.log(key); } // "name" ``` ```javascript console.log(Object.keys(user)); // ["name"] ``` A symbol does not show up in `for...in`, `Object.keys()` or `JSON.stringify()`. But it can still be obtained: ```javascript console.log(Object.getOwnPropertySymbols(user)); // [Symbol(id)] ``` ### Well-known symbols JavaScript has **built-in symbols**, special values that let you change the behaviour of built-in operations. | Symbol | What it is used for | | --- | --- | | `Symbol.iterator` | makes an object iterable (`for...of`) | | `Symbol.toPrimitive` | customises conversion to a primitive | | `Symbol.toStringTag` | sets the string returned by `Object.prototype.toString` | | `Symbol.hasInstance` | overrides the behaviour of `instanceof` | | `Symbol.asyncIterator` | enables `for await...of` | An example with `Symbol.iterator`: ```javascript const arr = [1, 2, 3]; const iterator = arr[Symbol.iterator](); console.log(iterator.next()); // { value: 1, done: false } ``` It is `Symbol.iterator` that makes an array "iterable", and that is exactly what makes `for...of` and spread (`...arr`) work. An example with `Symbol.toPrimitive`: ```javascript const user = { name: 'Tim', age: 25, [Symbol.toPrimitive](hint) { return hint === 'string' ? this.name : this.age; } }; console.log(String(user)); // "Tim" console.log(+user); // 25 ``` The symbol lets you control how an object is converted to a number or a string. ### The global registry and "hidden" keys Sometimes you need a symbol with a given name to be **the same** symbol everywhere. That is what the **global symbol registry** is for: ```javascript const id1 = Symbol.for('user'); const id2 = Symbol.for('user'); console.log(id1 === id2); // true ``` `Symbol.for(key)` looks the symbol up in the global registry and, when it is not found, creates a new one and stores it under that key. `Symbol.keyFor()` does the reverse: ```javascript Symbol.keyFor(id1); // 'user' ``` Symbols are a good fit for: - creating "private" properties in libraries and classes; - extending objects you do not own without risking their data; - metadata, for example in frameworks or an ORM. An example of use in a class: ```javascript const _id = Symbol('id'); class User { constructor(name) { this.name = name; this[_id] = Math.random(); } } const user = new User('Tim'); console.log(user); // { name: 'Tim', [Symbol(id)]: 0.123... } console.log(Object.keys(user)); // ["name"] ``` The symbol makes the `_id` field **invisible to ordinary iteration** and protects it from accidental overwriting. The summary table: | Property | Description | | --- | --- | | Type | a primitive | | Uniqueness | every `Symbol()` is unique | | Used as | a key for object properties | | Not visible | in `for...in`, `Object.keys()`, `JSON.stringify()` | | Global registry | through `Symbol.for()` and `Symbol.keyFor()` | | Examples of built-in symbols | `Symbol.iterator`, `Symbol.toPrimitive`, `Symbol.hasInstance` and others | ### Common mistakes - Expecting `Symbol('id') === Symbol('id')`. The description does not make symbols equal, every call produces a new symbol. - Treating symbol fields as genuinely private. They are visible through `Object.getOwnPropertySymbols()` and `Reflect.ownKeys()`; for real privacy there are `#privateField` declarations. - Calling `Symbol` with `new`. That is a `TypeError`, because `Symbol` is a primitive, not a constructor. - Concatenating a symbol with a string (`'id: ' + sym`). The implicit conversion throws a `TypeError`, you need `String(sym)` or `sym.description`. - Confusing `Symbol()` with `Symbol.for()`. The first is always unique, the second shares a value through the global registry. - Counting on symbol fields to survive `JSON.stringify()` or `structuredClone()`. They simply disappear.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.