JavaScript Symbol


In JavaScript, Symbols are a relatively recent addition that provide a unique and immutable primitive value. They are primarily used to create unique property keys for objects. Symbols can help avoid naming collisions and are ideal when you want to create private properties in objects. This blog post will walk you through the basics of JavaScript Symbols, their syntax, use cases, and how to work with them effectively.


1. What is a JavaScript Symbol?

A Symbol is a primitive data type introduced in ECMAScript 6 (ES6). Each Symbol is guaranteed to be unique, even if two Symbols are created with the same description. They are often used to create unique keys for object properties, ensuring that property names do not conflict with others in objects.

Basic Syntax of Symbol:

let sym = Symbol();

This creates a new unique Symbol that can be used as a property key for objects.


2. Creating a Symbol

A Symbol is created by calling the Symbol() function. Optionally, you can pass a description to the symbol. This description is mainly used for debugging purposes and does not affect the uniqueness of the Symbol.

Example: Creating a Symbol

let symbol1 = Symbol();
let symbol2 = Symbol('description');

console.log(symbol1);  // Output: Symbol()
console.log(symbol2);  // Output: Symbol(description)

Explanation:

  • symbol1 is a symbol with no description, while symbol2 has the description 'description'.
  • Even though the descriptions are identical, symbol1 and symbol2 are still distinct and unique.

3. Using Symbols as Object Property Keys

One of the most common uses of Symbols is to create unique keys for object properties. Since Symbols are unique, you can ensure there are no collisions between object properties even if other code is using the same property name.

Example: Using Symbols in Objects

let sym1 = Symbol('name');
let sym2 = Symbol('age');

let person = {
  [sym1]: 'John Doe',
  [sym2]: 30
};

console.log(person[sym1]);  // Output: John Doe
console.log(person[sym2]);  // Output: 30

Explanation:

  • sym1 and sym2 are unique symbols used as keys in the person object.
  • These properties won't conflict with any other properties in the object, as the keys are guaranteed to be unique.

4. Symbols and Object Properties

Symbols are not enumerable in for...in loops or Object.keys() methods. This means that you cannot accidentally access them when iterating over an object's properties using these methods. They are mainly used to add "private" properties to objects that are not directly accessible unless you explicitly reference the Symbol.

Example: Symbols and Enumeration

let symbol1 = Symbol('id');
let symbol2 = Symbol('id');

let user = {
  [symbol1]: 1234,
  [symbol2]: 5678,
  name: "Alice"
};

for (let key in user) {
  console.log(key);  // Output: name (symbol properties are not logged)
}

console.log(Object.keys(user));  // Output: ['name'] (symbol properties are not included)

Explanation:

  • The for...in loop and Object.keys() only include the regular properties, excluding Symbol-based properties.
  • The Symbol properties can only be accessed if you specifically use the Symbol.

5. Global Symbols and the Symbol.for() Method

In addition to creating individual Symbols, JavaScript provides a global registry of Symbols. This registry allows you to reuse Symbols by their description across different parts of your program.

Symbol.for() Method

The Symbol.for() method checks if a Symbol with a given description exists in the global registry. If it does, it returns the existing Symbol; if not, it creates a new Symbol.

Example: Using Symbol.for()

let globalSymbol1 = Symbol.for('uniqueSymbol');
let globalSymbol2 = Symbol.for('uniqueSymbol');

console.log(globalSymbol1 === globalSymbol2);  // Output: true

Explanation:

  • Symbol.for('uniqueSymbol') creates a globally unique Symbol. Even though you create it in different places, it will always refer to the same Symbol if the description is the same.
  • globalSymbol1 and globalSymbol2 refer to the same global Symbol because they share the same description 'uniqueSymbol'.

6. Retrieving the Description of a Symbol

You can retrieve the description of a Symbol using the description property. This is useful for debugging or logging purposes, as Symbols themselves do not convert to strings directly.

Example: Getting Symbol Description

let symbol = Symbol('mySymbol');
console.log(symbol.description);  // Output: mySymbol

Explanation:

  • The description property gives you the human-readable description passed when creating the Symbol, but the Symbol itself remains unique and cannot be directly used as a string.

7. Well-Known Symbols in JavaScript

JavaScript also provides several built-in "well-known" Symbols that have specific behavior in the language. These Symbols allow you to customize certain operations like object iteration, type conversion, and more.

Some examples of well-known Symbols include:

  • Symbol.iterator: Used to define the default iterator for an object (enables for...of loop).
  • Symbol.toStringTag: Used to define the string description of an object when Object.prototype.toString() is called.
  • Symbol.hasInstance: Determines if an object is an instance of a class (used in instanceof).

Example: Using Symbol.iterator

let iterable = {
  items: ['a', 'b', 'c'],
  [Symbol.iterator]: function() {
    let index = 0;
    let items = this.items;
    return {
      next: function() {
        if (index < items.length) {
          return { value: items[index++], done: false };
        } else {
          return { value: undefined, done: true };
        }
      }
    };
  }
};

for (let item of iterable) {
  console.log(item);  // Output: a, b, c
}

Explanation:

  • The object iterable uses Symbol.iterator to define how it should be iterated over in a for...of loop.
  • The next() method controls the iteration and provides the values to be accessed.

8. Best Practices for Using Symbols

  • Avoid Overusing Symbols: Symbols are useful when you need unique property names or for implementing private properties. However, overusing them may lead to more complexity and hard-to-maintain code.
  • Use Global Symbols Wisely: Global Symbols created with Symbol.for() should be used when you need to ensure consistency across different parts of your codebase.
  • Use Well-Known Symbols: Leverage well-known Symbols like Symbol.iterator to enhance the behavior of objects and customize built-in JavaScript operations.