Among the primitive data type, you may have already heard about Symbol
. But you’re asking yourself what is it? When is it useful? When are they currently used?
If it’s the case, you are in the right place. It was my case few times ago :)
Symbol
is a new primitive data type introduced with ES6. It can provide us unique value by using directly Symbol(optionalDescriptiveText)
or can share Symbol through the global Symbol registry.
Thanks to it we can add property to object being sure it doesn’t conflict with another one.
I already spoiled it in the previous part, you can create a unique Symbol value by using Symbol(optionalDescriptiveText)
:
const myFirstSymbol = Symbol('This is my first symbol');
As I said previously a Symbol is unique:
// Will print false!!!
console.log(
Symbol('Description') !== Symbol('Description'),
);
// Will show you in the console something like
// Uncaught TypeError: Symbol is not a constructor
new Symbol('Trying to make an object');
You can also create Symbol
to be shared through your application/library.
You can do it with: Symbol.for(key)
:
// Create a shared Symbol
const sharedSymbol = Symbol.for('Shared Symbol');
// You can assert that you can get this Symbol
// Will print true
console.log(sharedSymbol === Symbol.for('Shared Symbol'));
// Will print true
console.log(Symbol.for() === Symbol.for(undefined));
const sharedSymbol = Symbol.for('Key of shared symbol');
// Will print: "Key of shared symbol"
console.log(Symbol.keyFor(sharedSymbol));
Not gonna lie, I don’t know in which situation we would like to retrieve the key of a shared Symbol. If you know some use cases, do not hesitate to put it in commentaries :)
Okay, now that we have seen how to create a Symbol
, let’s some properties that have Symbol
s.
When adding in objet a Symbol as key, the property will not be enumerable:
const person = {
firstName: 'Bob',
lastName: 'Sponge',
[Symbol('secret')]: 'I was created by a marine biologist',
};
// Will print
// Key: "firstName" and value: "Bob"
// Key: "lastName" and value: "Sponge"
Object.entries(person).forEach(([key, value]) =>
console.log(`Key: "${key}" and value: "${value}"`),
);
There is something pretty unpredictable that happens. Each iframe
has its own realm so its own instance of Symbol
. However, shared Symbol
are the same through realm.
Let’s make an iframe
in which we declare a shared Symbol
:
<iframe
srcdoc="<script>
var sharedSymbol = Symbol.for('Shared symbol');
</script>"
></iframe>
Now let’s get this iframe
and get the window from it through the contentWindow
property:
const iframe = document.querySelector('iframe');
const iframeWindow = iframe.contentWindow;
// Will print false!
console.log(iframeWindow.Symbol === Symbol);
// But will print true!
console.log(
iframeWindow.sharedSymbol === Symbol.for('Shared symbol'),
);
There are some well-known Symbol
s that are used to implement methods that you use everyday.
Let’s see a few:
Symbol.iterator
: This symbol defines the default iterator for an object that will make the use offor...of
possible. The object will be then iterable.
For example, if we have an Array
of Person
with the type:
type Person = {
firstName: string;
lastName: string;
};
And when we loop on this Array
, we want to get directly the template ${firstName} ${lastName}
. The code will be:
const persons = [
{ lastName: 'Spears', firstName: 'Britney' },
{
lastName: 'Grande',
firstName: 'Ariana',
},
{
lastName: 'Timberlake',
firstName: 'Justin',
},
];
persons[Symbol.iterator] = function () {
let index = 0;
return {
next: () => {
const hasNext = this.length > index;
if (hasNext) {
const person = this[index++];
return {
done: false,
value: `${person.firstName} ${person.lastName}`,
};
} else {
return {
done: true,
};
}
},
};
};
// This will print
// Britney Spears
// Ariana Grande
// Justin Timberlake
for (let person of persons) {
console.log(person);
}
Symbol.hasInstance
: This symbol manages the configuration of theinstanceof
operator for a class.
For example, let’s imagine we have two classes Building
and House
.
We want new House() instanceof Building
to return true. We can do this:
class Building {
constructor() {
this.type = 'building';
}
static [Symbol.hasInstance](instance) {
return (
instance.type === 'house' ||
instance.type === 'building'
);
}
}
class House {
constructor() {
this.type = 'house';
}
static [Symbol.hasInstance](instance) {
return instance.type === 'house';
}
}
// Will print true
console.log(new House() instanceof Building);
// Will print false
console.log(new Building() instanceof House);
class Building {}
class House extends Building {}
Symbol.split
: This symbol can be used as a method and will be called by theString
’ssplit
method:
For example, we can define a WordSplit
class that will split a phrase with space:
class WordSplit {
[Symbol.split](string) {
return string.split(' ');
}
}
console.log(
'A phrase that will be splitted'.split(new WordSplit()),
);
Symbol.toStringTag
: The symbol can be used to defined an object’s property that returns a string that will be used to describe the object. This method is called by theObject
’stoString
method:
class Computer {
constructor() {
this[Symbol.toStringTag] = 'Computer';
}
}
// Will print [object Computer]
console.log(new Computer().toString());
We just see together what Symbol
is, its properties and where they are currently used. I hope it’s now clear for you what are Symbol
s and where they are currently used in everyday life features. So do not tell yourself that Symbol
are not useful anymore (if it was the case) :)
Fun fact, React uses Symbol
to tag the type of elements through the property $$typeof
: see code .
You can find me on Twitter if you want to comment this post or just contact me. Feel free to buy me a coffee if you like the content and encourage me.