Symbols in JavaScript: Beyond Strings and Numbers for Unique Identity

A Comprehensive Guide to Using Symbols as Immutable Property Keys

Photo by Rahul Mishra on Unsplash

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 returns undefined.

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

--

--

राहुल मिश्रा
राहुल मिश्रा

No responses yet