What is Symbol? Why is there such a thing?

Symbol is a new data type in ES6. Symbol is a primitive value (base data type), and the Symbol instance is unique and immutable. It is created to be used as a unique token, and thus as a non-string object attribute, to ensure that object attributes use unique identifiers without the risk of attribute collisions.

usage

1. Basic usage

Symbols need to be initialized using the Symbol() function. Because symbols themselves are primitive types, the Typeof operator returns symbol for symbols.

let sym = Symbol(a);console.log(typeof sym); // symbol
Copy the code

The Symbol() function can take a string argument to describe it, which can then be used to debug the code. However, it is worth noting that multiple Symbol() functions do not have equal values even if they accept the same parameters.

let genericSymbol = Symbol(a);let otherGenericSymbol = Symbol(a);let fooSymbol = Symbol("foo");
let otherFooSymbol = Symbol("foo");

console.log(genericSymbol == otherGenericSymbol); // false
console.log(fooSymbol == otherFooSymbol); // false
Copy the code

2. Use the global symbol registry

If you need to use the same Symbol instance in more than one place in your code, you can pass in a string and use the symbol.for () method to create a reusable Symbol, similar to the singleton pattern. It looks globally to see if the same instance was created using symbol.for () based on the argument passed in. If so, reuse it; if not, create it

let fooGlobalSymbol = Symbol.for("foo"); // Create a new symbol
let otherFooGlobalSymbol = Symbol.for("foo"); // Reuse existing symbols
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true
Copy the code

The difference between Symbol. For () and Symbol() : Instances created by Symbol() are always unique and will not be equal to other instances just because you pass in the same arguments, but instances created by Symbol.for() with the same arguments will be equal because they share the same Symbol instance

let fooSymbol = Symbol("foo");
let otherFooSymbol = Symbol("foo");
console.log(fooSymbol == otherFooSymbol); // false

let fooGlobalSymbol = Symbol.for("foo"); // Create a new symbol
let otherFooGlobalSymbol = Symbol.for("foo"); // Reuse existing symbols
console.log(fooGlobalSymbol === otherFooGlobalSymbol); // true
Copy the code

3. Use symbols as attributes

Properties in objects are usually strings, but you can use Symbol instances as properties. The advantage of this is that the new properties do not overwrite any of the previous properties

let s1 = Symbol("foo"),
  s2 = Symbol("bar"),
  s3 = Symbol("baz"),
  s4 = Symbol("qux");
let o = {
  [s1]: "foo val"};O [s1] = 'foo val';
console.log(o);
// {Symbol(foo): foo val}
Object.defineProperty(o, s2, { value: "bar val" });
console.log(o);
// {Symbol(foo): foo val, Symbol(bar): bar val}
Object.defineProperties(o, {
  [s3]: { value: "baz val" },
  [s4]: { value: "qux val"}});console.log(o);
// {Symbol(foo): foo val, Symbol(bar): bar val,
// Symbol(baz): baz val, Symbol(qux): qux val}
Copy the code

Note: When creating a Symbol instance as an object attribute, if the Symbol does not initially declare a variable to receive, the Symbol must iterate through all the Symbol attributes of the object to find the corresponding attribute key:

let o = {
  [Symbol("foo")]: "foo val"[Symbol("bar")]: "bar val"};console.log(o);
// {Symbol(foo): "foo val", Symbol(bar): "bar val"}
let barSymbol = Object.getOwnPropertySymbols(o).find(symbol= > symbol.toString().match(/bar/));
console.log(barSymbol);
// Symbol(bar)
Copy the code

4. Common built-in symbols

ES6 also introduces a number of commonly used built-in well-known symbols that expose language behavior that developers can directly access, rewrite, or simulate. If these default properties are modified, it is possible to change the final result of some operations. For example, if a for-of loop uses the symbol. iterator attribute on a related object, you can change the behavior of for-of when iterating over that object by redefining the value of symbol. iterator on a custom object.

5. Symbol.asyncIterator

A Generator that returns a Promise, usually with “for await of”

6. Symbol.hasInstance

According to the ECMAScript specification, this symbol represents, as a property, “a method that returns the object’s default AsyncIterator. Used by the for-await-of statement. In other words, this symbol represents a function that implements the asynchronous iterator API.

This property is defined on the prototype of Function. We all know that the instanceof operator can be used to determine whether an object instance belongs to a constructor. The mechanism is that the instanceof operator uses the symbol.hasinstance function to determine the relationship

function Foo() {}
let f = new Foo();
console.log(f instanceof Foo); // true
class Bar {}
let b = new Bar();
console.log(b instanceof Bar); // true
Copy the code

If you redefine the symbol. hasInstance property of a function, you can make the Instanceof method return something unexpected

class Bar {}
class Baz extends Bar {
  static [Symbol.hasInstance]() {
    return false; }}let b = new Baz();
console.log(Bar[Symbol.hasInstance](b)); // true
console.log(b instanceof Bar); // true
console.log(Baz[Symbol.hasInstance](b)); // false
console.log(b instanceof Baz); // false
Copy the code

Symbol.isConcatSpreadabl

This property is defined on the Array prototype

According to the ECMAScript specification, this symbol represents, as an attribute, “a Boolean value that, if true, means that the object should have its Array elements flattened with array.prototype.concat ().” The array.prototype.concat () method in ES6 selects how to concatenate an array-like (pseudo-array) object into an Array instance based on the type of object it receives. So modify the Symbol. IsConcatSpreadable’s value can modify this behavior.

Symbol. IsConcatSpreadable corresponding effect

False: Adds an entire object to the array true: adds an entire pair to the array

let initial = ["foo"];
let array = ["bar"];
console.log(array[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(array)); // ['foo', 'bar']
array[Symbol.isConcatSpreadable] = false;
console.log(initial.concat(array)); // ['foo', Array(1)]
let arrayLikeObject = { length: 1.0: "baz" };
console.log(arrayLikeObject[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(arrayLikeObject)); // ['foo', {...}]

arrayLikeObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(arrayLikeObject)); // ['foo', 'baz']
let otherObject = new Set().add("qux");
console.log(otherObject[Symbol.isConcatSpreadable]); // undefined
console.log(initial.concat(otherObject)); // ['foo', Set(1)]
otherObject[Symbol.isConcatSpreadable] = true;
console.log(initial.concat(otherObject)); // ['foo']
Copy the code

8. Symbol.iterator

According to the ECMAScript specification, this symbol represents, as a property, “a method that returns the default iterator for an object. Used by for-of statement

This property returns a Generator function, and for of in turn calls the next() method, which is why for of can be used on certain objects.

class Emitter {
  constructor(max) {
    this.max = max;
    this.idx = 0; } * [Symbol.iterator]() {
    while (this.idx < this.max) {
      yield this.idx++; }}}function count() {
  let emitter = new Emitter(5);
  for (const x of emitter) {
    console.log(x);
  }
}
count();
/ / 0
/ / 1
/ / 2
/ / 3
/ / 4
Copy the code

9. Symbol.match

According to the ECMAScript specification, this symbol represents, as a property, “a regular expression method that uses regular expressions to match strings. Used by the string.prototype.match () method.

The string.prototype.match () method evaluates regular expressions using functions with symbol.match as keys. So changing the Symbol. Match property of a regular expression gives string.prototype.match () the value you want

console.log(RegExp.prototype[Symbol.match]);
ƒ [Symbol. Match]() {[native code]}
console.log("foobar".match(/bar/));
// ["bar", index: 3, input: "foobar", groups: undefined]

class FooMatcher {
  static [Symbol.match](target) {
    return target.includes("foo"); }}console.log("foobar".match(FooMatcher)); // true
console.log("barbaz".match(FooMatcher)); // false
class StringMatcher {
  constructor(str) {
    this.str = str;
  }
  [Symbol.match](target) {
    return target.includes(this.str); }}console.log("foobar".match(new StringMatcher("foo"))); // true
console.log("barbaz".match(new StringMatcher("qux"))); // false
Copy the code

11. Symbol.search

This symbol represents, as a property, “a regular expression method that returns an index in a string that matches a regular expression. Used by the string.prototype.search () method”

12. Symbol.species

This symbol represents, as a property, “the value of a function that serves as a construct for creating derived objects.”

13. Symbol.split

This symbol represents, as a property, “a regular expression method that splits a string at the index position that matches the regular table expression. Used by the string.prototype.split () method.

14. Symbol.toPrimitive

This symbol represents, as a property, “a method that converts an object to its corresponding original value. Used by ToPrimitive abstract operation”

15. Symbol.toStringTag

This symbol, as a property, represents “a string used to create the default string description of an object.” By the method of built-in Object. The prototype. The toString ()”

16. Symbol.unscopables

This symbol, as a property, means “an object whose properties, both inherited and owned, are excluded from the with environment binding of the associated object