Symbol data type in JavaScript

1. Introduction

​Typesymbol is a basic data type newly introduced by ES6, which has static properties and static methods. Among them, static properties expose several built-in member objects, and static methods expose global symbolregistration.

​Typessymbol have the following characteristics: ① Uniqueness: each symbolvalue is unique; ② Immutability: symbolthe value cannot be modified; ③ Attribute identifier: the symbolvalue of the type can be used as the identifier of the object attribute key; ④Symbol can be used with other data Types can be operated on but cannot be cast to other data types. Using the first two features, you can avoid property name conflicts and ensure that properties are not accidentally overwritten.

Typessymbol are widely used in many libraries and frameworks. Common application scenarios include creating private properties, defining constants, defining event names, and implementing various identifier-related functions.

2. Create

1、Symbol([description])

Symbol([description])​ The value of the type is created through the function symbol, but it will not be added to the global symbol table. The optional parameter descriptionis string type data, which represents the current symboldescription. This parameter is only used as an identifier and will not affect symbolit . Uniqueness.

​ But this function is not a constructor, because this function does not support new Symbol()the syntax, and an error will be thrown if this syntax is used TypeError. Each value of the type Symbol()obtained through the function symbolis unique and unique. Even if two symbolhave the same value description, they belong to two different values.

// 创建symbol数据
let s = Symbol();
console.log(s); // Symbol()
console.log(typeof s); // symbol
// 创建symbol数据并添加描述
let s1 = Symbol('one');
console.log(s1); // Symbol(one)
console.log(typeof s1); // symbol
// 创建symbol数据并添加相同的描述
let s2 = Symbol('one');
console.log(s2); // Symbol(one)
console.log(typeof s2); // symbol
// 判断symbol数据是否相等
console.log(s1 === s2); // false
console.log(s1 == s2); // false
// 使用new关键字创建symbol数据
let s3 = new Symbol(); // Uncaught TypeError: Symbol is not a constructor

2、Symbol.for(key)

​ The fourth part is explained in the common method chapter.

3. Common attributes

1. description (read-only)

​ This property is a read-only property used to get symbolthe description string of the current value.

Case code:
// 创建symbol数据 不添加描述
let s = Symbol();
// 创建symbol数据并添加描述
let s1 = Symbol('one');

// 使用description输出symbol数据的描述
console.log(s.description); // undefined
console.log(s1.description); // one

2、hasInstance

​ This attribute is Symbola built-in static attribute of the type. The attribute value is a Symbol value, which is immutable and is used to define the @@hasInstancekey of the object's method. @@hasInstanceThe method is used to determine whether an object is an instance of a certain constructor. We can Use this attribute to customize instanceofthe behavior of an operator on a class.

When an object's prototype chain is checked using instanceofthe operator, the object's @@hasInstancemethod is called. In other words, obj instanceof Constructorit is actually called Constructor[Symbol.hasInstance](obj). Therefore, we can use a custom @@hasInstancemethod to customize whether the object is an instance of a certain constructor.

Case code:
class MyArray {
    
    
    static [Symbol.hasInstance](instance) {
    
    
      return Array.isArray(instance);
    }
}
console.log([] instanceof MyArray); // true
console.log({
    
    } instanceof MyArray); // fales

3、isConcatSpreadable

​ This property is Symbola built-in static property of the type. The property value is a Symbol value, which is immutable and is used to define @@isConcatSpreadablethe key of the object. @@isConcatSpreadablemethod is an internal Symbol that determines concat()whether the object expands its elements when the array's method is called.

​ If @@isConcatSpreadablereturns true(default value), the object elements are expanded and merged; if the method returns false, the object is added to the array as a single element to form a multi-dimensional array. We can @@isConcatSpreadablecustomize the object 's concat()expansion behavior in the method by setting the object's

Case code:
let arr1 = [1, 2, 3]
let arr2 = ['a', 'b', 'c']
// 默认为true 进行展开合并
console.log(arr1.concat(arr2)); // [1, 2, 3, 'a', 'b', 'c']
// 设置arr2 的值为false
arr2[Symbol.isConcatSpreadable] = false
// 再次合并 此时不会展开
console.log(arr1.concat(arr2)); // [1, 2, 3, ['a', 'b', 'c']]

4、iterator

​IsSymbol.iterator a built-in static property of the Symbol type. The property value is a Symbol value used to define the default iterator of the object @@iterator. @@iteratormethod is a special internal method that returns an iterator object.

When an object is traversed using for...ofa loop or the spread operator ( ), the object's method is called to obtain an iterator object. An iterator object allows you to access each element of the object in turn by calling its method. We can implement custom iterator behavior through custom methods. Custom methods should return an iterator object with methods. Each time the method is called, the iterator object should return an object containing the and properties, indicating the value of the current iteration and indicating whether the iteration has ended....@@iteratornext()@@iterator@@iteratornext()next()valuedonevaluedone

Case code:
let arr3 = [1, 2, 3]
// for..of..形式遍历
console.log('for..of..形式遍历');
for (const item of arr3) {
    
    
	console.log(item);
}
// ... 扩展运算符形式遍历
console.log('扩展运算符形式遍历');
console.log([...arr3]);
// 自定义默认迭代器
arr3[Symbol.iterator] = function () {
    
    
	let index = 0;
 	return {
    
    
       next: function () {
    
    
         if (index < arr3.length) {
    
    
           return {
    
     value: arr3[index++] * 3, done: false };
         } else {
    
    
           return {
    
     value: undefined, done: true };
         }
       }
	};
};
// 输出数组本身
console.log('输出数组本身');
console.log(arr3);
// for..of..形式遍历
console.log('for..of..形式遍历');
for (const item of arr3) {
    
    
	console.log(item);
}
// ... 扩展运算符形式遍历
console.log('扩展运算符形式遍历');
console.log([...arr3]);
// forEach 形式遍历
console.log(' forEach 形式遍历');
arr3.forEach(item => {
    
    
	console.log(item);
});
// for 形式遍历
console.log('for 形式遍历');
for (let i = 0; i < arr3.length; i++) {
    
    
	console.log(arr3[i]);
}
Results of the:

Insert image description here

5、toPrimitive

​IsSymbol.toPrimitive a built-in static property of the Symbol type, used to define @@toPrimitivethe key of the object's conversion method. @@toPrimitiveThe conversion method is an internal method used to convert an object to a primitive value. It accepts a parameter hintindicating the expected conversion type. hintThe parameter can be one of three values:

  • "default": Indicates that the object can be converted to a primitive value of any type, for example: "" + x(forced to a primitive value , not a string).
  • "number": Indicates that the object is expected to be converted to a primitive value of numeric type, for example: arithmetic operation ( +-*/).
  • "string": Indicates that the object is expected to be converted to a primitive value of string type, such as template string ( ${}), String()etc.

The value of the parameter hintis determined by many complex factors such as context and operation needs, such as: calling the operator of the object, implicit type conversion, valueOfand toStringmethods, etc.

When an object is used in a context that requires primitive values, such as arithmetic operations or string concatenation, the JavaScript engine first looks for the object's Symbol.toPrimitiveproperties. If the attribute exists and is a function, the engine will call the function and pass in the corresponding hintparameters to convert and obtain the original value of the object, that is, a string, number, or Boolean value. We can customize the conversion behavior of the object through custom @@toPrimitivemethods, fully control the original conversion process, and return the original value of the object.

Case code:
// 一个没有提供 Symbol.toPrimitive 属性的对象
const obj1 = {
    
    };
console.log(+obj1); // NaN
console.log(`${
      
      obj1}`); // "[object Object]"
console.log(obj1 + ""); // "[object Object]"

// 接下面声明一个对象,手动赋予了 Symbol.toPrimitive 属性
const obj2 = {
    
    };
obj2[Symbol.toPrimitive] = function (hint) {
    
    
	// hint 参数值是 "number"
 	if (hint === "number") {
    
    
      // 返回对象有多少条属性
      return Object.keys(obj2).length;
    }
	// hint 参数值是 "string"
	if (hint === "string") {
    
    
		// 返回对象转换成的JSON字符串
		return JSON.stringify(obj2);
	}
	// hint 参数值是 "default"
	return true;
};
console.log(+obj2); // 0  
console.log(`${
      
      obj2}`); // "{}"  
console.log(obj2 + ""); // "true"

6, match, replace, search, toStringTag and other attributes

​ Please understand yourself. . .

4. Commonly used methods

1、for()

The methodSymbol.for(key) will keysearch for the corresponding value from all declared global symbol data according to the parameters (excluding Symbol()data created through creation). If this value exists, it will return it; if it does not exist, it will create a new global symbol data keybased on this. description, and return the created data.

​ If you create data based Symbol()on this first , and then use this method to search, it will not be found, because this method only searches in global data, and creates non-global data.keydescriptionsymbolSymbol()

// 第一次使用for()方法 由于之前不存在以foo为key的symbol 所以创建一个 symbol 并放入 symbol 注册表中,键为 "foo"
const a = Symbol.for("aaa");
// 第二次使用for()方法 由于之前注册过 所以直接从 symbol 注册表中读取键为"foo"的 symbol
const b = Symbol.for("aaa");
// 验证两者是否为同一个symbol
console.log(a === b); // true

// 如果是通过Symbol()方法创建 则会创建两个key相同的symbol 但并不是同一个symbol
const c = Symbol("bbb");
const d = Symbol("bbb");
// 验证两者是否为同一个symbol
console.log(c === d); // false

2、keyFor()

The methodSymbol.keyFor(symbol) will search for the data symbolfrom all declared global symboldata based on the parameters and return the data key. If symbolthe value is found from the global data symbol, the value symbolof the is returned key, and the return value is a string type; if the value does not exist symbolor the symbolcorresponding value keyis empty, then it is returned undefined.

​ If the parameter symbolis Symbol()the data created by the method, it will not be found, because symbolthe data created by this method is not global, and the return value is undefined.

// 创建一个全局 Symbol 且有key
const a = Symbol.for("aaa");
console.log(Symbol.keyFor(a)); // "aaa"
// 创建一个全局 Symbol 但没有key
const b = Symbol.for();
console.log(Symbol.keyFor(b)); // undefined
// 创建一个非全局 Symbol 且有key
const c = Symbol("ccc");
// 创建一个全局的 Symbol 且有相同的key
const c2 = Symbol.for("ccc");
console.log(Symbol.keyFor(c)); // undefined
console.log(Symbol.keyFor(c2)); // "ccc"
// 验证两者是否为同一个symbol
console.log(c === c2); // false
// 下面的 原生Symbol 没有保存在全局 Symbol 注册表中
console.log(Symbol.keyFor(Symbol.iterator)); // undefined

3、toString()

​The objectSymbol has its own toString()methods, which override Object.prototype.toString()the methods on the prototype chain.

​ Symbol data cannot be implicitly converted to a string, so toString()a method is required to convert the data into a string.

// 创建一个symbol
const a = Symbol("aaa");
console.log(a.toString()); // Symbol(aaa)
// 创建一个全局symbol
const b = Symbol.for("bbb");
console.log(b.toString()); // Symbol(bbb)

4. Other methods such as valueOf()

​ Please understand yourself. . .

5. Related applications

1. As the only property key of the object

​CanSymbol be used as a key for object properties to ensure keythe uniqueness of the properties and avoid accidental overwriting or conflict of properties.

// 声明一个 symbol 数据
const a = Symbol('aaa');
// 声明一个全局 symbol 数据
const b = Symbol.for('bbb')
// 声明对象 利用 symbol 数据作为 key
const obj = {
    
    
    [a]: '11111111',
    a: '22222222',
    [b]: '33333333'
};
// 再次声明一个 symbol 数据 与 a 有相同的description
// 以该symbol作为key 修改数据 虽然声明时的 description 相同 
// 但是是两个不同的 symbol 数据 所以obj[Symbol('aaa')] 与 obj[a] 是两个不同的属性
// 修改其中一个不会影响另一个
obj[Symbol('aaa')] = '44444444';
// 输出属性数据 
console.log(obj[a]); // 11111111
// 此处相当于又声明了一个symbol
console.log(obj[Symbol('aaa')]); // undefined
// 再次输出对象的数据
console.log(obj);

// 可通过 symbol 数据作为 key 修改数据
// obj[a] = '44444444';
Results of the:

Insert image description here

2. Declare a unique constant

With Symbolthe uniqueness of data, we can declare constants and ensure that the constant values ​​are unique and cannot be accidentally modified or overwritten.

// 声明一个常量 且常量值为 symbol 数据
const a = Symbol("aaa");
// 在函数中匹配常量值
function myFunction(value) {
    
    
    // 判断传入的值是否与常量值相等
    if (value === a) {
    
    
      console.log("常量值被匹配");
    } else {
    
    
      console.log("常量值未匹配");
    }
}
// 传入定义的变量
myFunction(a); // 输出 "常量值被匹配"
// 传入一个新的 symbol 数据 且 description 与常量值相同
myFunction(Symbol("aaa")); // 输出 "常量值未匹配"

3. Rewrite the built-in method of the object

By utilizing Symbols and built-in properties, we can rewrite the object's built-in methods to adapt to specific business scenarios and customize the object's behavior. However, when rewriting built-in methods, you must not break the original language specifications and the expected behavior of other codes.

// 声明一个对象 并改写其内置toString方法的返回值
const myObj = {
    
    
    // 该Symbol的内置属性 决定了toString方法的返回值
    [Symbol.toStringTag]: "MyObject",
};
// 输出对象的toString方法的返回值
console.log(Object.prototype.toString.call(myObj)); // 输出 "[object MyObject]"

4. Define the private members of the class

SymbolAn identifier that can be used

// 定义一个symbol数据
const _privateMember = Symbol("private");
// 定义一个类
class MyClass {
    
    
  constructor() {
    
    
      // 定义类的私有成员 以symbol数据作为标识符
    this[_privateMember] = "私有成员";
  }
  // 私有成员的get方法
  getPrivateMember() {
    
    
    return this[_privateMember];
  }
}

// new 一个实体对象
const instance = new MyClass();
// 通过get方法正常访问
console.log(instance.getPrivateMember()); // 输出 "私有成员"
// 直接访问 无法访问
console.log(instance[_privateMember]); // 输出 undefined

5. Other uses. . .

6. Reference materials

Symbol

Guess you like

Origin blog.csdn.net/weixin_45092437/article/details/132744623