Using babel to understand the Class of es6

preface

There are many places where class classes are used in work, usually exposing class classes or instances of class classes. For example, if a product has attributes such as price, specification, name, etc., you can initialize the product object through a new class class.

Friends who know about es6 should know that es6 is the syntax sugar of es5. Class is actually a method to generate instance objects based on es5's constructor, but the writing method of class is more elegant and more similar to traditional object-oriented programming.

Although Class is usually used, it often ignores the details and causes problems. Out of curiosity and in order to fully understand Class, I use babel tool to downgrade es6 syntax, see the "true face of Lushan" of Class, understand what babel has done, and write a record for myself to review.

Before and after a Class babel

Define a Class class

class K {
    constructor(name) {
        this.name = name;
    }
    // Static method
    static classMethod() {
        this.getname()
        return 'hello';
    }
    // setter
    set prop(value) {
        console.log('setter: '+ value);
    }
    // getter
    get prop() {
        return 'getter';
    }
    // Prototype method
    getName() {
        return "celeste";
    }
}

let k = new K("celeste")

The above class uses babel to degrade the syntax, and the code is as follows:

var K = function () {
  function K(name) {
    _classCallCheck(this, K);
    
    this.name = name;
  }

  _createClass(K, [{
    key: 'getName',
    value: function getname() {
      return "celeste";
    }
  }, {
    key: 'prop',
    set: function set(value) {
      console.log('setter: ' + value);
    },
    get: function get() {
      return 'getter';
    }
  }], [{
    key: 'classMethod',
    value: function classMethod() {
      this.getName();
      return 'hello';
    }
  }]);

  return K;
}();

var k = new K("celeste");

It can be found that the Class class is essentially a self executing function. This function returns a constructor K after execution.
In addition, the function defined here is not in the form of function declaration, but in the form of variable declaration and assignment of var K, which is actually the reason why the class class does not have variable promotion. Although js function will scan the whole function body statement first and promote all declared variables to the top of the function, it will not promote the assignment. Before console, the variable K has not been assigned, so the printing result is undefined.

// Variable assignment
console.log(Bb); // undefined
var Bb = function Bb () {};
// Function declaration
console.log(Aa); // ƒ Aa () {}
function Aa () {};
A kind of classCallCheck,_ createClass function

After reading the outer layer, take a look at the key information in it, mainly_ classCallCheck,_ What have they done in createClass? The source code is as follows:

"use strict";
// For forward compatibility, the es6 syntax is actually strict
// Only strict mode is available in the class or module

// Constructor to determine whether right is left
function _instanceof(left, right) { 
    if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { 
        return !!right[Symbol.hasInstance](left); 
    } else { 
        return left instanceof right; 
    } 
}

// Constructor to determine whether the constructor is instance or not. If not, an error will be thrown
function _classCallCheck(instance, Constructor) {
    if (!_instanceof(instance, Constructor)) { 
        throw new TypeError("Cannot call a class as a function"); 
    } 
}

// Traverse props, set the properties of each item in props and mount it on target
function _defineProperties(target, props) { 
    for (var i = 0; i < props.length; i++) { 
        var descriptor = props[i]; 
        // Define whether enumerable (no)
        descriptor.enumerable = descriptor.enumerable || false; 
        // Define whether it can be deleted
        descriptor.configurable = true; 
        // If the descriptor has the value attribute (that is, the prototype method other than set/get), it can be assigned
        if ("value" in descriptor) descriptor.writable = true; 
        // Changing variables descriptor.key Defined on target
        Object.defineProperty(target, descriptor.key, descriptor); 
    } 
}

// The parameters are: constructor, prototype method and static method
function _createClass(Constructor, protoProps, staticProps) { 
    // The prototype method is mounted on the prototype of the constructor
    if (protoProps) _defineProperties(Constructor.prototype, protoProps); 
    // The static method (the function defined by the static keyword) will be passed in as an item in the third parameter array and will directly become an attribute under the constructor
    if (staticProps) _defineProperties(Constructor, staticProps); 
    return Constructor; 
}

So constructor actually initializes a constructor:


function K(name) {
    _classCallCheck(this, K);
    
    this.name = name;
}

A kind of The function of classCallCheck(this, K) is to judge whether K is the constructor of this. If it is not, an error will be thrown to ensure that it is safe (based on that if K is a constructor, then this must point to the instance object of K).

And_ The createClass function is used to mount the methods defined in the class to the function prototype (for prototype methods) or the class itself (for static static methods):

hold_createClass Functions and function calls are visually put together:

// A kind of createClass function
function _createClass(Constructor, protoProps, staticProps) { 
    if (protoProps) _defineProperties(Constructor.prototype, protoProps);
    if (staticProps) _defineProperties(Constructor, staticProps); 
    return Constructor; 
}
  // Function execution in self executing function after syntax degradation
  _createClass(K, [{
    key: 'getName',
    value: function getname() {
      return "celeste";
    }
  }, {
    key: 'prop',
    set: function set(value) {
      console.log('setter: ' + value);
    },
    get: function get() {
      return 'getter';
    }
  }], [{
    key: 'classMethod',
    value: function classMethod() {
      this.getName();
      return 'hello';
    }
  }]);

As you can see, setter and getter are also in the second parameter array. They are also the methods on the prototype. When parameters are passed, they are slightly different. value -- set/get is to distinguish them when they are attached to the prototype. The code that distinguishes them is_ This sentence in the defineProperties function:

if ("value" in descriptor) descriptor.writable = true;
Some conclusions:

1. All methods of the class are defined on the prototype property of the class

So new methods of classes can be used` Object.assign `Add to 'prototype' object
Object.assign(Person.prototype, {
  // add some functions ...
});

2. All methods defined in the class are non enumerable
3. The JS engine will automatically add an empty constructor method to the empty class (in fact, it will create a constructor by default, and point this of the constructor to the instance of the class)
4.constructor function can return Object.create(null) returns a new object, which can cause the instance object to be an instance of a class.

Let's look at the rest of the questions in the next section. After summing up these two days, we may merge them into one or open a new one... - 2020 / 06 / 06 01:20

Tip: syntax sugar, also translated as icing grammar, is a term invented by Peter J. Landin, a British computer scientist. It refers to a kind of grammar added to computer language. This grammar has no effect on language functions, but it is more convenient for programmers to use. Generally speaking, the use of syntax sugar can increase the readability of the program, thus reducing the chance of program code error.

babel online tools https://babeljs.io/repl

Tags: Javascript Attribute Programming REST

Posted on Sat, 06 Jun 2020 01:00:17 -0400 by cronus