Before introducing javascript inheritance, we need to understand how the language implements inheritance. I also looked up a lot of data here. Let’s start with javascript prototypes and prototype chains.

Prototype chain

The javascript language was originally designed for convenience, so designer Brendan Eich didn’t use class like Java when he developed it. Javascript uses the new command followed by a constructor. This part refers to the explanation of French programmer Vjeux.

Function DOG(name){this.name = name; } var dogA = new DOG(' dogA '); alert(dogA.name); / / heavy hairCopy the code

The this keyword in the constructor represents the newly created instance object. Therefore, you cannot share the properties and methods of instance objects when they are generated.

Function DOG(name){this.name = name; This. Species = 'species '; } var dogA = new DOG(' dogA '); Var dogB = new DOG(' erao '); Doga. species = 'feline '; alert(dogB.species); // Display "canine", not affected by dogACopy the code

Hence the introduction of the Prototype property, which contains an object (” Prototype object “) in which all the properties and methods shared by instance objects are stored. Properties and methods that do not need to be shared are stored in constructors.

The instance object, once created, automatically references the properties and methods of the Prototype object. That is, the properties and methods of an instance object are divided into two types, one is local and the other is referenced.

Function DOG(name){this.name = name; } prototype = {species: 'DOG'}; Var dogA = new DOG(' DOG '); Var dogB = new DOG(' erao '); alert(dogA.species); / / canine alert (dogB. Species); / / canineCopy the code

Since all instance objects share the same Prototype object, the Prototype object looks as if it is a prototype of the instance object, and the instance object looks as if it “inherits” the Prototype object.

The design of the prototype and prototype chain is mainly based on the following figure

Help you comb through the relationship between javascript prototype chain and prototype. Let’s start with six inheritance methods

The first type: prototype chain inheritance

Prototype chain inheritance is one of the common inheritance methods, which involves the constructor, prototype and instance, there is a certain relationship between the three, that is, each constructor has a prototype object, the prototype object contains a pointer to the constructor, and the instance contains a pointer to the prototype object.

Let’s look at the code.

  function Parent1() {
    this.name = 'parent1';
    this.play = [1, 2, 3]
  }
  function Child1() {
    this.type = 'child2';
  }
  Child1.prototype = new Parent1();
  console.log(new Child1());
Copy the code

Inheritance through the stereotype chain we talked about above because both instances are using the same stereotype object. Their memory space is shared, and when one changes, the other changes, which is a disadvantage of using prototype chain inheritance.

Second: Constructor inheritance (with call)

Look directly at the code, as shown below.

function Parent1(){ this.name = 'parent1'; } Parent1.prototype.getName = function () { return this.name; } function Child1(){ Parent1.call(this); this.type = 'child1' } let child = new Child1(); console.log(child); // No problem console.log(child.getname ()); / / complainsCopy the code

You can see that the last printed child is displayed on the console, inheriting the Parent1 attribute name in addition to the type attribute of Child1. In this way, the subclass can get the attribute values of the parent class, which solves the drawbacks of the first method of inheritance. However, the problem is that once there are methods defined by the parent class in the prototype object, the subclass cannot inherit those methods.

The above two inheritance methods have their own advantages and disadvantages, so combining the advantages of the two, the following combination inheritance method is generated.

Third: Combinatorial inheritance (the first two combinations)

This method combines the advantages and disadvantages of the previous two inheritance methods. The combined inheritance code is as follows.

function Parent3 () { this.name = 'parent3'; this.play = [1, 2, 3]; } Parent3.prototype.getName = function () { return this.name; } function Child3() {Parent3() parent3.call (this); this.type = 'child3'; } // call Parent3() child3. prototype = new Parent3(); / / hang up the constructor manually, pointing to his own constructor Child3. The prototype. The constructor = Child3; var s3 = new Child3(); var s4 = new Child3(); s3.play.push(4); console.log(s3.play, s4.play); Console.log (s3.getName()); // Output 'parent3' console.log(s4.getName()); // Normal output 'parent3'Copy the code

But here’s a new problem: Parent3 (Child3, Child3, Child3); Parent3 (Child3, Child3, Child3, Child3, Child3); This is not what we want to see.

Fourth: original type inheritance

I have to mention the object.create method in ES5, which takes two parameters: an Object to be used as a prototype for the new Object, and an Object (optional) to define additional properties for the new Object.

let parent4 = { name: "parent4", friends: ["p1", "p2", "p3"], getName: function() { return this.name; }}; let person4 = Object.create(parent4); person4.name = "tom"; person4.friends.push("jerry"); let person5 = Object.create(parent4); person5.friends.push("lucy"); console.log(person4.name); //tom console.log(person4.name === person4.getName()); //true console.log(person5.name); //parent4 console.log(person4.friends); //["p1", "p2", "p3"."jerry","lucy"], console.log(person5.friends); //["p1", "p2", "p3"."jerry","lucy"],Copy the code

The Object. Create method can be used to implement shallow copies of some objects.

Fifth: parasitic inheritance

Using primitive inheritance, you can obtain a shallow copy of the target object, and then augment that shallow copy by adding a method that produces parasitic inheritance.

Although it has the same advantages and disadvantages as original inheritance, parasitic inheritance still adds more methods to the parent class than original inheritance for common object inheritance. So let’s see how the code works.

let parent5 = { name: "parent5", friends: ["p1", "p2", "p3"], getName: function() { return this.name; }}; function clone(original) { let clone = Object.create(original); clone.getFriends = function() { return this.friends; }; return clone; } let person5 = clone(parent5); console.log(person5.getName()); //parents console.log(person5.getFriends()); //["p1", "p2", "p3"]Copy the code

The sixth kind: parasitic combinatorial inheritance

Object. Creat method to solve common Object inheritance problem, this is the relative best of all inheritance methods.

function clone (parent, Prototype = object.create (parent. Prototype); child.prototype.constructor = child; } function Parent6() { this.name = 'parent6'; this.play = [1, 2, 3]; } Parent6.prototype.getName = function () { return this.name; } function Child6() { Parent6.call(this); this.friends = 'child5'; } clone(Parent6, Child6); Child6.prototype.getFriends = function () { return this.friends; } let person6 = new Child6(); console.log(person6); console.log(person6.getName()); console.log(person6.getFriends());Copy the code

It can be seen from this code that this parasitic combinatorial inheritance can basically solve the shortcomings of the previous inheritance methods, better achieve the desired results of inheritance, but also reduce the number of construction, reduce the cost of performance.

On the whole, parasitic combinatorial inheritance is the best inheritance among the six inheritance modes. In addition, ES6 provides the extends keyword for inheritance, so let’s take a look at the logic behind extends that implements inheritance.

The extends keyword of ES6 implements logic

Using the extends sugar in ES6, it’s easy to implement JavaScript inheritance directly with keywords, but if you want to understand how extends sugar is implemented, you have to dig into the underlying logic of extends.

Let’s take a look at how inheritance can be implemented directly using extends.

Class Person {constructor(name) {this.name = name} class Person {constructor(name) {this.name = name getName() {... } getName = function () { console.log('Person:', this.name) } } class Gamer extends Person { constructor(name, Age) {// If there is a constructor in a subclass, you need to call super() first before using "this". Super (name) this.age = age}} const asuna = new Gamer(' asuna ', 20) asuna.getName() // Successfully access the method of the parent classCopy the code

Compiled with Babel, it looks like this

function _possibleConstructorReturn (self, call) { return call && (typeof call === 'object' || typeof call === 'function') ? call : self; } function _inherits (subClass, Prototype = Object. Create (superClass && superClass. Prototype, {constructor: {value: //) {constructor: {value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var Parent = function Parent () {this _classCallCheck(this, Parent); }; 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));Copy the code

In fact, we’ll see that extend is inherited as a parasitic combination.

So that’s the end of the six inheritance methods.

Reference:

This part of the content is arranged by referring to the core principles of javascript.