When we talk about JavaScript primitive data types, we know that there are Number, String, Null, Boolean, Undefined. ES6 introduces new base data types Symbol and BigInt. Today we will understand the type of Symbol. The Symbol type is designed to solve attribute name conflicts, along with the ability to simulate private attributes.
Introduction to the
The easiest way to create the symbol variable is with the symbol () function. Sysmbol variables are special in two ways:
- It can be used as an object property name. Just strings and
symbol
Type can be used as an object property name. - No two
symbol
The values of theta are the same.
const symbol1 = Symbol();
const symbol2 = Symbol();
symbol1 === symbol2; // false
const obj = {};
obj[symbol1] = 'Hello';
obj[symbol2] = 'World';
obj[symbol1]; // 'Hello'
obj[symbol2]; // 'World'
Copy the code
Although the call to Symbol() makes it look like an object, Symbol is actually a JavaScript primitive data type. Using new as a constructor for Symbol raises an error.
const symbol1 = Symbol();
typeof symbol1; // 'symbol'
symbol1 instanceof Object; // false
// Throws "TypeError: Symbol is not a constructor"
new Symbol();
Copy the code
Description information
The Symbol() function takes only one argument, the string description. The string argument’s sole purpose is to aid debugging, namely its toString() value. Note, however, that two symbols with the same description are not equal.
const symbol1 = Symbol('my symbol');
const symbol2 = Symbol('my symbol');
symbol1 === symbol2; // false
console.log(symbol1); // 'Symbol(my symbol)'
Copy the code
There is a global Symbol registry to which symbols created with symbol.for () are added, using its description as the index key. That is, if you create two symbols with the same description with symbol.for (), they are equal.
const symbol1 = Symbol.for('test');
const symbol2 = Symbol.for('test');
symbol1 === symbol2; // true
console.log(symbol1); // 'Symbol(test)'
Copy the code
In general, you should not use global registries unless you have a very good reason, as this can cause naming conflicts.
Naming conflicts
JavaScript has a symbol built in, which is symbol.iterator in ES6. Objects that have symbol. iterator are called iterables, which means you can use a for/of loop on them.
const fibonacci = {
[Symbol.iterator]: function* () {let a = 1;
let b = 1;
let temp;
yield b;
while (true) { temp = a; a = a + b; b = temp; yield b; }}}; // Prints every Fibonacci number less than 100for (const x of fibonacci) {
if (x >= 100) {
break;
}
console.log(x);
}
Copy the code
Why Symbol. Iterator instead of a string? Assuming that instead of symbol. iterator, the iterable needs a string attribute name ‘iterator’, like the iterable’s class:
class MyClass {
constructor(obj) {
Object.assign(this, obj);
}
iterator() {
const keys = Object.keys(this);
let i = 0;
return (function* () {if (i >= keys.length) {
return;
}
yield keys[i++];
})();
}
}
Copy the code
Instances of MyClass are iterable objects that iterate over properties of the object. But the above class has a potential flaw. Suppose a malicious user passes an object with an iterator attribute to the MyClass constructor:
const obj = new MyClass({ iterator: 'not a function' });
Copy the code
If you use for/of on obj, JavaScript will raise TypeError: obj is not iterable. As you can see, the iterator function passed to the object overrides the iterator attribute of the class. This is somewhat similar to the security issue of prototype contamination, where mindless copying of user data causes problems with special attributes such as __proto__ and constructor.
The key here is that Symbol keeps the object’s internal data and user data in harmony. Since SYsmBOL cannot be represented in JSON, there is no need to worry about passing data to the Express API with inappropriate symbol. iterator attributes. In addition, for objects that mix built-in functions with user data, such as the Mongoose Model, you can use symbols to ensure that user data does not conflict with built-in attributes.
Private property
Since no two symbols are equal, this is a handy way to simulate private attributes in JavaScript. Symbol will not appear in the Object. The keys () in the results, so unless you explicitly export a symbol, or use the Object. The getOwnPropertySymbols () function to obtain, other code won’t be able to access this property.
function getObj() {
const symbol = Symbol('test');
const obj = {};
obj[symbol] = 'test';
returnobj; } const obj = getObj(); Object.keys(obj); Obj [symbol (); obj[symbol (); obj[symbol ()'test')]; / / undefined / / getOwnPropertySymbols () can still get a symbol const reference [symbol] = Object. GetOwnPropertySymbols (obj); obj[symbol]; //'test'
Copy the code
Another reason is that symbol does not appear in the result of json.stringify (). Instead, json.stringify () ignores the symbol attribute name and value:
const symbol = Symbol('test');
const obj = { [symbol]: 'test'.test: symbol };
JSON.stringify(obj); // "{}"
Copy the code
conclusion
Using Symbol to represent the internal state of the object provides a good separation between user data and program state. It eliminates the need for certain naming conventions, such as internal attributes beginning with ‘$’. The next time you need to define a private attribute, try the Symbol type.
See this rather energetic logo, don’t pay attention to it?