The first type: prototype chain inheritance

function Parent() {
  this.name = 'parent';
  this.play = [1.2.3];
}
function Child() {
  this.type = 'child';
}
Child.prototype = new Parent();
console.log(new Child);
Copy the code

Although the methods and attributes of the parent class are accessible, there is a potential problem, and I’ll give you another example to illustrate this.

var s1 = new Child();
var s2 = new Child();
s1.play.push(4);
console.log(s1.play, s2.play)
Copy the code

The result of this code is as follows

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.

Second: Constructor inheritance (borrowed from call)

  function Parent() {
    this.name = 'parent';
  }
  Parent.prototype.getName = function(){
    return this.name;
  }
  function Child() {
    Parent.call(this);
    this.type = 'child';
  }
  let child = new Child();
  console.log(child);
  console.log(child.getName());
Copy the code

Execution Result:

As you can see, the last printed child is displayed on the console. In addition to the child attribute type, it also inherits the Parent attribute name. 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.

Third: Combinatorial inheritance (the first two combinations)

 function Parent() {
   this.name='parent';
   this.play=[1.2.3];
 }
 Parent.prototype.getName = function() {
   return this.name;
 }
 
 function Child(){
   // Call Parent a second time
   Parent.call(this);
   this.type = 'child';
 }
 // Call Parent for the first time
 Child.prototype = new Parent();
 Child.prototype.constructor = Child;
 
 var s1 = new Child();
 var s2 = new Child();
 s1.play.push(4);
 console.log(s1.play, s2.play); // Do not affect each other
 console.log(s1.getName()); // parent
 console.log(s2.getName()); // parent
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: We can see that the Parent executes twice, first when changing the prototype of the Child and second when calling the Parent with the call method.

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?

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

  let parent = {
     name: parent,
     friends: ['p1'.'p2'.'p3'].getName: function() {
       return this.name; }}let person = Object.create(parent);
  person.name = 'tom';
  person.friends.push('jerry');
  
  let person2 = Object.create(parent);
  person2.friends.push('lucy');
 
  console.log(person.name);
  console.log(person.name === person.getName());
  console.log(person2.name);
  console.log(person.friends);
  console.log(person2.friends);
Copy the code

As you can see from the above code, the Object. Create method can inherit from ordinary objects, not only the attributes, but also the getName method.

tom

true

parent

[‘p1’, ‘p2’, ‘p3’, ‘jerry’, ‘lucy’]

[‘p1’, ‘p2’, ‘p3’, ‘jerry’, ‘lucy’]

The first result, “Tom”, is easier to understand. Person inherits the parent’s name property, but then customizes it.

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, “parent,” is also easier to understand. Person2 inherits the parent’s name attribute without overwriting it, so it prints the parent object’s attributes.

The last two results are the same, so you should be able to refer to shallow copies. The Object. Create method can implement shallow copies for 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.

Fifth: 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 parent = {
     name: parent,
     friends: ['p1'.'p2'.'p3'].getName: function() {
       return this.name; }}function clone(sup) {
    let clone = Object.create(sup);
    clone.getFriends = function() {
     return this.friends;
    }
    return clone;
  }
  
  let person = clone(parent);
  
  console.log(person.getName());   // parent
  console.log(person.getFriends()); // ['p1', 'p2', 'p3'];
  
Copy the code

In the code above, we can see that person 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 can be seen from the final output result, person adds the getFriends method through the Clone method, thus adding another method to the inheritance process of the ordinary object person. Such inheritance method is 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.

The sixth kind: 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.

  function clone(parent, child) {
    child.prototype = Object.create(parent.prototype);
    child.prototype.constructor = child;
  }
  
  function Parent() {
    this.name = 'parent';
    this.play = [1.2.3];
  }
  Parent.prototype.getName = function(){
   return this.name;
  }
  
  function Child() {
    Parent.call(this);
    this.friends = 'child'
  }
  
  clone(Parent, Child);
  
  Child.prototype.getFriends = function() {
   return this.friends;
  }
  
  let person = new Child();
  console.log(person);
  console.log(person.getName());    // parent
  console.log(person.getFriends())  // child
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.

The subclass constructor calls the attributes and methods of the parent constructor with a call.

A subclass inherits the attributes and methods of its parent’s constructor by pointing its constructor’s prototype to its parent’s constructor’s prototype (improvement: a subclass calls its constructor’s prototype twice by pointing its constructor’s prototype to its parent’s instance —-)

On the whole, parasitic combinatorial inheritance is the best inheritance among the six inheritance modes. In addition, ES6 provides the extends keyword of inheritance

The extends keyword of the seventh ES6

How does extends directly implement inheritance

  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