Back to: JavaScript Tutorial For Beginners and Professionals
JavaScript Symbol Primitive Data Type with Examples
In this article, I am going to discuss JavaScript Symbols with Examples. Please read our previous article where we discussed the JavaScript Data Structures with Examples. In JavaScript, Symbol is a new primitive data type introduced in ES6. It is a primitive data type along with the string, number, Boolean, null and undefined. At the end of this article, you will understand the following pointers in detail.
- What is Symbol in JavaScript?
- Symbol Iteration and Hidden Properties in JavaScript
- Features of Symbol
- Why do we need Symbols in JavaScript?
- Why Unique Property Names?
- Where to use Symbol in JavaScript?
- Converting a Symbol into a String in JavaScript
- Understanding Symbol.for(key) and Symbol.keyFor(symbolValue) methods
- Global and Local Symbol Differences
- Well-Known Symbols in JavaScript
- Symbol.hasInstance
- Symbol.iterator
- Symbol.isConcatSpreadable
- Symbol.toPrimitive
- Symbol.split
What is Symbol in JavaScript?
A value having the data type Symbol can be called a “Symbol value”. In a JavaScript runtime environment, a symbol value is created by calling the global function Symbol(), which dynamically produces an anonymous, unique value. A symbol can be used as an object property. The symbol can be used as unique values, whenever we use enum or string to maintain a unique value we should use Symbol instead. The Symbol can have a description as an optional, but for debugging purposes only.
Syntax to use Symbol in JavaScript:
To create a new primitive data type symbol, we use a global function Symbol() and define it with the following syntax:
let mySymbol = Symbol();
Symbol is not a full constructor. Notice that there was no new Keyword. As Symbol is a primitive value, if we attempt to create a symbol using the new operator, the new operator throws a TypeError.
let mySym = new Symbol(); //TypeError
The Symbol() function accepts a description as an argument, which makes the symbol more descriptive. We can pass the parameter to Symbol(), and that is used as the symbol description, useful just for debugging purposes:
let mySymbol1 = Symbol(‘JavaScript’);
And last but not least, a symbol is never equal to anything else except itself.
const sym1 = Symbol(‘Hello’)
const sym2 = Symbol(‘Demo’)
sym1 == sym2 //false
sym1 == ‘hi’ //false
sym1 === sym2 // true
typeof sym1 //symbol
They are unique, and every time we invoke Symbol(), we get a new and unique symbol that is guaranteed to be different from all other symbols. We can see it here
Symbol() === Symbol()//false
Symbol(‘test’) === Symbol(‘test’)//false
Even though every symbol gives a unique value we can’t view them by logging.
console.log(Symbol());//Symbol()
console.log(Symbol(‘test’));//Symbol(test)
Symbols are immutable (cannot be changed). Just like strings, the data type of symbol is Symbol.
As a symbol is a primitive data type so we can use typeof operator to check whether a variable is a symbol or not. This typeof operator returns the symbol string when we pass the symbol variable.
console.log(“type of symbol:”, typeof Symbol());//symbol
console.log(“type of symbol with description:”, typeof Symbol(‘John Doe’));//symbol
We can see it in the below example:
Example: JavaScript Symbol Example
<html> <head> <title>JavaScript Symbol example</title> </head> <body> <script> Symbol() === Symbol() //false console.log('Is both symbols are equal? ', Symbol() === Symbol()); Symbol('JavaScript') === Symbol('Programming') console.log('Is both symbols with description are equal? ', Symbol('JavaScript') === Symbol('Programming')); let mySymbol1 = Symbol('symDescription'); let mySymbol2 = Symbol('symDescription'); console.log('Two symbols with the same description equal? :', mySymbol1 === mySymbol2); console.log(Symbol());//Symbol() console.log(Symbol('JavaScript'));//Symbol(JavaScript) console.log("type of symbol:", typeof Symbol());//symbol console.log("type of symbol with description:", typeof mySymbol1);//symbol console.log("Symbols are of typeof symbol:", typeof Symbol() === 'symbol');//true console.log("A symbol is never equal to anything else except itself.", mySymbol1 === mySymbol1) let mySym = new Symbol()//TypeError throw when using new operator </script> </body> </html>
Output:
Symbol Iteration and Hidden Properties in JavaScript
The Symbol allows us to create “hidden” properties of an object, that no other part of the program or code can access or overwrite. It won’t be enumerated and are skipped by in for…in loops, and are ignored by function such as JSON.Stringyfy(), Object.keys(), and Object.getOwnPropertyNames(). This makes them ideal for properties that we don’t want to be included when serializing an object.
Example: JavaScript Symbol Iteration and Hidden Properties Example
<html> <head> <title>JavaScript accessing the property and hidden properties of an object example</title> </head> <body> <script> const employee = {}; const email = Symbol(); employee.name = "Alexa"; employee.age = 20; employee[email] = "test@abc.com"; //hidden properties of an object Object.keys(employee);//["name","age"] console.log("keys of employee object: ", Object.keys(employee)); JSON.stringify(employee); //"{"name": "Alexa","age":"20"}" console.log("Converting JavaScript object employee to JSON object: ", JSON.stringify(employee)); console.log("Accessing email of employee which is Symbol data type: ", employee[email]); </script> </body> </html>
Output:
From the above code snippet, we understand that Symbol properties don’t participate in for…in loops, while executing the Object.keys(employee) it just returned the keys of the employee that was name and age and it doesn’t include Symbol in it. That’s called Hiding symbolic properties.
if any other script or code tries to loop over our employee object, it won’t unexpectedly access a symbolic property. As another script does not have our symbol. Hence, the property will be protected from accidental access or overwrite.
Features of Symbol in JavaScript
- A Symbol value represents a unique identifier.
- Each symbol value is unique and immutable. Each symbol value is connected with a value of [description], which is either undefined or a string.
- In JavaScript, Symbol is a new primitive type introduced in ES6.
- We can create our own symbols using var mySymbol = Symbol()
- We can add a description for debugging purposes, like var mySymbol = Symbol(‘JavaScript’)
- Symbols are immutable and unique. Symbol() and Symbol(‘JavaScript’) all are different.
- Symbols are of type of symbol, thus typeof Symbol() === ‘symbol’
- We can also create global symbols with Symbol.for(key)
- If a symbol with the provided key already existed, we get that one back
- Otherwise, a new symbol is created, using the key as its description as well
- Symbol.keyFor(symbol) is the inverse function, taking a symbol and returning its key
- Global symbols are as global as it gets, globally available. Single registry used to search these symbols across the runtime
- window context
- <iframe> context, Symbol.for(‘JavaScript’) === iframe.contentWindow.Symbol.for(‘JavaScript’)
- There are also “well-known” symbols
- ES6 provides predefined symbols that already exist and are internally used by JavaScript which are called well-known symbols that contain the common behavior of JavaScript. Each well-known symbol is a static property of the Symbol object.
- Well-known symbols are Symbol.iterator for array-like objects, or Symbol.search for string objects.
- Iterating over symbol properties is hard, but not impossible and definitely not private
- Symbols are hidden to all “reflection” methods before ES6
- Symbols are accessible through Object.getOwnPropertySymbols
Why do we need Symbols in JavaScript?
At first look, symbols seem meaningless and yes, they are. We can’t do anything else with them other than use them as property names. Why use a Symbol as a property name? Because this creates an always-unique property name.
Example: JavaScript accessing the property of object example
<html> <head> <title>JavaScript accessing the property of object example</title> </head> <body> <script> const sym = Symbol('HelloSuymbol'); const obj = { [sym]: 'Value100'//dynamic property names } console.log('Way to access the property of object:', obj[sym]); </script> </body> </html>
Output: Way to access the property of object: Value100
In the above example, we are retrieving the object property, and also if we don’t know this syntax [sym]:’Value100’? it’s called dynamic property names. We will learn more about this in upcoming chapters.
Why Unique Property Names?
Let suppose we want to assign a property name on a large object (e.g.: obj of a bank). If we do so the normal way, there’s a slight chance (which increases with the size of the object) that our property will interfere with an already existing property.
What if we first do check if the property already exists? With the help of the introduction of dynamic properties now it’s easier to not notice that the property we want to assign already exists or will exist.
The solution for the problem is just to use the symbol.
Security: And at the last, with JavaScript more and more being used for building huge platforms, security play a huge role. The symbol is not aimed at and must not be considered as a security mechanism.
Anybody can always use the Object.getOwnPropertySymbol() method to gain access overall properties of an object which have property names of the data type symbol. This method returns an array of a symbol which helps us to find the symbol properties on the specified object.
Where to use Symbol in JavaScript?
- A symbol can be used as an object property.
- A symbol can be used as a unique value to avoid name clashes.
- There are many inbuilt symbols knows as Well-known symbol can be used for modifying the object behavior.
- Symbol use as Hidden object properties. We learned that Symbol properties don’t participate in for…in loops while executing the Object.keys(employee) it just returned the keys of the employee that was name and age and it doesn’t include Symbol in it. That’s called Hiding symbolic properties. if any other script or code tries to loop over our employee object, it won’t unexpectedly access a symbolic property. Because another script does not have our symbol. So, the property will be protected from accidental use or overwrite.
- The symbol can be used as unique values, whenever we use enum or string to maintain a unique value we should use Symbol instead.
Converting a Symbol into a String in JavaScript
Most of the values or the object in the JavaScript are implicitly/automatically converted to a string. Symbols are special data types they don’t automatically convert into a string even when performing concatenation (combining).
let symb = Symbol(‘test’) + ”//throws TypeError
Instead of this, they have to explicitly convert into a string as and when mandatory by using the toString() method or the String constructor
Example: JavaScript converting symbol into a string
<html> <head> <title>JavaScript converting symbol into a string example</title> </head> <body> <script> let symbJS = Symbol("JavaScript"); let symbJs_str1 = symbJS.toString();//"Symbol("JavaScript")" let symbJs_str2 = String(symbJS);//"Symbol("JavaScript")" console.log("Symbol symbJS value converting using toString() method: ", symbJs_str1); console.log("Symbol symbJS value converting using String() constructor : ", symbJs_str2); let symb = Symbol('test') + ''//throws TypeError console.log('symb value after concatinating: ', symb) </script> </body> </html>
Output:
Using Symbol.for() to create shared, global symbols
Using the global function Symbol() will not create the global or shared symbol that is available in our whole source code. In order to create a symbol that will be available across files and even across areas where each of which has its own global scope i.e.: global symbol registry.
There is a global symbol registry that contains all available symbols. We create symbols and access them later and it assures that continue repeatedly accessing the symbol by the same will returns exactly the same symbol.
Symbols inside the registry are called global symbols. If we want an application or module-wise symbol to be accessible everywhere in the code then that is what is used for.
The global symbol registry is built by JavaScript compiler infrastructure. For Global Symbol, there’re two methods we can use to set and retrieve the symbol from the global symbol registry is
- Symbol.for(key);
- Symbol.keyFor(symbolvalue);
Symbol.for(key)
The Symbol.for(key) method is used to create and retrieve global symbols by name. This function call looks up the key into the global registry, if a symbol with that key exists in the global registry then that existing symbol is returned, else if no symbol with that key exists in the global registry, it creates a new symbol Symbol.for(key) and stores it in the global symbol registry by the given key.
The global symbol registry keeps track of symbols by key. Also, note that the key will be used as a description when symbols are created into the global symbol registry.
Syntax: The below syntax for creating a new global symbol in the registry.
Symbol.for(key)
The Symbol.for(key) method accepts a single parameter that can be used as symbol description
key: symbol description used for creating a new or retrieving the already created ones.
Example: JavaScript Symbol Symbol.for(key) get a symbol by name
<html> <head> <title>JavaScript Symbol Symbol.for(key) get a symbol by name example</title> </head> <body> <script> // read from the global registry let id = Symbol.for("CourseId"); // if the symbol did not exist, it is created console.log("Reading the symbol did not exist, it is created", id); // read it again (maybe from another part of the code) let idAgain = Symbol.for("CourseId"); console.log("Reading the created symbol", idAgain); // the same symbol console.log("comparing if the symbol is same: ", id === idAgain);// true </script> </body> </html>
Output:
In the above example the first call to Symbol.for(“CourseId”) creates a symbol and adds it to the global symbol registry and returns it. The second call returns the same symbol because the key is already there in the registry and is mapped to the symbol returned by the first call.
Symbol.keyFor(symbolValue)
The Symbol.keyFor(symbolValue) method is used to returns the key associated with the symbol when the symbol was added into the global registry. If a symbol doesn’t exist in the global symbol registry, this method returns undefined.
The Symbol.keyFor(symbolValue) method internally uses the global symbol registry to check the key for the symbol. So, it doesn’t work for non-global symbols. If the symbol is not global, it won’t be able to find it and returns undefined.
Symbol.prototype.description: It is said that symbols have description property. So, we can access them by using symbolName.description
Syntax: The below syntax for creating a new global symbol in the registry.
Symbol.keyFor(symbolValue)
Example: JavaScript Symbol.keyFor(symbolValue) get a key associated with a symbol
<html> <head> <title>JavaScript Symbol Symbol.keyFor(symbolValue) get a key associated with a symbol example</title> </head> <body> <script> // get symbol by name let sym1 = Symbol.for("car"); let sym2 = Symbol.for("driver"); console.log("Reading the sym1 symbol did not exist, it is created: ", sym1); console.log("Reading the sym2 symbol did not exist, it is created: ", sym2); // get key associated with a symbol let key1 = Symbol.keyFor(sym1); let key2 = Symbol.keyFor(sym2); console.log("Reading the key associated with sym1 symbol: ", key1); console.log("Reading the key associated with sym2 symbol: ", key2); // accessing the symbol with description property console.log("accessing the symbol sym1 with description property: ", sym1.description); console.log("accessing the symbol sym2 with description property: ", sym2.description); </script> </body> </html>
Output:
Global and Local Symbol Differences in JavaScript
As we learned above the Symbol.keyFor(symbolValue) method internally uses the global symbol registry to check the key for the symbol. So, it doesn’t work for non-global symbols. If the symbol is not global, it won’t be able to find it and returns undefined. It is said that symbols have description property. So, we can access them by using symbolName.description
Example: Global and Local Symbol Differences in JavaScript
<html> <head> <title>JavaScript global and local symbol difference example</title> </head> <body> <script> // get symbol by name let globalsym = Symbol.for("car"); let localsym = Symbol("driver"); console.log("Reading the globalsym symbol did not exist, it is created: ", globalsym); console.log("Reading the localsym symbol did not exist, it is created: ", localsym); // get key associated with a symbol let globalsymkey = Symbol.keyFor(globalsym); let localsymkey = Symbol.keyFor(localsym); console.log("Reading the key associated with globalsym symbol: ", globalsymkey); console.log("Reading the key associated with localsym symbol: ", localsymkey); // accessing the symbol with key as description property console.log("accessing the symbol globalsym with description property: ", globalsym.description); console.log("accessing the symbol localsym with description property: ", localsym.description); </script> </body> </html>
Output:
Well-Known Symbols in JavaScript
JavaScript ES6 provides many inbuilt symbols that already exist and are used by JavaScript internally, which are called Well-known Symbol contains the common behavior of JavaScript. Each well-known symbol is nothing but the static property of the Symbol object. The Static Properties or Well-Known Symbols are:
- Symbol.hasInstance
- Symbol.iterator
- Symbol.isConcatSpreadable
- Symbol.toPrimitive
- Symbol.split
JavaScript Symbol.hasInstance
The Symbol.hasInstance symbol method is used to determine if a constructor (used to create class object or to initialize class instance) object identify an object as a constructor instance. It is used by instanceof operator. This method changes the behavior of instanceof operator.
Syntax: When we call instanceof operator
obj instanceof type;
JavaScript internally called the Symbol.hasInstance method
type[Symbol.hasInstance](obj);
This method determines if the obj is an instance of the type object, it will return true else if it’s not then it will return false. Let see some example to understand it better
Example: JavaScript Symbol static property Symbol.hasInstance
<html> <head> <title>JavaScript Symbol static property Symbol.hasInstance example</title> </head> <body> <script> class Car { } console.log("Is Array[] instance of Car: ", [] instanceof Car);// false class CarArr { static [Symbol.hasInstance](obj) { return Array.isArray(obj); } } console.log("Is Array[] instance of CarArr: ", [] instanceof CarArr);// true </script> </body> </html>
Output:
In the code sample, the Symbol.hasInstance method determines if the obj is the instance of a type object or a class using the instanceof operator. If we note that the class Car, array [] is not an instance of class Car, hence instanceof operator returns false Whereas, in another class CarArr, array[] is an instance of the class CarArr, hence instanceof operator returns true.
In the below example we are checking whether an object is an instance of a class or not using instanceof operator also we can use Symbol.hasInstance for such checks
Example: JavaScript Symbol static property Symbol.hasInstance checking the instance of an object
<html> <head> <title>JavaScript Symbol static property Symbol.hasInstance checking the instance of an object example</title> </head> <body> <script> class Car { constructor() { } } const tesla = new Car(); console.log("Is tesla a instance of class Car: ", Car[Symbol.hasInstance](tesla));// true </script> </body> </html>
Output: Is tesla a instance of class Car: true
JavaScript Symbol.iterator
In JavaScript ES6, all collection objects such as Array, Set, Map, and strings are iterable objects. As we already learned and know that object that has Symbol.iterator property are called iterable objects. JavaScript ES6 provides for…of the loop that works with the iterable objects.
The Symbol.iterator symbol method is used to determine whether this method will return an iterator for an object. It is used for…of the loop. When we called for…of loop JavaScript internally called the Symbol.iterator method to get the iterator of an object.
Example: JavaScript Symbol static property Symbol.iterator
<html> <head> <title>JavaScript Symbol static property Symbol.iterator example</title> </head> <body> <script> var numbers = [10, 20, 30]; for (let num of numbers) { console.log("The numbers are: ", num); } var iterator = numbers[Symbol.iterator](); console.log("iterating next object in the array: ", iterator.next());//{value: 10, done: false} console.log("iterating next object in the array: ", iterator.next());//{value: 20, done: false} console.log("iterating next object in the array: ", iterator.next());//{value: 30, done: false} console.log("iterating next object in the array: ", iterator.next());//{value: undefined, done: true} </script> </body> </html>
Output:
As we have learned about iterator in the Generator chapter, we can create our own user-defined iterable that will see with some example.
We already have seen how iterator works its uses for…of the loop and when we use this loop it internally calls the Symbol.iterator method to get the iterator of an object, then it calls the iterator.next() method to fetch the next object from the list once the iterator reaches the last object of the list it returns true with the done property of an object.
Example: JavaScript Symbol static property Symbol.iterator user-defined iterable
<html> <head> <title>JavaScript Symbol static property Symbol.iterator user-defined iterable example</title> </head> <body> <script> const myIterable = {}; myIterable[Symbol.iterator] = function* () { yield 1; yield 2; yield 3; }; console.log("user-defined iterable: ", [...myIterable]); // expected output: Array [1, 2, 3] </script> </body> </html>
Output:
JavaScript Symbol.isConcatSpreadable
This Symbol.isConcatSpreadable well-known symbol static property returns a Boolean value indicating whether an object should be added individually to the result of array elements by using Array.prototype.concat()
The concat() function is used to combined or concatenate two arrays, it also accepts a non-array argument. The default behavior of, Array.prototype.concat() spreads or add individually arrays element into its result array elements
Example: JavaScript Symbol static property Symbol.isConcatSpreadable
<html> <head> <title>JavaScript Symbol static property Symbol.isConcatSpreadable example</title> </head> <body> <script> let states = ['Maharashtra', 'Punjab'], capitals = ['Mumbai', 'Lucknow']; let allStates = states.concat(capitals) console.log("All states of india with their capitals are: ", allStates); let extraAllStates = allStates.concat('Goa');//non-array argument console.log("All states of india with their capitals with non-array argument: ", extraAllStates); </script> </body> </html>
Output:
Also, we can disable the default behavior of concat() function of spreading the array into its result array elements, by setting the Symbol.isConcatSpreadable property to false.
Example: Default behaviour of concat() function by setting Symbol.isConcatSpreadable to true
<html> <head> <title>JavaScript the default behavior of concat() function by setting Symbol.isConcatSpreadable to true example</title> </head> <body> <script> let states = ['Maharashtra', 'Punjab'], capitals = ['Mumbai', 'Lucknow']; //default behavior of concat() method capitals[Symbol.isConcatSpreadable] = true; let allStates = states.concat(capitals) console.log("Using contact() default behavior-India states with capitals: ", allStates); </script> </body> </html>
Output:
Example: Disable default behaviour of concat() function by setting Symbol.isConcatSpreadable to false
<html> <head> <title>JavaScript disable default behavior of concat() function by setting Symbol.isConcatSpreadable to false example</title> </head> <body> <script> let states = ['Maharashtra', 'Punjab'], capitals = ['Mumbai', 'Lucknow']; //disble default behavior of concat() method by setting false capitals[Symbol.isConcatSpreadable] = false; let allStates = states.concat(capitals) console.log("Disabling contact() default behavior-India states with capitals: ", allStates); </script> </body> </html>
Output:
JavaScript Symbol.toPrimitive
The Symbol.toPrimitive static property that used as function value to converts an object to a primitive value.
Example: JavaScript Symbol.toPrimitive static property that used as a function
<html> <head> <title>JavaScript Symbol.toPrimitive static property that used as function example</title> </head> <body> <script> function Car(model, price) { this.model = model; this.price = price; } Car.prototype[Symbol.toPrimitive] = function (arg) { var result; switch (arg) { case 'string': result = this.model + this.price; break; case 'number': result = this.price; break; case 'default': result = this.model + this.price; break; } return result; } var cost = new Car('BMW', 10000); console.log('Car cost is ' + cost); // Car Cost is BMW10000-default console.log(+cost + 1); // 10001 -number console.log(String(cost)); // BMW10000 -string </script> </body> </html>
Output:
In the above example, the Symbol.toPrimitive method takes an arg parameter which has one of three values: “number”, “string”, and “default”. The arg parameter specifies the type of return value.
JavaScript Symbol.split
The Symbol.split method splits the string at the indices that match the regular expression. This function is used by String.split() method.
Example: JavaScript Symbol.split
<html> <head> <title>JavaScript Symbol.split example</title> </head> <body> <script> class ReverseSplit { [Symbol.split](string) { const array = string.split(' '); return array.reverse(); } } console.log('Reverse of How Sunny day today is:', 'How Sunny day today is!'.split(new ReverseSplit())); // [ "is!", "today", "day", "sunny", "How" ] </script> </body> </html>
Output:
The above example will split the string based on the regular expression provided in the above code we have provided to (‘ ‘) which means in the whole string whenever space is found it gets split into an array and later we have reversed the string.
Static Methods are:
- Symbol.for(key)
- Symbol.keyFor(SymbolValue)
Symbol.for(key)
As we have seen that the Symbol.for(key) method is used to create and retrieve global symbols by name. This function call looks up the key into the global registry, if a symbol with that key exists in the global registry then that existing symbol is returned, else if no symbol with that key exists in the global registry, it creates a new symbol Symbol.for(key) and stores it in the global symbol registry by the given key.
Symbol.keyFor(SymbolValue)
As we already saw that the Symbol.keyFor(symbolValue) method is used to returns the shared symbol key associated with the symbol when the symbol was added into the global registry. If a symbol doesn’t exist in the global symbol registry, this method returns undefined.
Instance Properties
- Symbol.prototype.description
Symbol.prototype.description
It is said that symbols have description property. So, we can access them by using symbolName.description. It is a read-only string that contains the description of a symbol.
After going through the static properties, methods, and all we learned that how the well-known symbol is used to modify the object behavior.
In the next article, I am going to discuss TypedArray in JavaScript with Examples. Here, in this article, I try to explain the JavaScript Symbol with examples. I hope this JavaScript Symbol article will help you with your need. I would like to have your feedback. Please post your feedback, question, or comments about this article.