The Implementation Principle of es6 Class and Inheritance

  Class, es6, Front end, Inheritance, javascript

Before reading this article, you need at least some knowledge of JavaScript prototype inheritance. If you feel that there is something missing, you can first understand my article:https://segmentfault.com/a/11 …

1. use of es6class

Javascript uses prototype inheritance. We can realize class inheritance through prototype features.
Es6 provides us with the same syntax sugar as object-oriented inheritance.

class Parent {
constructor(a){
this.filed1 = a;
}
filed2 = 2;
func1 = function(){}
}

class Child extends Parent {
constructor(a,b) {
super(a);
this.filed3 = b;
}

filed4 = 1;
func2 = function(){}
}

Let’s use babel to explore the implementation principles of es6 classes and inheritance.

1. Implementation of Class

Before conversion:

class Parent {
 constructor(a){
 this.filed1 = a;
 }
 filed2 = 2;
 func1 = function(){}
 }

After conversion:

function _classCallCheck(instance, Constructor) {
 if (!  (instance instanceof Constructor)) {
 throw new TypeError("Cannot call a class as a function");
 }
 }
 
 var Parent = function Parent(a) {
 _classCallCheck(this, Parent);
 
 this.filed2 = 2;
 
 this.func1 = function () { };
 
 this.filed1 = a;
 };

It can be seen that the bottom layer of class is still the constructor:

1. Call the _classCallCheck method to determine whether there is a new keyword before the current function is called.

Before the constructor is executed, the new keyword will create an empty object inside the constructor, point the proptype of the constructor to the _proto_, and point this to the empty object. As above, _classCallCheck: this instanceof Parent returns true.

If there is no new before the constructor, the proptype of the constructor does not appear on the prototype chain of this and returns false.

2. assign variables and functions inside class to this.

3. Implement the internal logic of constuctor.

4.return this (the constructor defaults to what we did at the end).

2. Implementation of inheritance

Before conversion:

class Child extends Parent {
constructor(a,b) {
super(a);
this.filed3 = b;
}

filed4 = 1;
func2 = function(){}
}

After conversion:

Let’s look at the internal implementation of Child first, and then look at how the functions called internally are implemented:

var Child = function (_Parent) {
 _inherits(Child, _Parent);
 
 function Child(a, b) {
 _classCallCheck(this, Child);
 
 var _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, a));
 
 _this.filed4 = 1;
 
 _this.func2 = function () {};
 
 _this.filed3 = b;
 return _this;
 }
 
 return Child;
 }(Parent);

1. call the _inherits function to inherit the proptype of the parent class.

_inherits internal implementation:

function _inherits(subClass, superClass) {
if (typeof superClass !  == "function" && superClass !  == null) {
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
}
subClass.prototype = Object.create(superClass && superClass.prototype, {
constructor: { value: subClass, enumerable: false, writable: true, configurable: true }
});
if (superClass)
Object.setPrototypeOf ?   Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
}

(1) check the parent constructor.

(2) typical parasitic inheritance: create an empty object with the proptype of the parent class constructor and point this object to the proptype of the subclass constructor.

(3) Pointing the parent constructor to the _proto_ (of the child constructor is not so clear as to make sense. )

2. Use a closure to save the parent class reference and subclass the logic inside the closure.

3.new inspection.

4. Call the parent class constructor with the current this.

var _this = _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).call(this, a));

The child. _ proto _ _ | | object. getprototypeof (child) here is actually the parent constructor (_ inherited last operation), and then changes its caller to the current this through call and passes the parameters. (Here, I feel that I can directly use the Parent passed by the parameter)

function _possibleConstructorReturn(self, call) {
 if (!  self) {
 throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
 }
 return call && (typeof call === "object" || typeof call === "function") ?   call : self;
 }

Verify that this is initialized, super calls, and returns this whose parent class has been assigned.

5. Assign variables and functions inside the line subclass class to this.

6. Execute the logic inside the subclass constuctor.

It can be seen that es6 actually provides us with a simple way to write “combined parasitic inheritance”.

3. super

Super represents the parent class constructor.

Super.fun1 () is equivalent to Parent.fun1 () or Parent.prototype.fun1 ().

Super () is equivalent to Parent.prototype.construtor ()

When we do not write subclass constructors:

var Child = function (_Parent) {
 _inherits(Child, _Parent);
 
 function Child() {
 _classCallCheck(this, Child);
 
 return _possibleConstructorReturn(this, (Child.__proto__ || Object.getPrototypeOf(Child)).apply(this, arguments));
 }
 
 return Child;
 }(Parent);

Visible in the default constructor will take the initiative to call the parent class constructor, and the default parameters passed by the current constructor to the parent class.

Therefore, when we declare the constructor, we must actively call super (), otherwise we cannot call the parent constructor and inherit.

A typical example is Component of Reatc. We must call super(props) after declaring the constructor, because the parent class has to do some initialization operations on props in the constructor.