1. There is

1. How many ways are there to implement JS inheritance?

2. What inheritance is used to implement the extends keyword in ES6?

2. Inherit the concept

Inheritance makes it possible for a subclass to have various methods and attributes of its parent class

3. The first: 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

The code above appears to be fine, although the methods and attributes of the parent class are accessible, but there is a potential problem.

  var s1 = new Child2();
  var s2 = new Child2();
  s1.play.push(4);
  console.log(s1.play, s2.play);

// print the result [1,2,3,4],[1,2,3,4]
Copy the code

I only changed the play property of S1, so why did S2 change too? The reason is simple, because both instances use 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. To solve this problem, we need to look at other inheritance methods. Let’s look at a second approach to solving the stereotype property sharing problem.

4. 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());  / / complains
Copy the code

By executing the code above, you get something like this.

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 console execution result in this case is shown below.

Therefore, the above results show the advantages and disadvantages of the constructor implementation inheritance. It makes the reference attributes of the parent class not shared, which optimizes the disadvantages of the first method of inheritance. However, there is an obvious disadvantage that only instance properties and methods of the parent class can be inherited, not stereotype properties or 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.

5. Third: Combinatorial inheritance (the first two combinations)

  function Parent3 () {
    this.name = 'parent3';
    this.play = [1.2.3];
  }

  Parent3.prototype.getName = function () {
    return this.name;
  }

  function Child3() {
    // Call Parent3() for the second time
    Parent3.call(this);
    this.type = 'child3';

  }
  Call Parent3() for the first time
  Child3.prototype = new Parent3();
  // Hang the constructor manually and point to your own constructor
  Child3.prototype.constructor = Child3;
  var s3 = new Child3();
  var s4 = new Child3();
  s3.play.push(4);
  console.log(s3.play, s4.play);  // Do not affect each other
  console.log(s3.getName()); // Normal output 'parent3'
  console.log(s4.getName()); // Normal output 'parent3'

Copy the code

By executing the code above, you can see the output from the console, and the previous problems with method one and method two have been resolved.

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

So is there a better way to solve this problem? Read on for a sixth inheritance method to better solve this problem.

The above is more around constructors, so how do you implement inheritance for ordinary objects in JavaScript?

6. The fourth type: Original type inheritance (the first two combinations)

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’s take a look at how inheritance is implemented for ordinary objects through a piece of code.

  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);
  console.log(person4.name === person4.getName());
  console.log(person5.name);
  console.log(person4.friends);
  console.log(person5.friends);
Copy the code

The first result, “Tom,” is easier to understand, and person4 inherits the parent4 name attribute, but customizes it from there.

The second is the inherited getName method that checks if its name is the same as the value in the property, and the answer is true.

The third result, “parent4,” is also easier to understand. Person5 inherits the name attribute of parent4 without overwriting it, so it prints the attributes of the parent object.

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

Then the disadvantages of this method of inheritance are also obvious. Multiple instances of reference type attribute point to the same memory, there is the possibility of tampering. Next, let’s take a look at another inheritance method optimized on the basis of this inheritance — parasitic inheritance.

7. Parasitic inheritance

The method of inheritance is called parasitic inheritance, in which a shallow copy of the target object is obtained using the original inheritance, and then the power of the shallow copy is enhanced by adding some methods.

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());
  console.log(person5.getFriends());
Copy the code

From the code above, we can see that person5 is an instance generated by parasitic inheritance, and not only does it have a getName method, but it also has a getFriends method at the end, as shown in the figure below.

As you can see from the final output, person5 uses the Clone method to add the getFriends method, thus adding another method to the generic object’s inheritance process, which is called parasitic inheritance.

Parasitic combinatorial inheritance addresses some of the drawbacks I mentioned in the third combinatorial inheritance approach above, namely the waste of calling the parent class’s constructor twice.

8. Sixth: Parasitic combinatorial inheritance

Combined with the inheritance method mentioned in the fourth method and the Object. Create method to solve the inheritance problem of common objects, we reformed the advantages and disadvantages of the previous inheritance methods and obtained the parasitic combination inheritance method, which is also the relatively optimal inheritance method among all inheritance methods. The code is as follows.

Copy the code

  function clone (parent, child) {
    // Use object.create to reduce the need for multiple constructs in composite inheritance
    child.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 method 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. Let’s take a look at the execution results of the above code.

You can see the result printed by Person6, where the properties are inherited and the methods are fine, and the expected results are printed.

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.

9.ES6 implements the extends keyword 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
  }
  // Prototype method
  Prototype. GetName = function() {}
  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 class
Copy the code

summary

Object. Create is used to divide the different inheritance methods. Finally, parasitic combinatorial inheritance is the optimal inheritance method modified by combinatorial inheritance.

To sum up, we can see that different inheritance methods have different advantages and disadvantages, and we need to have a thorough understanding of the advantages and disadvantages of each method, so that in daily development, we can choose the inheritance method most suitable for the current scenario.

Development, in the front of the daily work, developers tend to ignore the relevant systemic study for inheritance, but because of the inheritance method is more, the method to realize each detail is scattered, many developers are difficult to have a systematic, overall understanding, cause inefficient, and the code is difficult to further improve.