Symbol and Symbol attribute

ES6 introduces a new basic data type, Symbol, in addition to JS’s existing basic types.

The Symbol() function returns a value of type Symbol, which has static attributes and static methods. Its static attributes expose several built-in member objects; Its static method exposes the global Symbol registry and is similar to the built-in object class, but as a constructor it is incomplete because it does not support the syntax: new Symbol().

? > Each Symbol() returns a unique Symbol value. A symbol value can be used as an identifier for object properties; This is also the meaning of symbol.

Creating symbolic values

Symbols have no literal form, which is unique among JS primitive types. To create a symbol value, we use the global function symbol ().

    const firstName = Symbol(a)const person = {}

    person[firstName] = 'Anti-celery League leader'
    console.log(person[firstName]) // Anti-celery league leader
Copy the code

Using the above method we create a variable of symbol type firstName as an attribute of the Person object and use the symbol value every time the attribute is accessed.

Symbols to describe

The Symbol() function can also accept an additional parameter to describe the Symbol value, but the description cannot be used to access the corresponding attribute, but can be used for debugging. As follows:

    const name = Symbol('name')
    const person = {}

    person[name] = 'Anti-celery League leader'

    console.log(person[name]) // Anti-celery league leader
    console.log(name) // Symbol(name)
Copy the code

The symbol’s Description is stored in the internal property [[Description]], which is read when invoked explicitly or implicitly.

When we print the name symbol using console.log(), console.log() implicitly calls the toString() method of the name variable. As follows:

    const name = Symbol('name')
    const str = name.toString()

    console.log(str)  // Symbol(name)
    console.log(name) // Symbol(name)
Copy the code

? > There is currently no way to access the [[Description]] property directly from code other than using the toString() method.

Identification symbol value

Since symbols are values of primitive types, we can use the Typeof operator to determine whether a variable is a symbol. As follows:

    const symbol = Symbol('test symbol')

    console.log(typeof symbol === 'symbol') // true
Copy the code

Using symbolic values

We can use symbols any time we want to use the name of the property to be evaluated. It can also be used in calls to Object.defineProperty() or Object.defineProperties(). As follows:

    const name = Symbol('name')

    // Use the name of the attribute to be calculated
    const person = {
        [name]: 'Anti-Celery League Leader 1'
    }

    // Make this property read-only
    Object.defineProperty(person, name, {
        writable: false
    })

    const restName = Symbol('name')

    Object.defineProperties(person, {
        [restName]: {
            value: 'Anti-Celery League Leader 2'.writable: false}})console.log(person[name])       // Anti-celery league leader 1
    console.log(person[restName])   // Anti-celery alliance leader 2
Copy the code

The object.defineProperty () method directly defines a new property on an Object, or modifies an existing property of an Object, and returns the Object.

The object.defineProperties () method directly defines new properties or modifies existing properties on an Object and returns the Object.

Shared symbolic value

Suppose we want to use the same symbolic value in different code segments, such as the same symbolic attribute in two different object types, to represent a unique identifier. To do this, ES6 provides us with a “global symbol Registry” that we can access at any point in time.

To create shared Symbol values, use the symbol.for () method instead of the Symbol() method. As follows:

    const id = Symbol.for('id')
    const obj = {}

    obj[id] = 123

    console.log(obj[id]) / / 123
    console.log(id)      // Symbol(id)
Copy the code

? The > symbol.for () method accepts only a single string parameter as the identifier of the value of the target Symbol and as the Symbol’s description.

Before the symbol.for () method is created, the global Symbol registry is first searched to see if there is a Symbol value with a key of ID. If it exists, it returns the existing symbol value; Otherwise, create a new symbol value. As follows:

    const id1 = Symbol.for('id')

    const obj = {
        [id1]: 123
    }

    console.log(obj[id1]) / / 123
    console.log(id1)      // Symbol(id)

    const id2 = Symbol.for('id')

    console.log(id1 === id2) // true
    console.log(obj[id2]) / / 123
    console.log(id2)      // Symbol(id)
Copy the code

In the above example, ID1 and ID2 contain the same Symbol value, which is created on the first call to symbol.for (‘id’), while the second call detects the Symbol value in the global Symbol registry to create and returns the existing Symbol value.

Symbol.keyFor()

Retrieves the corresponding key value based on the symbol value from the global Symbol registry. As follows:

    const id = Symbol.for('id')

    console.log(Symbol.keyFor(id)) // id
Copy the code

Conversion of symbolic values

Type conversion is an important part of THE JS language and can be very flexible to convert one data type to another. However, symbolic types are very inflexible when it comes to conversions, because other types lack reasonable equivalence with symbolic values, especially when symbolic values cannot be converted to strings or values.

Since symbolic values cannot be converted to strings or numbers, it is assumed that concatenating a Symbol to a string or using a mathematical operator on it will cause an error. As follows:

    const id = Symbol.for('id')

    // Throw error: Cannot convert a Symbol value to a string
    const str = id + ' '

    const symbol = Symbol.for('number')

    Error: Cannot convert a Symbol value to a number
    const num = symbol + 1
Copy the code

? It is not possible to use mathematical operators on symbols, but using logical operators does not cause errors because symbolic values are considered equivalent to true in logical operators (just like other non-null values in JS).

Retrieving symbolic properties

Want to retrieve all attributes of the Object name you can use the Object. The keys () with the Object. GetOwnPropertyNames ().

The object.keys () method returns an array of the given Object’s own enumerable properties, in order of their names and using the for… The in loop returns the same order as it iterates through the object. If the key-value of an object is not enumerable, an array of keys is returned.

Object. GetOwnPropertyNames () method returns a specified by the Object of all its attributes of the attribute name (including not enumerated attribute but does not include Symbol value as the name of the attribute) consisting of an array.

Neither of the above methods can return properties of symbol types to keep their functionality unchanged in ES5. In order to let we can retrieve the Object’s symbol type attributes, ES6 Object. The new getOwnPropertySymbols () method.

Object. GetOwnPropertySymbols () method returns a given Object itself all the attributes of the Symbol of the array.

    const symbol = Symbol.for('id')

    const obj = {
        a: 'aaa',
        [symbol]: 123.b: 'bbb'
    }

    const arr = Object.getOwnPropertySymbols(obj)

    console.log(arr.length)  / / 1
    console.log(arr[0])      // Symbol(id)
    console.log(obj[arr[0]]) / / 123
Copy the code

The code above Object obj has two common properties and a Symbol attribute, we use the Object. The getOwnPropertySymbols () method returns an array, then only contains its symbolic value.

All object initializations do not contain any free symbol type attributes, but objects can inherit type-conforming attributes from their prototypes. ES6 predefined some of these properties, which are called “well-known conformance.”

Use well-known symbols to expose internal methods

ES6 allows you to use stereotype properties of symbolic types to define the underlying behavior of some objects.

ES6 defines “well-known symbols” to represent common behaviors in JS that were previously thought to be internal operations. Each well-known Symbol corresponds to a property of the global Symbol object, such as symbol.create.

Some of these well-known symbols are described below:

Iterative symbols

Symbol.iterator: A method that returns the default iterator of an object. Be the for… Of use.

Symbol.asyncIterator: a method that returns the default asynchronous iterator for an object. Be used for await of.

Regular expression symbols

Symbol.match: A method used to match strings and also to determine whether an object can be used as a regular expression. Used by string.prototype.match ().

Symbol.replace: A method to replace substrings that match strings. Used by string.prototype.replace ().

Symbol.search: A method that returns the index in a string that matches the regular expression. Used by string.prototype.search ().

Symbol.split: A method of splitting a string at the index that matches the regular expression. Used by string.prototype.split ().

Other symbols

Symbol. HasInstance: A method for determining whether an object recognized by a constructor object is an instance of it. Used by instanceof.

Symbol. IsConcatSpreadable: a Boolean value that indicates that an object should be flattened array element for it. Used by array.prototype.concat ().

Symbol.unscopables: The value of an object that owns and inherits property names is excluded from the related object bound to the environment.

Symbol.species: A constructor function used to create derived objects.

Symbol.toPrimitive: a method for converting objects toPrimitive data types.

Symbol.tostringtag: String value for the default description of the object. Used by the Object. The prototype. The toString ().

Symbol.hasInstance

Each function has a symbol.hasinstance method that determines whether the specified object is an instance of the function. This method is defined on function.prototype, so all functions inherit the default behavior when faced with the instanceof operator. The symbol.hasinstance property is itself non-writable, non-configurable, and non-enumerable, ensuring that it cannot be overwritten incorrectly.

The symbol.hasinstance method accepts only a single parameter, the value to be tested. Returns true if the value is an instance of this function. As follows:

    ([] instanceof Array) // true
    // The above code is equivalent to:
    Array[Symbol.hasInstance]([]) // true
Copy the code

ES6 essentially redefines the instanceof operator as a shorthand syntax for the above method call, so using instanceof triggers a method call, essentially allowing you to change what the operator does.

We can define a function so that no object is judged to be an instance of the function. As follows:

    function Fun() {
        // no thing
    }

    // Comment this code and the result is true
    Object.defineProperty(Fun, Symbol.hasInstance, {
        value(v) {
            return false}})const obj = new Fun()

    console.log(obj instanceof Fun) // false
Copy the code

The object.defineProperty () method directly defines a new property on an Object, or modifies an existing property of an Object, and returns the Object.

Symbol.isConcatSpreadable

Built-in Symbol. IsConcatSpreadable Symbol is used to configure a particular object as an Array. The prototype. The concat () method whether the parameters of the Array elements.

By default array.prototype.concat () expands its element to concatenate the result. As follows:

    const arr1 = [ 1.2.3 ]
    const arr2 = [ 4.5 ]

    const arr3 = arr1.concat(arr2)

    console.log(arr3) // [1, 2, 3, 4, 5]
Copy the code

When we set the Symbol. IsConcatSpreadable attribute to false, as follows:

    const arr1 = [ 1.2.3 ]
    const arr2 = [ 4.5 ]

    arr2[Symbol.isConcatSpreadable] = false

    const arr3 = arr1.concat(arr2)

    console.log(arr3) // [1, 2, 3, [4, 5]]
Copy the code

For array-like objects, they are not expanded by default. Expect to expand its > element is used to connect, you need to set up Symbol. IsConcatSpreadable to true:

    const arr1 = [ 1.2.3 ]
    const obj = {
        [Symbol.isConcatSpreadable]: true.length: 2.0: 4.1: 5
    }

    const arr3 = arr1.concat(obj)

    console.log(arr3) // [1, 2, 3, 4, 5]
Copy the code

Set the Symbol. IsConcatSpreadable to false, as follows:

    const arr1 = [ 1.2.3 ]
    const obj = {
        [Symbol.isConcatSpreadable]: false.length: 2.0: 4.1: 5
    }

    const arr3 = arr1.concat(obj)

    console.log(arr3) // [ 1, 2, 3, { 0: 4, 1: 5, length: 2, Symbol(Symbol.isConcatSpreadable): false }]
Copy the code
Symbol.toPrimitive

Symbol.toPrimitive is a built-in Symbol value that exists as a function value attribute of an object and is called when an object is converted to its original value. As follows:

    const obj = {
        [Symbol.toPrimitive]: function(hint) {
            if (hint == 'number') {
                return 42
            }
            return null}};console.log(obj / 2) / / 21
Copy the code
Symbol.toStringTag

Symbol.toStringTag is a built-in Symbol that is usually used as the attribute key of an object. The corresponding attribute value should be a string representing the object’s custom type tag. Usually only built-in Object. The prototype. The toString () method to read the label and put it in his own return values. As follows:

    function Cat() {
        // no thing
    }

    Cat.prototype[Symbol.toStringTag] = "Cat"

    const cat = new Cat()

    console.log(cat.toString())
    console.log(Object.prototype.toString.call(cat))
Copy the code

Unless specified otherwise, all objects inherit the Symbol. ToStringTag attribute from Object.prototype, whose default value is the string “Object”

Symbol.match

Symbol.match specifies that regular expressions are matched instead of strings. The string.prototype.match () method calls this function. As follows:

    const reg = /foo/

    reg[Symbol.match] = false

    console.log('/foo/'.startsWith(reg)) // true

    console.log('/123foo123/'.endsWith(reg)) // false
Copy the code

If Symbol. Match is set to false, expression checking using the match attribute will assume that the image is not a regular expression object. The startsWith and endsWith methods will not raise TypeError.

We can also define a regular expression object using symbol. match. As follows:

    const reg = {
        [Symbol.match]: function(value) {
            return value.length > 6 ? 'Length greater than 6' : 'Length less than 6'}}const len1 = 'Hello world'
    const len2 = 'Hello'


    console.log(len1.match(reg)) // The length is greater than 6
    console.log(len2.match(reg)) The length is less than 6
Copy the code
Symbol.replace

The symbol.replace property specifies the method to be called when a string replaces the matched string. The string.prototype.replace () method calls this method.

We can also define a regular expression object using symbol.replace. As follows:

    const reg = {
        [Symbol.replace]: function(value, replacement) {
            return value.length === 11 ? replacement + value.substring(11) : value
        }
    }

    const len1 = 'Hello world'
    const len2 = 'Hello'


    console.log(len1.replace(reg, '12345')) / / 12345
    console.log(len2.replace(reg, '12345')) // Hello
Copy the code
Symbol.search

Symbol.search specifies a search method that takes a user-typed regular expression and returns the subscript that the regular expression matches in the String. This method is called string.prototype.search ().

We can also define a regular expression object using symbol.search. As follows:

    const reg = {
        [Symbol.search]: function(value) {
            return value.length > 10 ? true : false}}const len1 = 'Hello world'
    const len2 = 'Hello'

    console.log(len1.search(reg)) // true
    console.log(len2.search(reg)) // false
Copy the code
Symbol.split

Symbol.split points to the method of splitting a string at the index of a regular expression. This method is called with string.prototype.split ().

We can also define a regular expression object using symbol.split. As follows:

    const reg = {
        [Symbol.split]: function(value) {
            return value.length > 10 ? [] : [value]
        }
    }

    const len1 = 'Hello world'
    const len2 = 'Hello'

    console.log(len1.split(reg)) / / []
    console.log(len2.split(reg)) // [ Hello ]
Copy the code

conclusion

Symbol is a new basic type value introduced in JS that can be used to create properties that are not enumerable and cannot be accessed without symbols.

Although symbol types cannot create truly private properties, they are difficult to modify, and we can use symbols when creating a method that we do not want to be modified by the user.

Adding a description to a symbol makes it easier to determine its purpose. Using global symbol registration allows us to use the same symbol value in different code segments.

Object. The keys () or Object. GetOwnPropertyNames () will not return symbolic value, therefore ES6. Adding an Object getOwnPropertySymbols () method, which allows to retrieve symbol type Object properties. You can still modify the properties of symbol types using the Object.defineProperty() and Object.defineProperties() methods.

Well-known symbols use global Symbol constants (such as symbol.hasinstance) to define functions for regular objects that were previously reserved for internal use. These symbols are conventionally prefixed with the Symbol., allowing developers to modify the behavior of regular objects in a variety of ways.

6 Reading notes.

  1. ES6 block-level binding, let, const declaration
  2. Strings and Regular expressions (RegExp)
  3. Functions
  4. Extended object functionality
  5. Deconstruction assignment
  6. Symbol and Symbol attribute

Refer to the article

  • MDN
  • Understanding ECMAScript 6
  • Understanding ECMAScript 6

githubaddress