Ruan Yifeng's ES6 -- inheritance of class

Posted by sureshp on Fri, 04 Mar 2022 10:43:09 +0100

Class can inherit through the extends keyword, which is much clearer and more convenient than that of ES5 by modifying the prototype chain;

class Father {
 }
class Son extends Father {
}

The code defines a Son class, which inherits all the attributes and methods of the Father class through the extends keyword. However, since no code is deployed, the two classes are exactly the same, which is equivalent to copying a Father class.

class Son extends Father {
          constructor (name,age,city) {
                super(name,age);//Call the constructor(name,age) of the parent class;
                this.city = city;
           }

           toString () { 
                  return this.city+ " " +super.toString();//Call the toString() of the parent class
            }
}

The super keyword appears in the constructor method and toString method. It represents the constructor of the parent class and is used to create a new this object of the parent class;

The subclass must call the super method in the constructor method, otherwise the new instance will be wrong. This is because the subclass does not have its own this object, but inherits the this object of the parent class, and then processes it. If the super method is not invoked, the subclass will not get the this object.

class Father {   }

class Son extends Father {
         constructor(){  }
}
let s = new Son();
//referenceError : this is not defined 

Son inherits the parent class Fatherm, but his constructor does not call the super method, which leads to an error in the new instance;
The essence of inheritance of ES5 is to create the instance object this of the subclass first, and then add the method of the parent class to this (Parent.apply(this)). The inheritance mechanism of ES6 is completely different. The essence is to create the instance object this of the parent class first (so you must call the super method first), and then modify this with the constructor of the subclass;
If the subclass does not define a constructor method, this method will be added by default, that is, no matter whether it is explicitly defined or not, any subclass has a constructor method.

class Son extends Father {
}

//Equivalent to
class Son extends Parent {
       constructor(...args) {
       super(...args);
      }
}

Another thing to note is that in the constructor of subclasses, the this keyword can be used only after super is called, otherwise an error will be reported. This is because the construction of subclass instances is based on the processing of parent instances. Only super methods can return parent instances;

class Father {
      constructor (x,y) {
            this.x= x;
            this.y = y;
       }
}

class Son extends Father {
      constructor (x, y, color) {
             this.color =color ;//ReferenceError : this is not defined
            super(x,y);
             this.color = color;//correct
            }
}

let s = new Son(25,8,"green");
s instanceof Son //true 
s instanceof Father //true

The constructor method of the subclass uses this keyword before calling super, and the result is an error, which is correct after the super method;

Object. The getprototypeof () method is used to get the parent class from the child class

Object.getPrototypeOf( Son ) ===Father
//true
//Therefore, you can use this method to judge whether a class inherits another class

super keyword
The keyword super can be used as either a function or an object,
(1) The first case is: when super is called as a function, it represents the constructor of the parent class. ES6 requires that the constructor of the child class must execute a super function;

class Father { }

class Son extends Father {
       constructor () {
                   super();
              }
}
//super() in the constructor of the subclass Son represents calling the constructor of the parent class. This is necessary, otherwise the JavaScript engine will report an error.

Although super represents the constructor of the parent class Father, it returns an instance of the subclass Son, that is, this inside super points to Son. Therefore, super() is equivalent to Father constructor. call(this);
Moreover, as a function, super() can only be used in the constructor of subclasses and will report errors elsewhere;

class A {
         constructor (){
               console.log(new.target.name);
            }
 }

class B extends A {
      constructor () {
           super();
           }
 }
   new A()//A
  new B()//B 

new.target points to the function currently being executed. When super() is executed, it points to the constructor of subclass B instead of the constructor of parent class A. this inside super() points to B;

(2) In the second case, when super is used as an object, it points to the prototype object of the parent class in ordinary methods and to the parent class in static methods;

class Father{
     getName ( ) {
         return "MGT360124";
     }
}
class Son extends Father {
       constructor () {
        super();
       console.log(super.getName() )  //"MGT360124"
        }
}
let s = new Son();

Super in subclass Son P () is to use super as an object. In this case, super points to father in the ordinary method Prototype, so super Getname () is equivalent to father prototype. getName();// "Mgt360124". Since super points to the prototype object of the parent class, the methods or properties defined on the parent class instance cannot be called through super;

class Father {
      constructor () {
              this.p  =2
          }
}

class Son extends Father {
          get  m ( ) {
                   return super.p;
          }
          getValue ( ) {
                    return super.a;
           }
}
let  s = new Son();
s.m
//undefined

p is the attribute of the parent class Father instance, super p cannot reference it

If the attribute is defined on the prototype object of the parent class, super can get it.

class A {}
A.prototype.x = 2;

class B extends A {
  constructor() {
    super();
    console.log(super.x) // 2
  }
}

let b = new B();

Attribute x is defined on A.prototype, so super X can take its value.

ES6 stipulates that when calling the method of the parent class through super, super will bind the this of the child class.

class  Father {
      constructor () {
           this.x =1;//this refers to an instance of the Father object
      }
      print () {
           console.log(this.x);
      }
}

class Son extends Father {
      constructor () {
             super();
               this.x = 2;//this refers to an instance of the Son object
      }
          m() {
           super.print();       
          }
}
let s = new  Son();
s.m();
//2 

super.print() calls father prototype. Print(), but father prototype. Print () will bind this of the subclass Son, resulting in the output of 2 instead of 1, that is, it actually executes super print. call(this).

If super is used as an object in a static method, then super will point to the parent class instead of the prototype object of the parent class;

class Parent {
           static myMethod (msg) {
                      console.log("static",msg);
               }
           myMethod (msg) {
                    console.log("instance" ,msg);
               }
}

class Child  extends Parent {
          static myMethod(msg) {
               super.myMethod(msg);
          }
            myMethod (msg) {
           super.myMethod(msg);
           }
 }

Child.myMethod(1);
//static 1
var child = new Child();
child.myMethod(2);
//instance 2

super refers to the parent class in static methods and the prototype object of the parent class in ordinary methods.
Note that when using super, you must explicitly specify whether to use it as a function or as an object, otherwise an error will be reported.
prototype attribute and proto attribute of class
In the ES5 implementation of most browsers, each object has a proto attribute, pointing to the prototype attribute of the corresponding constructor. class is the syntax sugar of the constructor, and has both prototype attribute and proto attribute. Therefore, there are two inheritance chains at the same time;
(1) The proto attribute of the subclass represents the inheritance of the constructor and always points to the parent class;
(2) The proto attribute of the subclass prototype attribute, which represents the inheritance of methods, always points to the prototype attribute of the parent class;

class A{
}
class B{
}
//The instance of B inherits the instance of A
Object.setPrototypeOf(B.prototype, A.prototype);

//The instance of B inherits the static property of A
Object.setPrototypeOf(B,A);

const b = new B();

In the chapter "object extension", object Implementation of setprototypeof() method:

Object.setPrototypeOf = function (obj, proto) {
obj.__proto__ = proto;
   return obj ;
}

therefore

Object.setPrototypeOf( B.prototype ,  A.prototype );
//Equivalent to
B.prototype.__proto__ =  A.prototype ;

Object.setPrototypeOf(B, A);
//Equivalent to
B.__proto__ = A;

These two inheritance chains can be understood as: as an object, the prototype (proto attribute) of subclass B is the parent class (A); As A constructor, the prototype object (prototype attribute) of subclass B is an instance of the prototype object (prototype) of the parent class;

Inheritance target of extensions
The extends keyword can be followed by many types of values;

class B extends A{
}

As long as A has A function with prototype attribute, it can be inherited by B. since all functions have prototype attribute (except Function.prototype function), A can make any function. There are three cases:
(1) Subclass inherits Object class

class A extends Object {
}
A.__proto__ === Object //true;
A.prototype.__proto__ === Object.prototype //true

In this case, A is the copy of the constructor Object, and the instance of A is the instance of Object
(2) No inheritance exists

class A {
}
A.__proto__ === Function.prototype //true
A.prototype.__proto__ = Object.prototype //true

In this case, as A base class (without any inheritance), A is an ordinary function, so it directly inherits function prototype. However, A returns an empty Object (i.e. Object instance) after calling, so A.prototype Proto refers to the prototype attribute of the constructor (Object);
proto property of the instance
The proto attribute of the proto attribute of the subclass instance points to the proto attribute of the parent instance. In other words, the prototype of a subclass is the prototype of a parent class.

Inheritance of native constructors
Native constructors are built-in constructors in languages, which are usually used to generate data structures.

Boolean()
Number()
String()
Array()
Date()
Function()
RegExp()
Error()
Object()

The extends keyword can be used not only to inherit classes, but also to inherit native constructors. Therefore, you can define your own data structure based on the original data structure.