Overview
Before introduction of symbol in ES6, It was very hard to define the private variables. Developers mostly used to do a workaround of using closure to make variables private. But now symbols can be used to create private object members. Symbols are added as a primitive type just like strings, numbers, booleans, null, and undefined.
Syntax
We can create symbols in javascript by using Symbol()
function.
let name = Symbol(); let details = {}; details[name] = 'Prashant Yadav'; console.log(details[name]); //"Prashant Yadav" let fullName = symbol()
As symbols are primitive values we cannot use them as a constructor. Calling Symbol with new keyword like new Symbol()
will throw an error.
Symbol function also accepts an argument which is used to describe the symbol.
let name = Symbol('Full Name'); let details = {}; details[name] = 'Prashant Yadav'; console.log(details[name]); //"Prashant Yadav" console.log(name); //Symbol(Full Name)
The description is stored internally and is read whenever the Symbol.toString()
method is called implicitly or explicitly.
Symbols are always unique. Even if declared with same description.
let details = {}; let firstName = Symbol('Full Name'); details[firstName] = 'Prashant'; console.log(details[firstName]); //"Prashant" let secondName = Symbol('Full Name'); details[secondName] = 'Yadav'; console.log(details[secondName]); //"Yadav" console.log(firstName == secondName); //false
As we can see we have created two different Symbols with same description still they are not same.
Different ways to define Symbol in Javascript
We can use Symbol where ever we use a computed property name.
As a dynamic object property
let firstName = Symbol('First Name'); let lastName = Symbol('Last Name'); let details = { [firstName] = 'Prashant' [lastName] = 'Yadav' }; console.log(details[firstName]); //Prashant console.log(details[lastName]); //Yadav
Using object.defineProperty()
let firstName = Symbol('First Name'); let lastName = Symbol('Last Name'); let details = {}; Object.defineProperty(details, firstName, { value: 'Prashant', writable: false }); Object.defineProperty(details, lastName, { value: 'Yadav', writable: false }); console.log(details[firstName]); //Prashant console.log(details[lastName]); //Yadav
Using object.defineProperties()
let firstName = Symbol('First Name'); let lastName = Symbol('Last Name'); let details = {}; Object.defineProperties(details, { [firstName]: { value: 'Prashant', writable: true }, [lastName]: { value: 'Yadav', writable: false } }); console.log(details[firstName]); //Prashant console.log(details[lastName]); //Yadav
Using Symbol globally in javascript
We may some times want to use share the same symbol with the different parts of our code. It is difficult to keep track of the symbol across large code base.
In order to make our work easy ES6 provides us a function called Symbol.for()
which stores the Symbol in global registry. It accepts a single parameter description Symbol.for('name')
and checks if a Symbol with same description exits or not. If it exists then it returns the existing else it will create a new one.
let details = {}; let firstName = Symbol.for('name'); details[firstName] = 'Prashant'; console.log(details[firstName]); //'Prashant' let lastName = Symbol.for('name'); details[lastName] = 'Yadav'; console.log(details[lastName]); //'Yadav' console.log(firstName); //Symbol(name) console.log(lastName); //Symbol(name) console.log(firstName === lastName); //true
Checking current Symbol being used.
There is an another function Symbol.keyFor(Symbol)
which is used to check the current Symbol being used in global registry. It takes the Symbol as input and returns the description of the Symbol.
let firstName = Symbol.for('name'); let lastName = Symbol.for('name'); let middleName = Symbol('abc'); console.log(Symbol.keyFor(firstName)); //"name" console.log(Symbol.keyFor(lastName)); //"name" console.log(Symbol.keyFor(middleName)); //undefined
As Symbol('abc')
is not stored in the global registry. It returns undefined
.
Retrieving Symbol Properties
We know that we can use Symbol as object property but we cannot retrieve it using existing object methods like Object.keys()
and Object.getOwnPropertyNames()
as they return all enumerable property names.
let office = { [Symbol("Tom")] : "CEO", [Symbol("Mark")] : "CTO", [Symbol("Mark")] : "CIO", } for(person in office) { console.log(person); } //undefined
As Symbol is not enumerable. ES6 provides us a function which we can use to retrieve Symbols. Object.getOwnPropertySymbols()
returns an array of own property Symbols.
let office = { [Symbol("Tom")] : "CEO", [Symbol("Mark")] : "CTO", [Symbol("Mark")] : "CIO", } const symbols = Object.getOwnPropertySymbols(office); console.log(symbols); /* (3) [Symbol(Tom), Symbol(Mark), Symbol(Mark)] 0: Symbol(Tom) 1: Symbol(Mark) 2: Symbol(Mark) length: 3 __proto__: Array(0) */
We can use this array of own property Symbols along with map to retrive all the values.
let office = { [Symbol("Tom")] : "CEO", [Symbol("Mark")] : "CTO", [Symbol("Mark")] : "CIO", } const symbols = Object.getOwnPropertySymbols(office); const values = symbols.map(symbol => office[symbol]); for(let value in values) { console.log(values[value]); } //CEO //CTO //CIO
Javascript Symbol Methods
Symbol.hasInstance
It is used by instanceOf
to check the inheritance of the object.
Symbol.isConcatSpreadable
Return’s a boolean value. It is used to indicate Array.prototype.concat()
that it should flatten the collection passed to it.
Symbol.iterator
Return’s an iterator.
Symbol.match
Used by String.prototype.match()
to compare strings.
Symbol.replace
Used by String.prototype.replace()
to replace substrings.
Symbol.search
Used by String.prototype.search()
to find substrings.
Symbol.split
Used by String.prototype.split()
to split string in array of characters.
Symbol.species
Constructor to create derived objects.
Symbol.toPrimitive
Returns the primitive value of the object.
Symbol.toStringTag
A string used by Object.prototype.toString()
to create an object description.
Symbol.unscopables
An object whose properties are the names of object properties that should not be included in a with
statement.