Symbols in JavaScript: Beyond Strings and Numbers for Unique Identity
A Comprehensive Guide to Using Symbols as Immutable Property Keys
In this article we will be going to unfold everything we need to know about Symbol
in JS by answering question like what is symbol? Why we use it? How to create Symbol
? and many more.
What ?
Symbol
is a built-in object whose constructor returns a Symbol
Symbols are primitive data types introduced in ES6.
How?
We create a Symbol by simply calling Symbol() constructor function:
3 Ways to create Symbols in JS:
// Different ways to create symbols:
const SYM = Symbol(); // WAY 1
const SYM2 = Symbol('ID'); // WAY 2 => by passing a description
const SYM3 = Symbol.for('mySecret'); // Way 3 =? By using a key (in this way "
// JS return symbol if exists, otherwise it creates a Symbol for key `mySecret
We can’t call Symbol using new
keyword, because Symbols are primitive data type. And if we try to call Symbol using new
we will get an error:
const SYM = new Symbol(); // Not Allowed
// Error
// Uncaught TypeError: Symbol is not a constructor
How can I see the value of a created Symbol?
Unfortunately, we can’t.
Symbols in JavaScript are unique and don’t have a “value” like other primitive data types.
Symbols are typically used as identifiers rather than holding meaningful values like strings or numbers.
we can only check the description which we pass while creating a symbol.
const sym = Symbol("mySymbolDescription");
console.log(sym.description); // "mySymbolDescription"
// NOTE:// this description is primarily for debugging purposes and doesn't affect the uniqueness or behavior of the symbol itself.
Why?
The sole purpose of Symbol is to create unique values. To understand, you can consider it a function which generates unique id on each call.
No two symbols can be same ever.
console.log(Symbol() == Symbol()); // false
console.log(Symbol() === Symbol()); // false
console.log(Symbol('JS') == Symbol('JS')); // false
console.log(Symbol('JS') === Symbol('JS')); // false
Usage?
A Symbol
value may be used as an identifier for object properties. This is the data type’s only purpose.
Here are the few usage around it:
1. Private Object Properties:
Symbols provide a way to create properties that are not easily accessible from outside the object. This can be useful for encapsulating internal data or behavior within an object
const _mySecretVar = Symbol('this is my secret var');
class Person{
constructor() {
this[_mySecretVar] = '***@#**';
}
}
2. Customizing Object Behavior:
Symbols can be used as keys for methods or properties that allow objects to define custom behavior. For example, using the built-in symbol Symbol.iterator
, you can implement your own iterator for an object.
const myIterable = {
[Symbol.iterator]: function* () {
yield 1;
yield 2;
yield 3;
}
};
// I can run for of loop in this object
for(let val of myIterable) {
console.log(val); // logs 1,2,3
}
3. Preventing Property Collisions:
Symbols can help prevent accidental property name collisions when working with objects. Since symbols are guaranteed to be unique, they can be used as property keys without the risk of conflicts.
const sym = Symbol();
const sym2 = Symbol();
const obj = {
[sym]: 'value'
[sym2]: 'value2'
};
// both sym and sym2 points to different key
4. Hidden Properties
Symbols can be used to create properties on objects that are not enumerable. This means they won’t show up in for...in
loops or when using Object.keys
.
const SYM = Symbol.for('SYM');
const obj = {
[SYM]: 'value',
normalKey: 'visible'
};
for (let key in obj) {
console.log(key); // Only 'normalKey' is printed
}
5. Customizing JSON Serialization:
Symbols can be used to customize the JSON serialization process for objects. You can define a method with the symbol Symbol.toPrimitive
to specify how an object should be converted to a primitive value during JSON serialization.
const customObject = {
[Symbol.toPrimitive](hint) {
if (hint === 'number') {
return 42;
}
if (hint === 'string') {
return 'customObject';
}
return null;
}
};
console.log(Number(customObject)); // 42
console.log(String(customObject)); // "customObject"
Symbols Shared Across Files and Realms:
Sometimes, you might want to share symbols across different parts of your code or even across different JavaScript realms (different instances of the JavaScript engine with their own global scope, like iframes). This is where Symbol.for()
and Symbol.keyFor()
come in.
Symbol.for(key)
allows you to create a globally shared symbol that is stored in a Symbol registry. If a symbol with the same key already exists in the registry, it will be returned. If not, a new symbol will be created and stored.Symbol.keyFor(symbol)
allows you to retrieve the key (a string) associated with a globally shared symbol from the Symbol registry. If the symbol is not in the registry, it returnsundefined
.
Here’s an example to illustrate:
// In one file or scope
const globalSym = Symbol.for("sharedSymbol");
// In another file or scope (can even be in a different realm)
const sameGlobalSym = Symbol.for("sharedSymbol");
console.log(globalSym === sameGlobalSym); // true
console.log(Symbol.keyFor(globalSym)); // "sharedSymbol"
console.log(Symbol.keyFor(sameGlobalSym)); // "sharedSymbol"
Thanks for reading. 😃 For more clarity and understanding please refer to:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol