Detailed explanation of JavaScript primitive data type Symbol

Detailed explanation of JavaScript primitive data type Symbol

Introduction

The easiest way to create a symbol variable is to use the Symbol() function. There are two special features of the sysmbol variable:

1. It can be used as an object attribute name. Only string and symbol types can be used as object attribute names.

2. No two symbols have the same value.

const symbol1 = Symbol();
const symbol2 = Symbol();

symbol1 === symbol2; // false

const obj = {};
obj[symbol1] = 'Hello';
obj[symbol2] = 'World';

obj[symbol1]; // 'Hello'
obj[symbol2]; // 'World'

Although calling Symbol() makes it look like an object, symbol is actually a JavaScript primitive data type. Using new with Symbol as a constructor will result in an error.

const symbol1 = Symbol();

typeof symbol1; // 'symbol'
symbol1 instanceof Object; // false

// Throws "TypeError: Symbol is not a constructor"
new Symbol();

Description

The Symbol() function takes only one parameter, the string description. The only use of this string parameter is to assist debugging, that is, its toString() value. Note, however, that two symbols with the same description are not equal.

const symbol1 = Symbol('my symbol');
const symbol2 = Symbol('my symbol');

symbol1 === symbol2; // false
console.log(symbol1); // 'Symbol(my symbol)'

There is a global symbol registry, and symbols created with Symbol.for() are added to this registry and indexed using their description. That is, if you create two symbols with the same description using Symbol.for(), they are equal.

const symbol1 = Symbol.for('test');
const symbol2 = Symbol.for('test');

symbol1 === symbol2; // true
console.log(symbol1); // 'Symbol(test)'

Generally speaking, you should not use a global registry unless you have a very good reason, as this can cause naming conflicts.

Naming Conflicts

JavaScript has a built-in symbol, which is Symbol.iterator in ES6. Objects that have a Symbol.iterator function are called iterable objects, which means you can use for/of loops on the object.

const fibonacci = {
  [Symbol.iterator]: function*() {
    let a = 1;
    let b = 1;
    let temp;

    yield b;

    while (true) {
      temp = a;
      a = a + b;
      b = temp;
      yield b;
    }
  }
};

// Prints every Fibonacci number less than 100
for (const x of fibonacci) {
  if (x >= 100) {
    break;
  }
  console.log(x);
}

Why use Symbol.iterator instead of string here? Assuming that Symbol.iterator is not used, the iterable object needs to have a string attribute named 'iterator', like the following iterable object class:

class MyClass {
  constructor(obj) {
    Object.assign(this, obj);
  }

  iterator() {
    const keys = Object.keys(this);
    let i = 0;
    return (function*() {
      if (i >= keys.length) {
        return;
      }
      yield keys[i++];
    })();
  }
}

Instances of MyClass are iterable objects, and you can traverse the properties of the object. But the above class has a potential flaw. Suppose a malicious user passes an object with an iterator property to the MyClass constructor:

const obj = new MyClass({ iterator: 'not a function' });

In this way, if you use for/of on obj, JavaScript will throw a TypeError: obj is not iterable exception. It can be seen that the iterator function passed into the object overrides the iterator property of the class. This is somewhat similar to the security issue of prototype pollution. Mindlessly copying user data will cause problems for some special properties, such as __proto__ and constructor.

The key point here is that symbol keeps the internal data of the object and the user data separate. Since sysmbol cannot be represented in JSON, you don't have to worry about passing data with an inappropriate Symbol.iterator property to the Express API. Additionally, for objects that mix built-in functions and user data, such as Mongoose models, you can use symbols to ensure that user data does not conflict with built-in attributes.

Private properties

Since any two symbols are not equal, they can be easily used to simulate private properties in JavaScript. Symbols do not appear in the result of Object.keys(), so unless you explicitly export a symbol, or retrieve it using Object.getOwnPropertySymbols(), other code cannot access this property.

function getObj() {
  const symbol = Symbol('test');
  const obj = {};
  obj[symbol] = 'test';
  return obj;
}

const obj = getObj();

Object.keys(obj); // []

// Unless you have a reference to this symbol, you cannot access this property obj[Symbol('test')]; // undefined

//Use getOwnPropertySymbols() to still get a reference to symbol const [symbol] = Object.getOwnPropertySymbols(obj);
obj[symbol]; // 'test'

Another reason is that symbol will not appear in the result of JSON.stringify(). To be more precise, JSON.stringify() will ignore the symbol attribute name and attribute value:

const symbol = Symbol('test');
const obj = { [symbol]: 'test', test: symbol };

JSON.stringify(obj); // "{}"

Summarize

Using Symbol to represent the internal state of an object can effectively isolate user data and program state. With it, we no longer need certain naming conventions, such as starting internal properties with '$'. Next time you need to define a private property, try the Symbol type!

The above is a detailed explanation of the JavaScript primitive data type Symbol. For more information about JavaScript primitive data type Symbol, please pay attention to other related articles on 123WORDPRESS.COM!

You may also be interested in:
  • Detailed explanation of the primitive data types Null and Undefined in JavaScript
  • Detailed explanation of two new primitive data types in JavaScript (Record and Tuple)

<<:  How to check whether the ports of the local computer and the remote server are connected under Linux

>>:  MySQL 8.0.2 offline installation and configuration method graphic tutorial

Recommend

Manual and scheduled backup steps for MySQL database

Table of contents Manual backup Timer backup Manu...

Detailed explanation of Nginx passively checking the server's survival status

introduce Monitors the health of HTTP servers in ...

Solutions to common problems using Elasticsearch

1. Using it with redis will cause Netty startup c...

A brief introduction to the simple use of CentOS7 firewall and open ports

Overview (official has more detailed description)...

How to uninstall MySQL cleanly (tested and effective)

How to uninstall Mysql perfectly? Follow the step...

HTML uses canvas to implement bullet screen function

Introduction Recently, I needed to make a barrage...

css add scroll to div and hide the scroll bar

CSS adds scrolling to div and hides the scroll ba...

Share the 15 best HTML/CSS design and development frameworks

Professional web design is complex and time-consu...

Summary of various uses of JSON.stringify

Preface Anyone who has used json should know that...

CSS+HTML to realize the top navigation bar function

Implementation of navigation bar, fixed top navig...

The implementation process of Linux process network traffic statistics

Preface Linux has corresponding open source tools...

Disable input text box input implementation properties

Today I want to summarize several very useful HTML...