Before ES6 we all knew that JS had six data types: Use of Undefined, Null, Boolean, String, Number, Object, and Symbol type. Symbol type is only added to ES6.

This article will be introduced from the following aspects:

  • Introduction to value types and reference types
  • How do I declare a Symbol?
  • Why do we have a Symbol?
  • The common usage of Symbol
  • Built-in use of common Symbol values

The reading time for this article is expected to be 15 minutes.

Introduction to value types and reference types

Before we understand Symbol, we need to understand JS data types. In JS, data types are divided into two types: value types and reference types.

  • Value types: numeric (Number), character (String), Boolean (Boolean), NULL and underpay
  • Reference type: Object

The so-called value type can be understood as follows: the mutual assignment between variables refers to the opening up of a new memory space, the variable value assigned to the new variable is saved in the new opened memory; The value changes of the following two variables do not affect each other. The following code looks like this:

let weChatName ="Front End guy"; 
// Create a memory space to store the weChatName value "front-end master";
let touTiao =weChatName; 
// Create a new memory space for the touTiao variable, and store a copy of weChatName's "front-end master" value in the new memory;
//weChatName and touTiao will not affect each other's values.Copy the code

Some languages, such as C, have the concept of reference passing and value passing. JS has a similar concept, which is inferred from the data type passed. If a value is passed to a function, reassigning the value does not modify the value in the calling location. However, if you change the reference type, the modified value will also be changed where it was called.

The so-called reference type can be understood as follows: the assignment between variables is just an exchange of Pointers, rather than a copy of the object to the new variable, the object is still only one, just a more guidance ~~; The following code looks like this:

let weChat = { name: "Front End guy".regYear:"2014" }; 
// The variable weChat is an address that points to the space where the object is stored;
let touTiao= weChat; 
// Assign the weChat guide address to touTiao instead of copying the object and opening a new memory space to store it;
weChat.regYear="2018";
console.log(touTiao);
//output:{name: 'regYear ', regYear: '2018'}
// At this time, the object's attributes are modified through weChat, while the object's attributes have been changed when viewing the attributes through touTiao;Copy the code

What data type is Symbol? Here I first tell you is the value type, the following will be introduced in detail.

How do I declare a Symbol?

Symbol’s most important feature is the title of this article: unique. How do you explain this uniqueness? Just like twins, there are no obvious differences in appearance, but there are still differences in personality and hobbies. Everyone is unique. Symbol represents a unique value and is a non-equivalent Symbol. Declaring a Symbol is simple, as shown in the following code:

const s = Symbol(a);Copy the code

Symbol([description]) declaration, support an optional parameter, just for description, convenient for us to develop debugging only. Each execution of Symbol() generates a unique Symbol value, as shown in the following code:

let s1 = Symbol("My Symbol");
let s2 = Symbol("My Symbol");
console.log(s1 === s2); / / Outputs false"Copy the code

It can be seen that even if the description value parameters of Symbol are the same, their values are not the same, the description value only plays the role of description, will not change the Symbol value itself. One thing to note about describing values: Accept all values except the Symbol value.

const symbol = Symbol(a);const symbolWithString=Symbol('Front-end talent');
//Symbol(front-end)
const SymbolWithNum=Symbol(3.14); 
/ / Symbol (3.14)
const SymbolWithObj=Symbol({foo:'bar'});
//Symbol([object Object])
const anotherSymbol=Symbol(symbol); 
//TypeError: Cannot convert a Symbol value to a stringCopy the code

Symbol is a value type, not a reference type. The Symbol function is not a constructor, so you cannot use the new method to generate a Symbol object, otherwise the compiler will throw an exception, as shown in the following code:

new Symbol(a);//TypeError: Symbol is not a constructorCopy the code

Symbol is a value type, not a reference type. This means that if Symbol is passed as a parameter, it will be passed by value, not by reference, as shown in the following code:

const symbol=Symbol('Front-end talent');
function fn1(_symbol) {
    return _symbol==symbol;
}
console.log(fn1(symbol));
//output:true;
function fn2(_symbol) {
    _symbol=null;
    console.log(_symbol);
}
fn2(symbol);
//output:null;
console.log(symbol);
//Symbol(front-end)Copy the code

Why do we have a Symbol?

What is the meaning of Symbol? The author first gives a simple business scenario:

In the development of front-end JavaScript applications, it is necessary to obtain a DOM element object through the API provided by the rendering engine and keep it in the JavaScript runtime. For business purposes, the DOM element object needs to be modified and adjusted through a third-party library, that is, the DOM element object needs to be inserted with some new attributes.

Later, as new requirements arise, the same DOM element object needs to be modified again using another third-party library. Unfortunately, the third party library also needs to insert properties into the DOM element object, and the library needs to operate on the same properties as the previous third party library. In this case, it is very likely that both third-party libraries will not work properly, and developers using these third-party libraries will have difficulty locating and fixing them.

Symbol can provide a good solution to the above problems. This is because of the non-equivalence property of Symbol instance values, that is, no two Symbols are equal. In ES2015 standard, literal object can use string, number as the attribute key, but also can use Symbol as the attribute key, so we can use Symbol value of non-equivalence characteristics to achieve non-interference attribute operation.

The common usage of Symbol

1, check whether it is Symbol

How do you determine if a variable is of type Symbol? The only way to do this is to use Typeof, as shown in the following code:

const s = Symbol(a);console.log(typeof s); 
/ / Outputs "symbol"Copy the code

2. Used as a property of an object

With Symbol, we can use Symbol as an attribute of the object. The only difference is that we need to use the [] syntax to define attributes, as shown in the following code:

const WECHAR_NAME = Symbol(a);const WECHAR_REG = Symbol(a);let obj = {
  [WECHAR_NAME]: "Front End guy";
}
obj[WECHAR_REG] = 2014;
console.log(obj[WECHAR_NAME]) //output: front-end master
console.log(obj[WECHAR_REG]) //output:2014Copy the code

It is also important to note that when we use Symbol as the object’s Key, it is private. We cannot obtain the Key through enumeration, as shown in the following code:

let obj = {
    weChatName:'Front-end talent'.regYear: 2014[Symbol('pwd')]: 'wjqw@$#sndk9012',}console.log(Object.keys(obj));   
// ['weChatName', 'regYear']

for (let p in obj) {
    console.log(p)   
    // Output: 'weChatName' and 'regYear' respectively
}
console.log(Object.getOwnPropertyNames(obj));  
// [ 'weChatName', 'regYear' ]Copy the code

Keys (); keys(); keys(); In, which is not included in the property names of the object itself. Using this feature, we can define some properties that do not need external manipulation and access using Symbol. Because of this feature, when we use json.stringify () to convert objects to JSON strings, the Symbol property is also excluded from the output. Execute the following code in the above code:

console.log(JSON.stringify(obj));
//output:{"weChatName":" regYear":2014}Copy the code

Based on this feature, we can better design our data objects, making “internal operation” and “external selective output” more flexible.

Isn’t there any way to get the object properties defined by Symbol? Private is not absolute, we can obtain it through some API function, execute the next code in the above code:

// Use the Object API
console.log(Object.getOwnPropertySymbols(obj)); // [Symbol(pwd)]

// Use the new reflection API
console.log(Reflect.ownKeys(obj));// [Symbol(pwd), 'age', 'title']Copy the code

Define the private properties/methods of the class

We all know that in JS, there is no access control keyword private in object-oriented languages such as Java. All properties or methods defined on a class are publicly accessible. As the author mentioned above, object attributes are private, which can only be realized by defining class private attributes and methods, as shown in the following code:

Let’s create an A.js file, as follows:

const PASSWORD = Symbol(a);class Login {
  constructor(username, password) {
    this.username = username;
    this[PASSWORD] = password;
  }
  checkPassword(pwd) {
      return this[PASSWORD] === pwd; }}export default Login;Copy the code

We are creating a file b.js and importing a file a.js as follows:

import  Login from './a.js';
const login = new Login('admin'.'123456');
console.log(login.checkPassword('123456'));  // true
console.log(login.PASSWORD);  // undefined
console.log(login[PASSWORD]);// PASSWORD is not defined
console.log(login["PASSWORD"]); // undefinedCopy the code

Because the Symbol constant PASSWORD is defined in the module where A. Js is located, the outside module cannot obtain this Symbol, and it is impossible to create an identical Symbol (because symbols are unique). Therefore, the Symbol of this PASSWORD can only be used within A. js, so the class attributes defined by it cannot be accessed outside the module, thus achieving the effect of privatization.

Create a shared Symbol

Although symbols are unique, there are some business scenarios where we need to share a Symbol. How do we achieve this? In this case, we need to use another API to create or obtain the Symbol. That is symbol.for (), which can register or obtain a global Symbol instance, as shown in the following code:

let obj = {};
(function(){
 let s1 = Symbol("name");
 obj[s1] = "Eden"; }) ();console.log(obj[s1]);
//SyntaxError: Unexpected identifier cannot be accessed here
(function(){
 let s2 = Symbol.for("age");
 obj[s2] = 27; }) ();console.log(obj[Symbol.for("age")]); / / the Output "27"Copy the code

Symbol.for() registers a globally scoped Symbol if the Key has never been used, and returns a Symbol equivalent to the Symbol created the first time, as shown in the following code:

const symbol=Symbol.for('foo');
const obj={};
obj[symbol]='bar';
const anotherSymbol=Symbol.for('foo');
console.log(symbol===anotherSymbol);
//output:true
console.log(obj[anotherSymbol]);
//output:barCopy the code

The value and meaning of Symbol are commonly used

In addition to creating Symbol values ourselves, ES6 uses them everywhere in the ECMAScript engine. We can use these common values to modify the implementation logic of the underlying code for more advanced customization.

The following table summarizes common Symbol values

Define a

describe

meaning

@@iterator

“Symbol.iterator”

Used to define a method for an object and return an iterator that belongs to the corresponding object. This iterator is used for a for-of loop.

@@hasInstance

“Symbol.hasInStance”

Use to define a method for a class. This method is called with the instanceof statement to check whether an object is an instanceof a class.

@@match

“Symobol.match”

Defines a method for regular expressions that can be used by the string.prototype.match () method to check whether the corresponding String matches the current regular expression

@@replace

“Symbol.replace”

For regular expressions, objects define a method. This method is called with the use of the string.prototype.replace () method to handle the internal processing logic when the current String uses the regular expression or object as a replacement flag

@@search

“Symbol.search”

For regular expressions, objects define a method. This method is called with the use of the string.prototype.search () method to handle the internal processing logic when the current String uses the regular expression or object as a location retrieval flag

@@split

“Symbol.split”

For regular expressions, objects define a method. This method is called because of the use of the string.prototype.split () method, which handles the internal processing logic when the current String uses the regular expression or object as a split flag

@@unscopables

“Symbol.unscopables”

Used to define a property for an object. This property describes which properties of the object can be used by the with statement.

@@isConcatSpreadable

“Symbol.isConcatSpreadable”

Used to define a property for an object. This property is used to determine whether the object is expanded as an argument to the array.prototype.concat () method.

@@species

“Symbol.species”

Used to define a static property for a class that determines the default constructor for the class.

@@toPrimitive

“Symbol.toPrimitive”

Used to define a method for an object. This method is called when the object needs to be converted to a value type, depending on the behavior of the program to determine the value to which the object needs to be converted.

@@toStringTag

“Symbol.toStringTag”

Use to define an attribute for a class. This property determines the contents of the tag when the toString() method is called on an instance of the class.

Because commonly used Symbol value is more, the author only carries on the explanation to which most commonly used several.

1, the Symbol. The iterator

Iterator we can customize an iterable object using symbol. iterator. We can use symbol. iterator as a method attribute for a method name that returns an iterator. Iterator: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol: Iterator protocol The iteration state has two attributes, as shown in the table:

Define a

describe

meaning

done

Boolean

Whether the iterator has finished iterating

value

Any

Current iteration status value

Here’s how we use symbol. iterator with iteration, as shown in the following code:

let obj = {
    array: [1.2.3.4.5].nextIndex: 0[Symbol.iterator]: function(){
        return {
         array: this.array,
         nextIndex: this.nextIndex,
         next: function(){
             return this.nextIndex < this.array.length ?
                    {value: this.array[this.nextIndex++], done: false}, {done: true}; }}}};let iterable = obj[Symbol.iterator]();
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().value);
console.log(iterable.next().done);Copy the code

The above code will print:

One, two, three, four, fivetrueCopy the code

In addition to customizing the iteration logic, we can also use the engine’s default iteration to save us code, as shown in the following code:

const arr = [1.2];
const iterator = arr[Symbol.iterator](); // returns you an iterator
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());Copy the code

The above code will be printed

{ value: 1, done: false }
{ value: 2, done: false }
{ value: undefined, done: true }Copy the code

2, Symbol. HasInstance

Use to define a method for a class. This method is called when an instanceof statement is used to check if an object is an instanceof a class. To extend instanceof’s internal logic, we can use this method to specify a static method for a class. The first parameter of this method is the object being examined. The custom method content determines the return result of the instanceof statement, as follows:

class Foo{
    static [Symbol.hasInstance](obj){
        console.log(obj);
        return true; }}console.log( {} instanceof  Foo);Copy the code

The above code will be printed

{}
trueCopy the code

3, the Symbol. The match

Symbol.match implements custom logic for strings when the match() method is used. The following code looks like this:

Before customization:

const re=/foo/
console.log('bar'.match(re));//null
console.log('foo'.match(re));
//[ 'foo', index: 0, input: 'foo', groups: undefined ]Copy the code

After using Symbol. Match:

const re=/foo/
re[Symbol.match]=function (str) {
    const regexp=this;
    console.log(str);
    return true;
}
console.log('bar'.match(re));
console.log('foo'.match(re));Copy the code

The code at the top will print:

bar
true
foo
trueCopy the code

4, Symbol. ToPrimitive

In JS development, we use implicit conversion rules, which include converting reference types to value types, but sometimes the result of implicit conversion is not what we expect. While we could override the toString() method to customize the handling of an object’s implicit conversion to a string, we would be out of the question if we needed to convert it to a number. We can use symbol.toprimitive to define a more flexible approach, as shown in the following code (for demonstration only, modified with your own business) :

const obj={};
console.log(+obj);
console.log(`${obj}`);
console.log(obj+"");
//output:
//NaN
//[object Object]
//[object Object]
const transTen={
    [Symbol.toPrimitive](hint){
        switch (hint) {
            case 'number':
                return 10;
            case 'string':
                 return 'Ten';
            default:
                 return true; }}}console.log(+transTen);
console.log(`${transTen}`);
console.log(transTen+"");
//output:
/ / 10
//Ten
//trueCopy the code

5, Symbol. ToStringTag

As mentioned in the previous table, the purpose of symbol.toStringTag is to customize the tag contents of an instance of this class when toString() is called. For example, if we define a class in development, we can use symbol.toStringTag to modify the contents of toString(), using it as the property key to specify a Getter for the type.

class Foo{
    get [Symbol.toStringTag](){return 'Bar'}}const obj=new Foo();
console.log(obj.toString());
//output:[object Bar]Copy the code

section

Today’s content is a little bit too much, we need to understand slowly, we know that Symbol value is unique, some use scenarios of Symbol, and the use of Symbol common values to rewrite the lower level of the method, let us write more flexible processing logic. Although Symbol is powerful, it needs to be mastered in practice in combination with business scenarios.


ES6 Basics Let and scope

【ES6 basics 】 Const introduction

ES6 Basics Default value

【ES6 分 】 Spread syntax

【ES6 Basics 】 Destructuring Assignment

【ES6 basics 】 Arrow functions

【ES6 Basics 】 Template String

【ES6 foundation 】Set and WeakSet

【ES6 foundation 】Map and WeakMap

More exciting content, please pay attention to the wechat “front-end talent” public number!