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

Analyze the usage and principles of Vue's provide and inject

First, let's talk about why we use provide/in...

Super detailed tutorial to implement Vue bottom navigation bar TabBar

Table of contents Project Introduction: Project D...

New ways to play with CSS fonts: implementation of colored fonts

What if you designers want to use the font below ...

Process parsing of reserved word instructions in Dockerfile

Table of contents 1. What is Dockerfile? 2. Analy...

Details of the order in which MySQL reads my.cnf

Table of contents The order in which MySQL reads ...

Detailed explanation of the syntax and process of executing MySQL transactions

Abstract: MySQL provides a variety of storage eng...

Docker pull image and tag operation pull | tag

I re-read the source code of the Fabric project a...

Basic installation process of mysql5.7.19 under winx64 (details)

1. Download https://dev.mysql.com/downloads/mysql...

Next.js Getting Started Tutorial

Table of contents Introduction Create a Next.js p...

Bootstrap FileInput implements image upload function

This article example shares the specific code of ...

A brief discussion on the difference between src and href in HTML

Simply put, src means "I want to load this r...

Detailed explanation of Vue's calculated properties

1. What is a calculated attribute? In plain words...