Class of ES6 New Features

Posted by jaygattis on Fri, 30 Aug 2019 07:50:13 +0200

In JS, it actually has no concept of class. If we want to implement an object-oriented programming, we can only use the constructor, but the constructor has its own shortcomings. First of all, its writing is not clear. If we need to inherit, we need to operate prototype manually. Secondly, its object-oriented idea is not very strong, so in ES6 it encapsulates a layer of constructor's grammatical sugar, called Class. After encapsulation, it has a clearer way of writing, and more like an object-oriented programming language.

1: Basic concepts

Constructor Grammar Sugar Constructor Grammar Sugar

  • Clear Writing
  • object-oriented programming

In ES5:

  // Constructor of ES5
  function Position(x, y) {
    this.x = x;
    this.y = y;
  }
  
  const position = new Position(1, 1);

In ES6:

In ES6, first there is a class modifier, which tells the editor to declare a class called Position. Class is a constructor's grammatical sugar. How do we look at it? We can see if the constructor of the prototype of this constructor is itself, and if it is itself, it means that the behavior of the class is consistent with that of the constructor.

    class Position {
       constructor(x, y) {
         this.x = x;
         this.y = y;
       }
     }
     Position === Position.prototype.constructor;
     /* true */
     const position = new Position(1, 1);

All methods of a class are defined in the prototype attribute of the class

In ES5, for example, we define some methods in the constructor, so we have this method in the instance, but ES6 is different. In Class, all methods are defined on the prototype attribute, that is, all methods except this. are defined on the prototype attribute.

    class Position {
       constructor(x, y) {
         this.x = x;
         this.y = y;
       }
    
       scan() {
         console.log(this.x, this.y);
       }
    }
    Position.prototype;
      
    /*
    {
      constructor: class Position
      scan: ƒ scan()
      __proto__: Object
    }
    */

Internal methods cannot be enumerated

The internal methods are all on prototype, but the prototype method is not enumerable, that is to say, its enumeration attribute is false. If we want to get this enumeration at this time and know what methods it has, we need to use getOwn Property Names.

   class Position {
     constructor(x, y) {
       this.x = x;
       this.y = y;
     }
     scan() {
       console.log(this.x, this.y);
     }
   }
   
   Object.keys(Position.prototype);
   /* [] */
   
   Object.getOwnPropertyNames(Position.prototype);
   /* ["constructor", "scan"] */

this pointer - default pointer to the instance itself

    class Position {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
      scan() {
        console.log(this.x, this.y);
      }
    }
    
    const position = new Position(1, 1);
    
    // Call self-scanning
    position.scan();
    // 1 1

It is inconsistent to deconstruct and call this again, which will cause problems and errors.

      const { scan } = position;
      scan();
      /* Cannot read property 'x' of undefined */

There are two solutions to the pointing problem of this call after deconstruction. The first method, with the help of arrow function: When the arrow function is defined, this is already specified, that is, what it defines and where it defines, and what its this is.

      class Position {
         constructor(x, y) {
           this.x = x;
           this.y = y;
         }
         // Change the scan() function to an arrow function, and this always points to where it was defined.
         scan = () => {
           console.log(this.x, this.y);
         }
      }
      
      const position = new Position(1, 1);
      const { scan } = position;
      scan();
      /* 1 1 */

The second method, manual bind:

      class Position {
         constructor(x, y) {
           this.x = x;
           this.y = y;
           this.scan = this.scan.bind(this);
         }
         scan() {
           console.log(this.x, this.y);
         }
      }
      const position = new Position(1, 1);
      const { scan } = position;
      scan();
      /* 1 1 */

2: Method attributes

The constructor constructor, first of all, is a required, that is, it is a necessary, if we do not pass it in, then an empty constructor will be generated by default.

constructor - required - Default empty function

  • Call a constructor when using the new command
        class Position {
           constructor(x, y) {
              this.x = x;
              this.y = y;
              console.log('constructor');
           }
        }
        
        const position = new Position(1, 1);
        /* constructor */
  • Default return instance object
         class Position {
            constructor(x, y) {
              return Object.create({ name: 'Eric' });
            }
          }
        
          const position = new Position(1, 1);
          //Console input:position
          // Get an empty object: {}
          
          position instanceof Position;
          /* false */

Instances of classes

  • new command creation
  • The attributes of the instance are all defined on the prototype object except this
      hasOwnProperty();

getter and setter - interceptor, intercepting the access behavior of an attribute

get is to intercept its fetch, set is to intercept its memory, getter and setter must appear in pairs, and we need to set when getter and setter should be which, should be the current object does not exist, otherwise it will report a stack overflow, because it will have a circular call problem.

      class Position {
        constructor(x, y) {
          this.x = x;
          this.y = y;
        }
      
        get z() {
          return this.x;
        }
      
        set z(value) {
          this.x = 2;
        }
      }
      
      const position = new Position(1, 1);

Static - static methods - cannot be inherited by instances - this points to classes

Static literally means static, it can not be inherited by instances, that is to say, static methods can not be invoked in instances, it can only be invoked through the class itself, when this is directed at the current class itself.

      class Position {
        constructor(x, y) {
          this.x = x;
          this.y = y;
          this.z = 100;
        }
      
        static scan() {
          console.log(this);
        }
      }
      
      const position = new Position(1, 1);
      
      position.scan();
      /* position.scan is not a function */
      
      
      Position.scan();
      /*
      class Position {
        constructor(x, y) {
          this.x = x;
          this.y = y;
          this.z = 100;
        }
      
        static scan() {
          console.log(this);
        }
      }
      */

New Attribute Writing

In the previous code, attributes x and y were defined inside the constructor, which is actually quite clear, but there is a proposal: put all attributes at the top of the constructor, we can define some variables directly inside the constructor, of course, this variable does not need modifiers, this is the case. It is added to this object by default.

      class Count {
        x = 0;
      
        reduce() {
          this.x = this.x - 1;
          console.log(this.x);
        }
      
        increment() {
          this.x = this.x + 1;
          console.log(this.x);
        }
      }
      
      const count = new Count();
      
      count.increment();
      count.increment();
      count.increment();
      count.increment();
      
      // 1
         2
         3
         4 

3: Class Inheritance

    class Point {
      constructor(x, y) {
        this.x = x;
        this.y = y;
      }
    
      scan = () => {
        console.log(this.x, this.y);
      }
    }
    // SubPoint inherits Point
    class SubPoint extends Point {
    
    }
    const subPoint = new SubPoint(1, 1);
    
    subPoint;
    /*
    {
      scan: () => { console.log(this.x, this.y); }
      x: 1
      y: 1
      __proto__: Point
    }
    */

Subclasses must call super methods

      class Point {
        constructor(x, y) {
          this.x = x;
          this.y = y;
        }
      
        scan = () => {
          console.log(this.x, this.y);
        }
      }
      
      class SubPoint extends Point {
        constructor(...rest) {
          // super(...rest)
        }
      }
      const subPoint = new SubPoint(1, 1);
      
      /* Must call super constructor in derived class before accessing 'this' or returning from derived constructor */

Object.getPrototypeOf - A parent instance can be obtained in a class

      class Point {
        constructor(x, y) {
          this.x = x;
          this.y = y;
        }
      
        scan = () => {
          console.log(this.x, this.y);
        }
      }
      
      class SubPoint extends Point {
        constructor(...rest) {
          super(...rest)
        }
      }
      const subPoint = new SubPoint(1, 1);
      
      Object.getPrototypeOf(subPoint);
      /*
      Point {
        constructor: class SubPoint
        __proto__: Object
      }
      */

Topics: Attribute REST Programming