How to Use Well-Known Symbols in JavaScript

The Symbol
class comes with static properties known as well-known symbols. They are used to implement and customize the behavior of certain built-in JavaScript operations on objects of a particular class.
Let's explore some of these well-known symbols.
11 Amazing New Features in ES13

Symbol.hasInstance
The Symbol.hasInstance
method customizes the behavior of the instanceof
operator. Generally, when you use the instanceof
operator like this:
obj instanceof type
JavaScript will call the Symbol.instanceof
method like this:
type[Symbol.hasInstance](obj);
Here's an example where we use the instanceof
method on an instance of a user-defined class.
class List {}
console.log([] instanceof List); // false
Going by the default behavior of instanceof
, []
is an Array
, not an instance of the List
class, so instanceof
returns false
here.
If we want to change this behavior and make instanceof
return true
in this scenario, we would customize the Symbol.hasInstance
method like this:
class List {
static [Symbol.hasInstance](obj) {
return Array.isArray(obj);
}
}
console.log([] instanceof List); // true
Symbol.iterator
With the Symbol.iterator
method, we can specify if and how objects of a class can be iterated over. When this method is present, we will be able to use the for...of
loop and spread syntax (...
) on the objects of the class.
When you use the for...of
loop on an array:
const numbers = [1, 2, 3];
for (const num of numbers) {
console.log(num);
}
/*
1
2
3
*/
Internally, JavaScript first calls the Symbol.iterator
method of the numbers
array to get the iterator object. Then it continuously calls the next()
method on the iterator object and copies the value
property in the num
variable, It exits the loop when the done
property of the iterator object is true
.
var iterator = numbers[Symbol.iterator]();
console.log(iterator.next()); // Object {value: 1, done: false}
console.log(iterator.next()); // Object {value: 2, done: false}
console.log(iterator.next()); // Object {value: 3, done: false}
console.log(iterator.next()); // Object {value: undefined, done: true}
By default, objects of a user-defined class are not iterable. But we can make them iterable with the Symbol.iterator
method, as you'll use in the following example:
class List {
elements = [];
add(element) {
this.elements.push(element);
return this;
}
// Generator
*[Symbol.iterator]() {
for (const element of this.elements) {
yield element;
}
}
}
const colors = new List();
colors.add('red').add('blue').add('yellow');
// Works because of Symbol.iterator
for (const color of colors) {
console.log(color);
}
/*
red
blue
yellow
*/
console.log([...colors]);
// [ 'red', 'blue', 'yellow' ]
Symbol.toStringTag
This symbol lets us customize the default string description of the object. It is used internally by the Object.prototype.toString()
method.
class CarClass {
constructor(color, maxSpeed, age) {
this.color = color;
this.maxSpeed = maxSpeed;
this.age = age;
}
}
const car = new CarClass('red', 100, 2);
console.log(Object.prototype.toString.call(car));
// [object Object]
Here is the default implementation of Symbol.toStringTag
was outputted. Here's how we can customize it:
class CarClass {
constructor(color, maxSpeed, age) {
this.color = color;
this.maxSpeed = maxSpeed;
this.age = age;
}
get [Symbol.toStringTag]() {
return 'Car';
}
}
const car = new CarClass('red', 100, 2);
console.log(Object.prototype.toString.call(car));
// [object Car]
Symbol.toPrimitive
The Symbol.toPrimitive
method makes it possible for an object to be converted to a primitive value. It takes a hint
argument that specifies the type of the resulting primitive value. This hint argument can be one of 'number'
, 'string'
, or 'default'
.
Here's an example of using the Symbol.toPrimitive
method.
class Money {
constructor(amount, currency) {
this.amount = amount;
this.currency = currency;
}
[Symbol.toPrimitive](hint) {
if (hint === 'string') {
return `${this.amount} ${this.currency}`;
} else if (hint === 'number') {
return this.amount;
} else if (hint === 'default') {
return `${this.amount} ${this.currency}`;
}
}
}
const price = new Money(500, 'USD');
console.log(String(price)); // 500 USD
console.log(+price); // 500
console.log('Price is ' + price); // Price is 500 USD
11 Amazing New Features in ES13

See also
- [SOLVED] Cannot use import statement outside a module in JavaScript
- How to get the difference between two arrays in JavaScript
- How to get the difference between two sets in JavaScript
- How to add or toggle a class on the body element in JavaScript
- How to Use the JavaScript Nested Ternary Operator
- How to easily get the intersection of two sets in JavaScript