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:

  1. It can be used as an object property name. Just strings andsymbolType can be used as an object property name.
  2. No twosymbolThe 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?