In general, object property keys can be of two types: string and symbol. Symbols are ultimately unique identifiers. They can be created by using the factory function Symbol().
After creating a symbol, you can give it a name or a description. It is especially useful for debugging practices. Let’s check out the following example:
// id is a symbol, it's a description "id" let id = Symbol("id");
As we have already noted, symbols are unique. Even when you create a variety of symbols with the same name, they are considered different symbols.
Remember that descriptions and names are merely labels not affecting anything.
Here is an example:
In the case above, the two symbols have the same description, but they are not equal.
It’s important to know that it’s not possible to auto-convert symbols to a string.
For example, this alert will bring an error:
For showing a symbol, you need to explicitly call .toString() on it, as follows:
For getting symbol.description property, call:
About “Hidden” Properties
With the help of symbols, you can easily create the so-called “hidden” properties of an object. It is impossible to access or overwrite other parts of the code.
In case you work with site objects belonging to third-party code, you will need to add identifiers to them. It is necessary to use a symbol key for it, as follows:
In another case, when another script wants to have its identifier inside the site.
So, that script may create its Symbol("id").
// ... let id = Symbol("id"); site[id] = "Their id value";
Symbols in a Literal
For using a symbol in an object literal, it is necessary to insert square brackets around it.
Just look at this example:
This is because you need the value from the variable, id as the key but not the string “id”.
The for…in Skips Symbols
The for..in loop doesn’t involve symbolic properties. Here is an example:
As we have already mentioned, symbols are unique, even if they have the same description. But, sometimes, you may need the symbols with the same name to be the same entities. For example, various parts of your application wish to access the symbol "id" which means the same property.
A global symbol registry is there to achieve that. It allows creating symbols inside of it and accessing them later. Moreover, it ensures that continuous accesses by the same name show precisely the same symbol. For reading a symbol for the global registry, you are recommended to use Symbol.for(key).
Using this call helps to check the global registry. Then, if there is a symbol, defined as key, it will be returned. Otherwise, a new symbol Symbol(key) will be created and stored in the registry by a particular key.
It is demonstrated in the example below:
The symbols that are inside the registry are known as global symbols. You can use them at the time you want an application-wide symbol that has an entry everywhere in the code.
It is possible to use a reverse call for global symbols. You can do that with the help of Symbol.keyFor(sym). It will do the reverse: return a name by a global symbol.
For better understanding, check out the following example:
The Symbol.keyFor will work exclusively for global symbols. In case the symbol turns out not global, there will be no option for finding it and returning undefined. In other words, each symbol has a description property.
Here are some of them:
There are many other system symbols, as well.
For instance, Symbol.toPrimitive is targeted at describing the object to primitive conversion.
Symbol.isConcatSpreadable can be described as a pretty specific symbol. It drives the behavior of Array#concat..
Technically, symbols are not entirely hidden. A built-in method Object.getOwnPropertySymbols(obj) will allow you to receive all the symbols. There exists another method, called Reflect.ownKeys(obj),, which is targeted at returning all the keys of an object along with symbolic ones. Anyway, most libraries, syntax constructs, and built-in functions avoid using the methods mentioned above.