There are eight inheritance schemes commonly used in JavaScript.

This article is as brief as possible in order to facilitate the intuitive comparison of the eight schemes (to draw the graphs closer together). See the article for details of the eight inheritance schemes.

define

First, let’s agree on the meanings of the symbols to be used next:

  1. = = =: The top line isprototypeThe line segment, the line below isconstructorLine segment
  2. /: __proto__Line segment
  3. |: Inheritance of instance attributes
  4. + properties: Add attributes

Basically, these symbols correspond to directed line segments as shown below:

1. Prototype chain inheritance

            superType ==== superType.prototype
                 |           / 
subType ==== new superType() + properties
  |         /
new subType()
Copy the code

As you can see from the figure, the prototype of subType is an instance of superType, with some attributes added. So the disadvantages are:

  • Multiple subType instances operating on a stereotype type change that instance (the stereotype of a subType).

2. Borrow constructor inheritance

superType
|				
| subType
|  |    
new subType()
Copy the code

Using a parent class constructor to enhance a child class instance is equivalent to copying an instance of the parent class to a child class (without using a stereotype).

Disadvantages:

  • Only instance properties and methods of the parent class can be inherited, not stereotype properties/methods
  • Unable to reuse, each subclass has a copy of the parent class instance function, affecting performance

3. Combinatorial inheritance

superType     superType ==== superType.prototype
|                 |           / 
| subType ==== new superType() + properties
|  |         /
new subType()
Copy the code

Combining these two methods is called combinatorial inheritance. The inheritance of prototype attribute and method is realized by prototype chain, and the inheritance of instance attribute is realized by borrowing constructor technique.

As you can see from the diagram, the instance attributes of superType are inherited by the prototype and instance of superType, so it has the following disadvantages:

  • When you create an instance object using a subclass, there are two copies of the same properties/methods in the prototype.

4. Inheritance of the original type

      obj
     /
clone
Copy the code

Disadvantages:

  • The prototype chain inherits multiple instances of reference type attribute pointing to the same, there is the possibility of tampering.
  • Unable to pass parameters

Parasitic inheritance

      obj
     /
clone + properties
Copy the code

The defect is inherited from the original pattern.

Parasitic combinatorial inheritance

superType ==== superType.prototype
|                  / 
| subType ==== prototype + properties
|  |         /
new subType()
Copy the code

Steps:

  • The parent class initializes instance and stereotype properties
  • Class initialization with constructor passing enhanced class instance properties (supports parameter passing and avoids tampering)
  • Point the subclass stereotype to the superclass stereotype and fix the default constructor attribute lost by overwriting the stereotype
  • New subclass stereotype attribute

Inheritance is implemented using a combination of borrowed constructors passing parameters and parasitic patterns. As you can see from the figure, the disadvantages of prototype chain inheritance have been avoided.

This is the most mature method, and it is the method that libraries implement today

7. Blending inherits multiple objects

SuperClass1 ========= SuperClass1.prototype
| SuperClass2              /
|  | MyClass == MyClass.prototype + SuperClass2.properties + properties
|  |  |       /
new MyClass()
Copy the code

Steps:

  • The parent class initializes instance and stereotype properties
  • Class initialization with constructor passing enhanced class instance properties (supports parameter passing and avoids tampering)
  • Assign copies the attributes of the other parent class using object. assign, fixing the default constructor attributes that were modified by overriding the stereotype
  • New subclass stereotype attribute

8. ES6 extends extends

superType ==== superType.prototype
|    /              / 
| subType ==== prototype + properties
|  |         /
new subType()
Copy the code

Note that the object prototype for subType here points to superType. The core code of extends inheritance is as follows. It is implemented in the same way as the above parasitic composite stereotype inheritance, but with the added exception of pointing the object stereotype of subType to superType.

function _inherits(subType, superType) {
  
    // Create object, create a copy of the parent class prototype
    // Enhance the object to compensate for the loss of the default constructor property by rewriting the prototype
    // Assign the newly created object to the prototype of the subclass
    subType.prototype = Object.create(superType && superType.prototype, {
        constructor: {
            value: subType,
            enumerable: false.writable: true.configurable: true}});// The object prototype of subType points to superType
    if (superType) {
        Object.setPrototypeOf 
            ? Object.setPrototypeOf(subType, superType) : subType.__proto__ = superType; }}Copy the code

Here, why does subType __proto__ refer to superType?

  • Prototype is divided into object prototype __proto__ and function prototype prototype.

  • A function is also an object, in order to inherit a method on superType.

    • Set subtype. prototype. Only subType instances generated by new will inherit the methods on supertype. prototype.
    • With subtype.__proto__ = superType, a direct call to subtype.xxx also inherits methods on superType (supertype.xxx), such as array.isarray (). This is also known as a class method.

All of this is grammatical sugar.

Let’s examine the differences between ES6 inheritance and ES5:

Take a look at this code on the MDN:

class Polygon {
  constructor(height, width) {
    this.name = 'Rectangle';
    this.height = height;
    this.width = width;
  }
  sayName() {
    console.log('Hi, I am a '.this.name + '. ');
  }
  get area() {
    return this.height * this.width;
  }
  set area(value) {
    this._area = value; }}class Square extends Polygon {
  constructor(length) {
    this.height; // ReferenceError, super needs to be called first!
    
    // Here, it calls the constructor of the parent class,
    // As the height, width of Polygon
    super(length, length);
    
    // Note: In derived classes, super() must be called before you can use 'this'.
    // Ignoring this will result in a reference error.
    this.name = 'Square'; }}Copy the code

Super calls the constructor of the parent class. You essentially create an instance object of the parent class, this, and then modify this using the constructor of the subclass. It’s not that subclasses don’t have this until super() is used, but ES6 states that you can’t use this in a subclass constructor until super() is called. This is done to stay close to Java syntax, which ensures that the parent constructor is executed before this can be accessed.

Therefore, ES6 super(… Args) is not equivalent to Parent. Apply (this, args). So ES6 classes are not just syntactic sugar.

To summarize the differences between ES5 and ES6:

  • ES5 inheritance essentially creates an instance object of the subclass and then adds the Parent class’s methods to this (parent.call (this)).
  • ES6 inheritance is different, essentially creating an instance object of the parent class, this, and then modifying this with the constructor of the subclass.

The resources

  • There are eight inheritance schemes commonly used in JavaScript
  • MDN super
  • Github.com/yygmind/blo…
  • Does an ES6 subclass have its own this? – royal ShiJun answer – zhihu www.zhihu.com/question/37…