Understand and use symbols in ES6

As we all know, ES5 provides 6 basic data types: Number, String, Boolean, Null, undefined, Object. In the later ES6, a new data type symbol was added, but will we feel that this new data type is different? Is there any scenario where it will be used in actual development work? So today let’s get to know this new data type together.

1. Basic concepts:

symbol is a primitive data type that represents a unique value.

2.The uniqueness and particularity of Symbol:

  • symbol is generated through the Symbol function:
    let a = Symbol()
    typeof a   // 'symbol'

    The code here generates a unique value a through the Symbol function. The data type of a can be determined to be Symbol through typeof.

  • Symbol.prototype.description: When creating a Symbol, you can add a description to represent the description of the Symbol instance, mainly to make it easier to distinguish when printing on the console or converting a string.
    let s1 = Symbol('s1')
    let s2 = Symbol('s2')
     
    s1  //  Symbol(s1)
    s2  //  Symbol(s2)
     
    s1.toString()   // "Symbol(s1)"
    s2.toString()   // "Symbol(s2)"

    By passing in the parameters s1 and s2, the two symbols are described. If no description is added, two symbols will be printed when s1 and s2 are printed on the console, making it inconvenient to distinguish.

     At the same time, you can also directly return the Symbol description through the instance attribute description. For example: s1.description //'s1'.

  • When the parameter of the Symbol function is an object, the toString method of the object is called, converted to a string, and then a symbol value is produced.
    let obj = {
        toString () {
            return 'abc'
        }
    }
     
    let sym = Symbol(obj)
    sym   // Symbol('abc')

     

  • The parameters of the Symbol function are only descriptions of the symbol value, so the return values ​​​​of the symbol functions with the same parameters are also unequal (demonstrating its independent uniqueness ).
let s1 = Symbol()
let s2 = Symbol()
s1 === s2       // false
 
let s1 = Symbol('a')
let s2 = Symbol('a')
s1 === s2       // false
  • Symbols also cannot be directly evaluated with other data types.
let sym = Symbol('My symbol');
 
"your symbol is " + sym
// TypeError: can't convert symbol to string
 
`your symbol is ${sym}`
// TypeError: can't convert symbol to string
  • Although symbol cannot be directly calculated with other data types, Symbol values ​​can be converted to strings or Boolean values, but they cannot be converted to numeric values.
let sym = Symbol('My symbol');
 
String(sym) // 'Symbol(My symbol)'
sym.toString() // 'Symbol(My symbol)'
 
 
Boolean(sym) // true
!sym  // false
 
Number(sym) // Cannot convert a Symbol value to a number
  • Symbol can be used as attribute name.
let mySymbol = Symbol();
 
// 第一种写法
let a = {};
a[mySymbol] = 'Hello!';
 
// 第二种写法
let a = {
  [mySymbol]: 'Hello!'
};
 
// 第三种写法
let a = {};
Object.defineProperty(a, mySymbol, { value: 'Hello!' });
 
// 以上写法都得到同样结果
a[mySymbol] // "Hello!"

       When using Symbol as the attribute name, please note: you cannot use the dot operator .

       The value of this attribute cannot be obtained through a.mySymbol, because the dot operator is always followed by a string, so the value that mySymbol refers to as the identification name will not be read, causing the attribute name of a to be a string instead of a Symbol value.

  • Symbol is used as a property name. When traversing the object, the property will not appear in the for...in, for...of loops, nor will it be used by Object.keys(), Object.getOwnPropertyNames(), JSON.stringify( )return.
let obj = {
    a: 1,
    [Symbol]: 3
}
obj // {a: 1, Symbol(): 3}
 
// Object.keys 无法获取到
Object.keys(obj)    // ["a"]
 
// for of 也无法打印出来
for(item in obj) {
    console.log(item)
}
//  a
  • All Symbol property names of the specified object can be obtained through the Object.getOwnPropertySymbols() method. This method returns an array whose members are all Symbol values ​​used as property names of the current object.
const obj = {};
let a = Symbol('a');
let b = Symbol('b');
 
obj[a] = 'Hello';
obj[b] = 'World';
 
const objectSymbols = Object.getOwnPropertySymbols(obj);
 
objectSymbols
// [Symbol(a), Symbol(b)]
  • Symbol.for(),Symbol.keyFor()

In some special cases, you may need to use the same Symbol value during development. This can be done through Symbol.for. Symbol.for accepts a string as a parameter, and then searches for a Symbol value with that parameter as its name. If there is, return the Symbol value, otherwise create a new Symbol value with the string as its name and register it globally.

let s1 = Symbol.for('foo');
let s2 = Symbol.for('foo');
 
s1 === s2 // true

In the above code, s1 and s2 are both Symbol values, but they are generated by the Symbol.for method with the same parameters, so they are actually the same value.

Both writing methods Symbol.for() and Symbol() will generate new Symbols, but their difference is that the former will be registered in the global environment for search, while the latter will not. Symbol.for() will not return a new Symbol type value every time it is called. Instead, it will first check whether the given key already exists, and if it does not exist, it will create a new value.

For example, if you call Symbol.for("cat") 30 times, the same Symbol value will be returned each time, but calling Symbol("cat") 30 times will return 30 different Symbol values.

Symbol.for("bar") === Symbol.for("bar")
// true
 
Symbol("bar") === Symbol("bar")
// false

Since Symbol()there is no registration mechanism in the writing method, a different value will be returned every time it is called.

 

The Symbol.keyFor() method returns the key of a registered Symbol type value.

let s1 = Symbol.for("foo");
Symbol.keyFor(s1) // "foo"
 
let s2 = Symbol("foo");
Symbol.keyFor(s2) // undefined

Because variable s2 belongs to an unregistered Symbol value, undefined is returned.

Note that the name registered by Symbol.for() for the Symbol value is in the global environment, regardless of whether it is run in the global environment or not.

function foo() {
  return Symbol.for('bar');
}
 
const x = foo();
const y = Symbol.for('bar');
console.log(x === y); // true

In the code, Symbol.for('bar') is run inside the function, but the generated Symbol value is registered in the global environment. Therefore, running Symbol.for('bar') for the second time can get this Symbol value.

Therefore, you can use the global registration feature of Symbol.for() to get the same value in different iframes or service workers.

 

3. Practical applications of symbols

  • Use Symbol as the unique attribute name (key) of the object: Since each Symbol value is not equal, this means that the Symbol value can be used as an identifier for the object's attribute name, which ensures that attributes with the same name will not appear. . This is useful when an object is composed of multiple modules, to prevent a key from being accidentally overwritten or overwritten.
let obj = {
   [Symbol('name')]: '一斤代码',
   age: 18,
   title: 'Engineer'
}
 
Object.keys(obj)   // ['age', 'title']
 
for (let p in obj) {
   console.log(p)   // 分别会输出:'age' 和 'title'
}
 
Object.getOwnPropertyNames(obj)   // ['age', 'title']

According to my above introduction and this code, we can know that the Symbol type key cannot be enumerated through Object.keys() or for...in. It is not included in the object's own property name collection (property names). middle. Therefore, using this feature, we can use Symbol to define some properties that do not require external operations and access.

It is precisely because of this feature that when using JSON.stringify() to convert an object into a JSON string, the Symbol attribute will also be excluded from the output content:

JSON.stringify(obj)  // {"age":18,"title":"Engineer"}

During the development process, we can use this feature to better design our data objects, making "internal operations" and "external selective output" more elegant.

 

Of course, we do not rule out the need to obtain all the key values ​​​​of the object, so there will also be some APIs specifically for Symbol, such as: Reflect.ownKeys

// 使用Object的API
Object.getOwnPropertySymbols(obj) // [Symbol(name)]
 
// 使用新增的反射API
Reflect.ownKeys(obj) // [Symbol(name), 'age', 'title']
  • Use symbols to define constants to ensure the uniqueness of constants.
const obj = {}
obj.alw = {
    DEBUG: Symbol('debug'),
    INFO: Symbol('info'),
    WARN: Symbol('warn')
}  //通过symbol定义一组常量,保证这组常量的值都是不相等的

Through this property, magic strings can be eliminated .


Magic string: A specific string or value that appears multiple times in the code and forms a strong coupling with the code. Strings with too many correlations will lead to unclear meanings of variables, so magic characters should be eliminated as much as possible. Strings should be replaced by variables with clear meanings;

Coupling: often used to express the connection between blocks. The stronger the coupling, the stronger the connection between its code blocks or modules. Code blocks or modules are designed for the independence of their functions, so low coupling is often preferred;

Cohesion: often used to express the internal connection of a block. The stronger the cohesion, the stronger the functional strength of the module, that is, the closer the connection between the elements in a module (between language names, between program segments), so it is generally Prefer high cohesion;


To put it simply and crudely, a magic string is a specific string or value that appears multiple times, forming a strong connection with the code, which is not conducive to future modification and maintenance.

Example:

function getArea(shape, options) {
  let area = 0;
 
  switch (shape) {
    case 'Triangle': // 魔术字符串
      area = .5 * options.width * options.height;
      break;
    /* ... more code ... */
  }
 
  return area;
}
 
getArea('Triangle', { width: 100, height: 100 }); // 魔术字符串

In the above code, the string Triangle is a magic string. It appears many times and forms a "strong coupling" with the code, which is not conducive to future modification and maintenance.

 

A commonly used method to eliminate magic strings is to write it as a variable. This method is also called variable localization, so that if you need to maintain and modify it, you only need to modify the value of the originally defined variable.

const shapeType = {
  triangle: 'Triangle'
};
 
function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}
 
getArea(shapeType.triangle, { width: 100, height: 100 });

In the above code, Triangle is written as the triangle attribute of the shapeType object, thus eliminating strong coupling.

If you analyze it carefully, you can find that it does not matter which value shapeType.triangle is equal to, as long as it does not conflict with the values ​​​​of other shapeType attributes. Therefore, this is a good place to use Symbol values ​​instead.

const shapeType = {
  triangle: Symbol()
};
 
function getArea(shape, options) {
  let area = 0;
  switch (shape) {
    case shapeType.triangle:
      area = .5 * options.width * options.height;
      break;
  }
  return area;
}
 
getArea(shapeType.triangle, { width: 100, height: 100 });
  • Use Symbol to define private properties/methods of a class in Javascript : Because there is no private in Javascript, all properties and methods defined on the class are publicly accessible, but now it can be achieved through Symbol and modularization Private properties and methods of the class.

Example:

a.js

const PASSWORD = Symbol();  // 这个 PASSWORD 只能在a.js中使用了
 
class Login {
    constructor (username, password) {
        this.username = username
        this[PASSWORD] = password
    }
     
    checkPassword (pwd) {
        return this[PASSWORD] === pwd
    }
}
 
export default Login

 

b.js

import Login from './a'
 
const login = new Login('chencc', '123')
 
login.checkPassword('123')  // true
 
login[PASSWORD] // ReferenceError: PASSWORD is not defined

Because PASSWORD is defined in the module where a.js is located, external modules cannot obtain this Symbol, and it is impossible to create an identical Symbol. Therefore, this PASSWORD can only be used inside a.js, so use The class attributes it defines cannot be accessed outside the module, achieving a privatization effect.

 

 

Guess you like

Origin blog.csdn.net/weixin_46422035/article/details/114698355