A preface.

Due to the nature of the JavaScript language, there is no API to implement multiple inheritance. But as long as we understand the principle of inheritance, from the bottom implementation, or can achieve JS multiple inheritance.

There is A false multiple inheritance, which we will not discuss, namely C——> B ——> A; In this way, although C has instance properties and methods of class B and class A, it is not in our category and does not belong to the true sense of multiple inheritance.

Ii. Core Ideas

In the previous chapter, we introduced how inheritance is implemented. Before implementing multiple inheritance, consider the following questions:

  1. Can we achieve multiple inheritance of attributes by borrowing from constructors that call multiple base class constructors?
  2. Can we do this by assigning stereotype properties in stereotype objects to merge objects of multiple base class stereotypes?
  3. What about methods with the same name that have multiple base classes?

Starting from the above problems, we get the core ideas:

Multiple base class constructors are called in the borrowed constructor to complete the multiple inheritance of instance attributes, and multiple method inheritance is completed through the merge object of multiple base class prototype objects.

Theory and practice

In the previous chapter, we learned two very important basics of inheritance.

  1. Instance attributes are inherited by borrowing constructors.
  2. Reuse of methods is accomplished through prototype objects.

First we have the following parent classes:

Shape class definition file (shape.js) :

   // 父类 Shape(形状) Shape.js
 function Shape(shapeName) {
  this.shapeName = shapeName || "";
  console.log("Shape constructed");
}
Shape.prototype = {
  getShapeName() {
    return this.shapeName;
  },
  setShapName(shapeName) {
    this.shapeName = shapeName;
  },
  print() {
    console.log("this shape:".this.shapeName); }};export default Shape;
Copy the code

Color class definition file (color.js) :

// Color class definition file (color.js)
function Color(colorName) {
  this.colorName = colorName || "Colorless";
}
Color.prototype = {
  getColor() {
    console.log("Call get color method");
    return this.colorName;
  },
  setColor(colorName) {
    console.log("Set color to", colorName);
    this.colorName = colorName;
  },
  print() {
    console.log("this is Color:".this.colorName); }};export default Color;

Copy the code

Subclass file (Rectangle. Js) :

 // Rectangle class definition file
import Shape from "./Shape";
import Color from "./Color";

export default function Rectangle(name, color) {
  // This completes multiple inheritance of attributes.
  Shape.call(this, name);
  Color.call(this, color);
  this.width = 5;
  this.height = 6;
  console.log("Rectangle construted");
  this.getArea = () = > {
    console.log("get Area:".this.width * this.height);
    return this.width * this.height;
  };
  this.getC = () = > {
    console.log("get C:", (this.width + this.height) * 2);
    return (this.width + this.height) * 2;
  };
}

// Create a merge prototype object
const mergePrototype = Object.assign({}, Shape.prototype, Color.prototype);

Rectangle.prototype = Object.create(mergePrototype);

// Where do we inherit from here
Rectangle.prototype.__proto__.constructor = [Shape, Color];

// own function methods reuse defined locations
Rectangle.prototype.getParentClass = function() {
  const parents = this.__proto__.constructor || [];
  console.log("I inherit from:", parents.map(item= > item.name));
};

Copy the code

** Test file: index.js

import Rectangle from "./inherit/rectangle";
console.log("Rectangle.prototype.__proto__:", Rectangle.prototype.__proto__);

let rec2 = new Rectangle("Rectangular 2");
console.log("rec2 is:", rec2);
rec2.getArea();
rec2.getC();
rec2.setColor("red");
rec2.getColor();
rec2.getParentClass();

Copy the code

** The code running result is as follows:

As can be seen from the figure:

  1. Rec2 instances have their own instance methods getArea,getC, and instance properties width,height
  2. Inherited colorName,shapeName attributes;
  3. The Reactangle instance publicly gets the inherited parent method getParentClass;
  4. GetColor,setColor, print methods inherited from Color (because they override print in Shape)
  5. From the Shape of inherited getShapeName, setShapeName method

Basically, multiple inheritance is done.

Tips: regarding the overwriting of methods with the same name, this actually exists in both Java and C++ languages; This belongs to another problem in multiple inheritance, namely ambiguity problem, JS language, the author has no good way to deal with, can only avoid the declaration of the same name attribute. C++ provides the scope operator, which conveniently specifies which base class method of the same name to call.

Create a merge object with a method of the same name.

Traversal saves each method with the same name. Such as a Rectangle. NotSingle = [print,… , Rectangle.notSingle = [print,…]

  1. Create the method name Of the class name Of the method, such as printOfColor printOfShape

  2. Create a new method print(__range__),__range__ is a scope that specifies which parent class’s method of the same name will be called. The logic of the print function will determine which function to call based on the value of __range__. It is best to give default values.

  3. We simply pass a __range__ attribute when we call print.

conclusion

1. Inherit attributes by borrowing constructors.

2. Complete method inheritance by merging prototype objects.

As shown in the figure:

Disadvantages of this approach

  1. The instance of operator cannot be used; That’s actually pretty easy to change;

What is instanceof?

The instanceof operator is used to check whether the constructor’s prototype property appears on the prototype chain of an instance object.

Iterating through the prototype properties of the instance object to see if they match the constructor’s prototype, instanceof returns true.

According to the Instanceof principle, modify the merged object. Interested readers can try a wave for themselves.

According to the blog of Sanyuan; We can also implement the instanceof operator ourselves.

reference

MDN Obect.assign