Quiz

What are `Symbol`s used for in JavaScript?

Topics
JavaScript

TL;DR

Symbols are primitive values commonly used as collision-resistant object property keys and protocol hooks such as Symbol.iterator. Each call to Symbol(description) creates a distinct value, while Symbol.for(key) uses a shared registry. Symbol-keyed properties are skipped by for...in, Object.keys(), and JSON serialization, but they are not private: code can retrieve them with Object.getOwnPropertySymbols() or Reflect.ownKeys().

let sym1 = Symbol();
let sym2 = Symbol('myKey');
console.log(typeof sym1); // "symbol"
console.log(sym1 === sym2); // false, because each symbol is unique
let obj = {};
let sym = Symbol('uniqueKey');
obj[sym] = 'value';
console.log(obj[sym]); // "value"

Note: The Symbol() function must be called without the new keyword. It is not exactly a constructor because it can only be called as a function instead of with new Symbol().


Symbols in JavaScript

Symbols in JavaScript are a unique and immutable data type used primarily for object property keys to avoid name collisions.

Key characteristics

  • Uniqueness: Each Symbol value is unique, even if they have the same description.
  • Immutability: Symbol values are immutable, meaning their value cannot be changed.
  • Non-enumerable: Symbol properties are not included in for...in loops or Object.keys().

Creating Symbols

Symbols can be created using the Symbol() function:

const sym1 = Symbol();
const sym2 = Symbol('uniqueKey');
console.log(typeof sym1); // "symbol"
console.log(sym1 === sym2); // false, because each symbol is unique

The Symbol(..) function must be called without the new keyword.

Using Symbols as object property keys

Symbols can be used to add properties to an object without risk of name collision:

const obj = {};
const sym = Symbol('uniqueKey');
obj[sym] = 'value';
console.log(obj[sym]); // "value"

Symbols are not enumerable

  • Symbol properties are not included in for...in loops or Object.keys().
  • This can hide implementation details from ordinary string-key iteration, but it does not provide privacy.
  • Use Object.getOwnPropertySymbols(obj) to get all symbol properties on an object.
const mySymbol = Symbol('privateProperty');
const obj = {
name: 'John',
[mySymbol]: 42,
};
console.log(Object.keys(obj)); // Output: ['name']
console.log(obj[mySymbol]); // Output: 42

Global Symbol registry

You can create global Symbols using Symbol.for('key'), which creates a new Symbol in the global registry if it doesn't exist, or returns the existing one. This allows you to reuse Symbols across different parts of your code base or even across different code bases.

const globalSym1 = Symbol.for('globalKey');
const globalSym2 = Symbol.for('globalKey');
console.log(globalSym1 === globalSym2); // true
const key = Symbol.keyFor(globalSym1);
console.log(key); // "globalKey"

Well-known Symbols

JavaScript includes several built-in Symbols, referred to as well-known Symbols.

  • Symbol.iterator: Defines the default iterator for an object.
  • Symbol.toStringTag: Used to create a string description for an object.
  • Symbol.hasInstance: Used to determine if an object is an instance of a constructor.

Symbol.iterator

let iterable = {
[Symbol.iterator]() {
let step = 0;
return {
next() {
step++;
if (step <= 5) {
return { value: step, done: false };
}
return { done: true };
},
};
},
};
for (let value of iterable) {
console.log(value); // 1, 2, 3, 4, 5
}

Symbol.toStringTag

let myObj = {
[Symbol.toStringTag]: 'MyCustomObject',
};
console.log(Object.prototype.toString.call(myObj)); // "[object MyCustomObject]"

Summary

Symbols are useful for collision-resistant object properties and for customizing built-in protocols. Symbol-keyed properties are not necessarily non-enumerable—their property descriptor can have enumerable: true—but string-key enumeration APIs still skip them. Use class #private fields or closures when true access control is required.

Further reading

Exercises

Check your understanding
Beta
Check your understanding Exercise
Check your understanding Exercise

What does this code log?

const first = Symbol('id');
const second = Symbol('id');
const sharedA = Symbol.for('shared');
const sharedB = Symbol.for('shared');
const object = { [first]: 1, visible: 2 };
console.log(first === second, sharedA === sharedB, Object.keys(object).length);