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
Symbol
is unique and immutable, meaning that two symbols will never be equal. - Non-enumerability: Properties of type
Symbol
are not enumerable by default, meaning they will not 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
, you simply use the constructor function Symbol()
without the new
keyword.
const mySymbol = Symbol();
In this example, id
is a new unique and immutable Symbol
.
Description of a Symbol
It is 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.
The description of a symbol 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 toString()
method of the symbol.
Uniqueness of Symbols
The main characteristic of symbols is that 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,
symbol1
andsymbol2
are two distinct symbols, even though both have the same description.- This means that each call to
Symbol()
has produced a unique identifier.
Uses of the Symbol Type
Preventing Property Overwrites
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 have defined a literal object, with the
property
having the value value 1 - Subsequently, someone accidentally overwrites it with the value value 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
Symbol
calledsymbolProperty
and use it as a key for a property in theobject
. - Then, in another part of the code, we create a new
Symbol
calledanotherSymbol
. Even though both symbols have the same description (‘property’), they are different from each other. - Therefore, when we assign
'value 2'
toobject[anotherSymbol]
, it does not overwrite the value ofobject[symbolProperty]
, but rather creates a new property.
Symbol 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 the iterator of an object. |
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 you to create a global symbol that is accessible throughout the execution environment.
If you try to create a symbol with the same key, it will return the same symbol. In other words, 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 === globalSymbol2
returnstrue
.
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 unique at the global level, and if a symbol with that key already exists, it will return the same symbol.
Practical Examples
Pseudo-private Properties
JavaScript introduced the concept of private properties in 2022. But previously, 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,
privateProperty
is a symbol that acts as a “pseudo”-private property of theMyClass
.- We could only access the property if we have the symbol accessible.
Avoiding Property Name Collisions
Symbols can be useful for avoiding 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.
Iterating Over Properties
Although properties of type Symbol
are not enumerable by default. In other words, they are not accessible through standard methods like Object.keys()
or for...in
.
However, we can use the Object.getOwnPropertySymbols()
method to get all the properties of type Symbol
from 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 Symbol
s used as keys in the myObject
.