This primitive type is useful for so-called "private" and/or "unique" keys.
Using a symbol, you know no one else who doesn't share this instance (instead of the string) will not be able to set a specific property on a map.
Example without symbols:
var map = {};
setProp(map);
setProp2(map);
function setProp(map) {
map.prop = "hey";
}
function setProp2(map) {
map.prop = "hey, version 2";
}
In this case, the 2nd function call will override the value in the first one.
However, with symbols, instead of just using "the string prop", we use the instance itself:
var map = {};
var symbol1 = Symbol("prop");
var symbol2 = Symbol("prop"); // same name, different instance โ so it's a different symbol!
map[symbol1] = 1;
map[symbol2] = 2; // doesn't override the previous symbol's value
console.log(map[symbol1] + map[symbol2]); // logs 3
Answer from Ven on Stack OverflowVideos
Symbol is used to create a totally unique, one-of-a-kind identifier. It's use is precisely for the example you list.
Even if you call Symbol with the same string, the instances will be different. This allows different libraries (which may be used at the same time) to define keys which may be used at the same time.
For example, imagine two libraries using a common name to define something on window or global (or for illustration, the fake global door):
const door = {};
// from library 1
door.cake = () => console.log('chocolate');
// from library 2
door.cake = () => console.log('vanilla');
// your code
door.cake();
In this example, the first libraries code is lost because it was unintentionally given the same name as the first.
Now, if they both use Symbol, then even if they are named the same, you can still access both (assuming they export Symbol somehow):
const door = {};
// library 1
const cake1 = Symbol('cake');
door[cake1] = () => console.log('chocolate');
// library 2
const cake2 = Symbol('cake');
door[cake2] = () => console.log('vanilla');
// your code
door[cake1]();
door[cake2]();
Both are still accessible.
That is a bit of an oversimplification, but it illustrated the point.
In a more practical usage, these are used for things such as importing modules. The modules may end up with the same name, but that's okay because they'll have unique symbols associated with them, which makes them uniquely accessible as long as your have the Symbol objects.
As for when to use them yourself... it's probably going to be pretty rare. You'll mainly want to use them any time you have a way to provide the Symbol but need other things to remain unique. I've only used these directly in a few narrow circumstances where the created element may end up the same.
For example, if you were making an object using names as the key, you might have duplicate names. Without symbols, the objects would override one other. With symbols, they'll all remain.
const people = {};
people[Symbol('bob')] = { name: 'Bob Smith' };
people[Symbol('bob')] = { name: 'Bob Jones' };
From the documentation:
Every symbol value returned from
Symbol()is unique.
That means === comparisons will fail because they're not identical.
If you want a unique identifier of some sort that can be given a descriptive, if otherwise irrelevant name, then Symbol might be useful.