1. Introduction
In this tutorial, we’ll discuss in detail about a primitive type introduced in ECMAScript 6 – Symbol. The JavaScript already had five primitive types: string
, number
, boolean
, null
, and undefined
before Symbol was introduced. In this tutorial, we’ll discuss Symbol and how to create a Symbol. We’ll also discuss about Symbol properties. But before we discuss all this, we should first discuss why do we need a Symbol.
Symbol is a built-in object which returns a guaranteed unique value – a symbol
primitive.
2. Need for Symbol
Suppose you are having a collection of User
objects which you get from some other source. A User
object may look like this:
let user = { id: 1234, name: "James", city: "London", age: 19, }
Suppose you want to attach a unique identifier to this user
object to distinguish the objects within your system. You can think of doing something like this:
user.id = 123458676; // 123458676 is unique id value
But the problem here is, user
object already has an id
. Since we don’t control the user
object as it is coming from a different object, we have to think of using a different property name. There could be different real world situations where we have requirement to add a unique property to the object but it may have a conflicting name.
To avoid such situation, we can use Symbol. We can create a Symbol which guarantees a unique value and use it like this:
let user = { id: 1234, name: "James", city: "London", age: 19, }; const idSymb = Symbol("id"); user[idSymb] = 123458676; console.log(user);
Output
{
id: 1234,
name: 'James',
city: 'London',
age: 19,
[Symbol(id)]: 123458676
}
Symbols are often used to add unique property keys to an object that won’t collide with keys. These unique property keys are hidden and can’t be access by usual means.
3. Create a Symbol
To create a a new primitive Symbol, use Symbol()
. You can optionally provide a string to the Symbol()
which acts as a descriptor to the Symbol. A symbol’s description is stored internally in the [[Description]]
property. This property is read whenever the symbol’s toString()
method is called either explicitly or implicitly. Otherwise it is not possible to access [[Description]]
directly from code.
let symbl1 = Symbol(); let symbl2 = Symbol("test"); let symbl3 = Symbol("test");
All Symbol values symbl1
, symbl2
and symbl3
are unique.
console.log(symbl1 === symbl2); // false console.log(symbl1 === symbl3); // false console.log(symbl2 === symbl3); // false
Providing an optional string descriptor to Symbol does not create string to Symbol. Using
Symbol()
always provides a new Symbol value.
4. Can’t use new
with Symbol
You can not not use new
with Symbol. Using new
with Symbol will throw a TypeError
:
let symbl1 = new Symbol(); // TypeError: Symbol is not a constructor
Since you can’t use new
with Symbol, you can’t create wrapper object with Symbol like you can do with other primitives, for example new String
, new Boolean
and new Number
.
If you want to create wrapper for Symbol, use Object()
function.
let sym = Symbol('foo') // create Symbol let symObj = Object(sym); // Symbol wrapper object
5. Adding Symbol to object
Symbol is used to add unique property keys in object.
let symb = Symbol(); //wrong way to create Symbol property key let user = { id: 1234, name: "James", city: "London", age: 19, symb: 12345, }; console.log(user);
Output
The correct way to do it is like this:
let symb = Symbol(); let user = { id: 1234, name: "James", city: "London", age: 19, [symb]: 12345, }; console.log(user);
Output
{ id: 1234, name: 'James', city: 'London', age: 19, [Symbol()]: 12345 }
6. Identifying Symbols (typeof)
You can use the typeof
operator to determine whether a variable contains a symbol.
let symb = Symbol(); console.log(typeof symb); //symbol
7. Sharing Symbols
You might need different parts of your code to share same symbols. Suppose there are two different types of objects in your application which should use the same unique identifier. If you manage it yourself, it could be difficult and error prone. ECMAScript 6 provides a global symbol registry that you can access.
7.1 Symbol.for()
When you want to create a symbol which you want to be shared, use Symbol.for()
instead of Symbol()
. The Symbol.for()
method accepts a single string parameter which acts as an identifier for the symbol.
let symb1 = Symbol("id"); let symb2 = Symbol.for("id"); let symb3 = Symbol.for("id"); console.log(symb1); // Symbol(id) console.log(symb2); // Symbol(id) console.log(symb1 === symb2); // false console.log(symb2 === symb3); // true
The Symbol.for()
method first searches the global symbol registry to see whether a symbol with the key id
exists. If found, the method returns the existing symbol. If not found, a new symbol is created and registered to the global symbol registry using the specified key. The new symbol is then returned. Subsequent calls to Symbol.for()
using the same key will return the same
symbol.
7.2 Symbol.keyFor()
Using Symbol.keyFor()
you can retrieve the key associated with a symbol in the global symbol registry.
let id = Symbol.for("id"); console.log(Symbol.keyFor(id)); // "id" let id2 = Symbol("id"); console.log(Symbol.keyFor(id2)); // "undefined" since id2 is not in global symbol registry
8. Symbol Coercion
Symbols cannot be coerced into strings or numbers to prevent them from being accidentally used as properties.
let id = Symbol.for("id"); desc = id + ""; // TypeError: Cannot convert a Symbol value to a string
Similarly, you cannot coerce a symbol to a number. All mathematical operators cause an error when they’re applied to a symbol. For example:
let sym = Symbol.for("id"); sum = sym / 1; // TypeError: Cannot convert a Symbol value to a number
9. Retrieving Symbol properties
Neither of the two functions Object.keys()
and Object.getOwnPropertyNames()
methods can be used to retrieve property symbols from the object. You can use the Object.getOwnPropertySymbols()
method to retrieve property symbols from an object.
let user = { id: 1234, name: "James", city: "London", age: 19, }; const idSymb = Symbol("id"); user[idSymb] = 123458676; console.log(Object.getOwnPropertyNames(user)); // [ 'id', 'name', 'city', 'age' ] console.log(Object.getOwnPropertySymbols(user)); // [ Symbol(id) ]
Object.getOwnPropertySymbols()
returns an array containing symbols.
10. Symbols and for…in iteration
Symbols are not enumerable in for…in iterations.
let user = { id: 1234, name: "James", city: "London", age: 19, }; const idSymb = Symbol("id"); user[idSymb] = 123458676; for (key in user) { console.log(key); }
Output
id
name
city
age
11. Symbols and JSON.stringify()
Symbol properties will be completely ignored when using JSON.stringify()
:
let user = { id: 1234, name: "James", city: "London", age: 19, }; const idSymb = Symbol("id"); user[idSymb] = 123458676; console.log(JSON.stringify(user));
Output
{"id":1234,"name":"James","city":"London","age":19}
12. Symbol wrapper objects as property keys
When a Symbol wrapper object is used as a property key, this object will be coerced to its wrapped Symbol:
let user = { id: 1234, name: "James", city: "London", age: 19, }; const idSymb = Symbol("id"); user[Object(idSymb)] = 123458676; // using Symbol wrapper console.log(user);
Output
{
id: 1234,
name: 'James',
city: 'London',
age: 19,
[Symbol(id)]: 123458676
}
13. Conclusion
In this tutorial, we discussed Symbol, its need, creation and usage. This tutorial provides an overview of Symbol and is not complete. There are other properties and methods associated with Symbol. You are requested to read official documentation for the same. This tutorial is enough to get started with Symbol. We hope this tutorial was informative. Happy learning!