A Symbol is a primitive data type in JavaScript that represents a unique and persistent identifier.
Unlike other primitive data types like number, string, or boolean, symbols are always unique.
This means that every time you create a new symbol, you get a distinct value that cannot be duplicated.
- Uniqueness: Each
Symbolis unique and immutable, meaning two symbols will never be equal. - Non-enumerability: Properties of type
Symbolare not enumerable by default, meaning they won’t appear in operations likefor...in.
The Symbol data type is one of the most advanced and least understood types, but it offers useful features in certain contexts.
Creating a Symbol
To create a Symbol, we simply use the constructor function Symbol() without the new keyword.
const mySymbol = Symbol();
In this example, id is a new unique and immutable Symbol.
Symbol Description
It’s possible to provide an optional description when creating a Symbol. This description can be useful for identifying the purpose of the Symbol.
const name = Symbol('Symbol name');
In this case, name is a Symbol with an optional description.
A symbol’s description does not affect its uniqueness, but it can be useful for debugging.
let symbol = Symbol('my symbol');
console.log(symbol.toString()); // Prints: Symbol(my symbol)
The description can be obtained through the symbol’s toString() method.
Uniqueness of Symbols
The main characteristic of symbols is their value is always unique and different from all others (even if two symbols have the same description).
let symbol1 = Symbol('description');
let symbol2 = Symbol('description');
console.log(symbol1 === symbol2); // Prints: false
In the previous example,
symbol1andsymbol2are two distinct symbols, even though they both have the same description.- This means each call to
Symbol()has produced a unique identifier.
Uses of the Symbol Type
Preventing Property Overwriting
One of the most common uses of Symbols is as keys for object properties, to prevent them from being accidentally overwritten.
Let’s see it with an example.
let object = {
property: 'value 1'
};
// Later, another part of the code adds a property with the same name:
object.property = 'value 2';
console.log(object.property); // Prints: 'value 2'
In this case,
- We defined an object literal, with the
propertyhaving the value valor 1 - Later, someone by mistake overwrites it with the value valor 2
// We create a Symbol with a description
let symbolProperty = Symbol('property');
let object = {};
// We use the Symbol as a key for the property
object[symbolProperty] = 'value 1';
// Later, we try to add another property with the same name,
// but since we used a Symbol, there is no overwrite
let anotherSymbol = Symbol('property');
object[anotherSymbol] = 'value 2';
// We access the properties using the Symbols
console.log(object[symbolProperty]); // Prints: 'value 1'
console.log(object[anotherSymbol]); // Prints: 'value 2'
In this example,
- We create a
SymbolcalledsymbolPropertyand use it as a key for a property in theobjectobject. - Then, in another part of the code, we create a new
SymbolcalledanotherSymbol. Even though both symbols have the same description (‘propiedad’), they are different from each other. - Therefore, when we assign
'value 2'toobject[anotherSymbol], it does not overwrite the value ofobject[symbolProperty], but instead creates a new property.
Symbolic Methods and Properties
JavaScript includes several built-in symbols that are used as keys for special methods and properties. These symbols are defined in the Symbol object.
| Symbol | Description |
|---|---|
Symbol.iterator | This symbol is used to define an object’s iterator. |
Symbol.toStringTag | This symbol is used to define the value of the Symbol.toStringTag property |
Symbol.toStringTag is used to customize the result of the Object.prototype.toString() method.
Global Symbols with Symbol.for()
Another different use of Symbols occurs with the Symbol.for() method. This allows creating a global symbol that is accessible throughout the runtime environment.
If you try to create a symbol with the same key, it returns the same symbol. That is, Symbol.for() maintains a kind of “global registry” of symbols.
const globalSymbol1 = Symbol.for('mySymbol');
const globalSymbol2 = Symbol.for('mySymbol');
console.log(globalSymbol1 === globalSymbol2); // Prints: true
In this case,
- We create a global symbol with the key
'mySymbol'usingSymbol.for('mySymbol'). - When calling
Symbol.for('mySymbol')again, JavaScript does not create a new symbol. - Instead, it looks in the global symbol registry and, if a symbol with that key (like
'mySymbol') already exists, it returns the same symbol. - Since they are the same symbol, the comparison
globalSymbol1 === globalSymbol2returnstrue.
This is different from creating symbols with Symbol(), which always generates a unique symbol, even if you use the same description.
In contrast, Symbol.for() ensures that the created symbol is globally unique, and if a symbol with that key already exists, it returns the same symbol.
Practical Examples
Pseudo-private Properties
JavaScript incorporated the concept of private properties in 2022. But before that, it was common to use Symbols as a “partial” solution for encapsulation.
const privateProperty = Symbol('private');
class MyClass {
constructor(value) {
this[privateProperty] = value;
}
getValue() {
return this[privateProperty];
}
}
let instance = new MyClass(123);
console.log(instance.getValue()); // Prints: 123
In this example,
privatePropertyis a symbol that acts as a “pseudo”-private property of theMyClassclass.- We could only access the property if we have the symbol accessible.
Avoiding Property Name Collisions
Symbols can be useful to avoid property name collisions in objects, as they are unique and immutable.
const NAME = Symbol('name');
const person = {
[NAME]: 'John',
age: 30,
};
console.log(person[NAME]); // Result: John
In this case, NAME is a Symbol used as a key for a person’s name in the person object.
Property Iteration
Although properties of type Symbol are not enumerable by default. That is, they are not accessible through standard methods like Object.keys() or for...in.
However, we can use the Object.getOwnPropertySymbols() method to get all properties of type Symbol of an object.
const key1 = Symbol('key1');
const key2 = Symbol('key2');
const myObject = {
[key1]: 'Value 1',
[key2]: 'Value 2',
name: 'Object',
};
const symbols = Object.getOwnPropertySymbols(myObject);
console.log(symbols); // Result: [Symbol(key1), Symbol(key2)]
In this example, symbols contains the Symbols used as keys in the myObject object.
