- Mastering JavaScript ES6 Symbols
- By Mahdhi Rezvi
- The Nuggets translation Project
- Permanent link to this article: github.com/xitu/gold-m…
- Translator: Inchill
- Proofreader: PlusMultiply0
Understand the Symbol type in JavaScript ES6
JavaScript is one of the cores of Web development. JavaScript, also known as ECMAScript, was standardized in 1997. Since then, the following raw values have appeared in the language.
- Undefined
- Null
- Big Int(new in ES2020)
- Boolean
- Number
- String
Null is marked as one of the original values in JavaScript because its role is obviously original. But in some cases, NULL is not as “primitive” as it first appears. Each Object is derived from a null value, so the Typeof operator returns an Object for it.
But with the release of ES6 in 2015, a new primitive type, Symbol, was added. They are quite different from previous original values. They are just values, not strings, numbers or objects. They’re just Symbols.
What is this new primitive type about?
Symbol Primitive type is about uniqueness. Its value is a unique identifier. You can simply call Symbol() and get a unique identifier. Optionally, you can also pass the description as the constructor argument.
One thing you should remember is that Symbol is always unique. Even if you pass the same description as a constructor parameter to both Symbols, they will still be different.
Many people think of Symbol as a way of obtaining unique values. But only part of it is right. Although Symbol is unique, you will never get a unique value by logging it on the console. You can assign it to only one variable and use that variable as a unique identifier.
Symbol, in other words, don’t give a unique values, such as a may seem like a af1ae40223348538204f8c3a58f34 ID 285. But instead, when you print a Symbol, you receive Symbol() or Symbol(description). Remember that it is not a string, but a plain old Symbol.
typeof Symbol(a)"symbol"
Copy the code
You can get the string by calling the toString() method on Symbol. But this also only gives you a string of previously obtained values.
Matters needing attention
Does not automatically convert to a string
Window.alert () takes an argument of type string. However, even if you pass a number or even null, you will not receive an error message. JavaScript implicitly converts the data type to a string and displays it. But for Symbol, JavaScript does not implicitly convert it to a string. This is to keep the two separate, as they are fundamentally different and should not be accidentally converted from one to the other.
Use Symbol as the key in the object literal
Look at the code below.
let id = Symbol("id");
let obj = {
id: 1
};
// This is the string "id" as the key. Rather than a Symbol
Copy the code
In the example above, although we assign an ID attribute to the obj object, it is not the ID variable we defined in the previous line. To set the ID variable to the key, we should use [].
let id = Symbol("id");
let obj = {
[id]: 1
};
Copy the code
Similarly, you cannot access properties with Symbol keys using dot syntax. You must access the property using the square brackets mentioned above.
console.log(obj.id);
//undefined
console.log(obj[id]);
/ / 1
Copy the code
A common object inspection function skips Symbol
Because Symbol is designed to avoid collisions, the Symbol attribute is skipped in JavaScript’s most common object checking functions, such as for-in loops. In the Object. The keys and Object (obj). GetOwnPropertyNames (obj) will also be ignored as properties in key Symbol.
Also note that when you use json.stringify (), the object’s Symbol attribute is ignored.
Global Symbol registry
As we saw above, a Symbol is unique even if the same description is passed as an argument. However, in some cases, you need to share a Symbol across multiple pages or modules within the same page. In this case, you can use the global Symbol registry.
While this sounds like a complex system, its interfaces are fairly simple to use.
Symbol.for(key)
This method searches the global Symbol registry for existing symbols and returns when found. If it is not found, it creates a Symbol using the passed living global Symbol registry and returns it.
let userId = Symbol.for('user_id');
// Read from the global Symbol table
// Create a Symbol if it is not found
let userID = Symbol.for('user_id');
// Read from the global Symbol table
console.log(userID === userId);
// true
Copy the code
Symbol.keyFor(sym)
This method is the opposite of the symbol.for () method. This retrieves the shared Symbol key for the given Symbol from the global Symbol registry.
// Read from the global Symbol table
let userId = Symbol.for('user_id');
let userName = Symbol.for('username');
console.log(Symbol.keyFor(userId));
// user_id
console.log(Symbol.keyFor(userName));
// username
Copy the code
Use cases
Hidden attribute
Suppose you want to develop a library to sort a list of items.
How would you solve it?
You have several solutions. You can ignore whether the list is sorted and reorder it. Although the end result will be a sorted list, the algorithm is inefficient because it performs the actual sorting process on sorted arrays.
Rather than implementing an overly large solution, we can simply set a flag to indicate whether the list is sorted. This makes it easy to sort lists only when they are not sorted.
The implementation looks similar.
if(! list.isSorted) { sortAlgorithm(list); } list.isSorted =true;
Copy the code
While the above implementation does its job well, it fails in some respects.
- use
for-in
orObject.keys()
Any other code that accesses the list can trip up your properties. - If you are using a library to implement the sorting algorithm, the smart library owner is probably already set up
isSorted
Mark. If the context is larger, you’re overdoing the same thing. - If you are the owner of the library, a smart developer would try to set the flag himself, similar to the above.
In this case, symbols are perfect because they avoid conflicts.
let id = Symbol('id');
let user = {
name: "Jane Doe",
[id]: 1
};
console.log(user[id]);
/ / 1
// ---------------------------------
// Someone tried to add their own ID to user
user.id = '124C';
console.log(user[id]);
/ / 1
console.log(user.id);
// '124C'
// ---------------------------------
// Someone tried to add their ID as Symbol to the user
let id2 = Symbol('id');
user[id2] = '9990X';
console.log(user[id]);
/ / 1
console.log(user.id);
// '124C'
console.log(user[id2]);
// '9990X'
Copy the code
The system Symbol
JavaScript uses several internal symbols to fine-tune its performance in various aspects. Some of them are,
- Symbol.asyncIterator
- Symbol.hasInstance
- Symbol.isConcatSpreadable
- Symbol.iterator
You can read more about it here.
Note: Symbol is not 100% completely hidden
You can still use the Object. GetOwnPropertySymbols (obj) and Reflect ownKeys (obj) method to receive such as Object key Symbol. You may be wondering why. I personally believe that Symbol was created to avoid accidental naming conflicts. If someone really wants to override the Symbol property key, then I think they might.
Common problems with Symbol in React
In the Bits and Pieces editor discussion, I was asked to address the issue of Symbol in React JS. Below is a link to the questions raised.
Use Symbol for keys of children of arrays or iterators · Issue #11996 · facebook/react, github.com
Features that are being required to be implemented
For those of you who haven’t read the links above or don’t know what’s going on, here’s a summary for you.
React developers should be familiar with the concept of keys. Here is a glossary taken from the React document.
“Keys” are special string attributes that need to be included when creating an array of elements. The React key helps React identify items that have changed, been added or removed. You should provide keys for elements in an array to give them a stable identity.
I believe the above clip explains what a bond is.
The fundamental reason bonds exist is uniqueness. It needs to be able to uniquely identify sibling elements in the array. This sounds like a good Symbol use case because they are unique and can be used to uniquely identify each sibling element in an array.
However, when you add Symbol as the key of an array element, you receive the following error.
The reason for the above error is that the key type should be string. If you remember what we mentioned earlier, symbols are not strings, and they do not implicitly convert themselves like other primitive data types.
One feature being requested is to allow symbols to be used natively as keys, since they do not automatically convert themselves to strings.
Why did the React team reject and close the issue
Dan Abramov commented and closed the issue, stating that “unless there is a misunderstanding, I can’t see a practical use case that allows this use of Symbol”. He also mentioned that you can simply use a “custom ID or username” or something that comes with where you process the data.
I would like to present my views from two perspectives.
First, in some cases, you might be working with lists of data without ids. This can happen when data is collected from the front end and displayed as a list. Consider the example of a list of numbers I used in my demonstration. What if the numbers are entered by the user? You will have to set up a counter to assign a unique key to each entry. Some people use the method of assigning an array index to each element, but this is a very bad idea. You cannot use the input value as a key because there may be duplicate input. As suggested, Symbol would be an easier option.
But…
There is something fundamentally wrong with the proposal. Look at the code below.
<ul>
<li key={Symbol()}>1</li>
<li key={Symbol()}>2</li>
<li key={Symbol()}>3</li>
<li key={Symbol()}>1</li>
</ul>
Copy the code
As you can see, all four keys are unique. When an element value changes, React knows which one has changed and triggers a rebuild. But when the DOM tree is rebuilt, the key for that particular element changes again, because Symbol() is given a unique value each time it is used inline. The keys are different each time you render, which forces React to remount elements or components.
If you are not sure how the DOM tree building process and change detection works in this case, read the instructions in this document carefully.
This rerendering problem can be avoided by using the global Symbol registry — symbol.for (key), because each time a Symbol is called, the Symbol is looked up in the global registry, and if found, it is returned.
But once again….
There are problems with this approach. To retrieve a Symbol from the global registry, you should provide the Symbol’s key, which itself is unique. If you think about it, the key itself is unique for identifying each element. So why do we need to create a Symbol on that instance?
Pay attention to
Eduardo, however, provides a solution where once objects or arrays are initialized using Symbol, they are never reinitialized. This means that the value is not recalculated on each render, so the value (Symbol) will always be the same. This method only works in certain situations.
import React from 'react';
const TODO = [
{id: Symbol(), content: 'Wake up'},
{id: Symbol(), content: 'Go to sleep'},];const App = () = > {
return (
<>
{TODO.map(({id, content}) => (
<p key={id}>{content}</p>
))}
</>
);
};
export default App;
Copy the code
You should note that all of the given solutions can be used, but they trigger unnecessary remounts and cause unnecessary load on memory and CPU. Our goal is to come up with a solution so that using Symbol can also be efficient.
If you have any comments, feel free to comment below.
Thanks for reading and have fun coding.
Reference documentation
- JavaScript Info
- Mozilla Blog
- MDN Docs
- ECMA Script Specs
- React Docs
If you find any mistakes in your translation or other areas that need to be improved, you are welcome to the Nuggets Translation Program to revise and PR your translation, and you can also get the corresponding reward points. The permanent link to this article at the beginning of this article is the MarkDown link to this article on GitHub.
The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, front-end, back-end, blockchain, products, design, artificial intelligence and other fields. If you want to see more high-quality translation, please continue to pay attention to the Translation plan of Digging Gold, the official Weibo, Zhihu column.