Concept of inheritance

An example in life: first to define a Class (Class) is called car, car properties including color, tire, brand, velocity, displacement, etc., this Class can be derived by “cars” and “truck” two classes, so can attribute on the basis of the car, to add a car boot, add a big crate to truck. So cars and trucks are not the same, but they both belong to the car class, so this example can specify the inheritance relationship between cars, cars, and trucks. Inheritance makes it possible for a subclass to have the methods and attributes of its parent class, such as the example above where “car” and “truck” inherit the attributes of the car, respectively, without having to define the attributes that the car already has in “car” again. While “car” inherits “car”, it can also redefine some properties of the car, rewrite or override some properties and methods, so that it can obtain properties and methods different from the parent class of “car”.

Js implements inheritance

Prototype chain inheritance

Prototype chain inheritance is one of the common inheritance methods, which involves constructors, prototypes and instances. There is a certain relationship among them, 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. Want to understand the relationship between these three, you can produce my article JS prototype, prototype chain know how much below we combined with code to understand.

 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 above code seems to be fine, although the methods and properties of the parent class are accessible, but there is a potential problem. Let me give you another example to illustrate this problem.

  var s1 = new Child1();
  var s2 = new Child2();
  s1.play.push(4);
  console.log(s1.play);  / / [1, 2, 3, 4]
   console.log(s2.play); / / [1, 2, 3, 4]
Copy the code

Obviously I only changed the play attribute of S1, why did S2 also change? The reason is simply that both instances use the same prototype object. Their memory space is shared, and when one changes, the other changes, which is a disadvantage of using prototype chain inheritance. Note Child1. Prototype. The constructor will equal the Parent1 rather than Child1, because Child1 execution. The prototype = new Parent1 (after); Since there is no constructor in new Parent1(), the constructor property of the prototype object of the prototype object is obtained through the prototype chain. So Child1. Prototype. Constructor = Parent1. To get the constructor property right, inheritance can be implemented with the following code

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

To solve this problem, we need to look at other inheritance methods, so let’s look at the second method that can solve the problem of shared prototype properties.

Constructor inheritance (with call)

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

This is the result of executing the above code.



As you can see, the last printed child is displayed in the console, inheriting the name property of Parent1 in addition to the type property of Child1. In this way, the subclass can get the property value of the parent class, which solves the disadvantages of the first method of inheritance, but the problem is that once the parent class has methods defined by itself, the subclass will not be able to inherit those methods. The console execution results for this situation are shown in the figure below.

Therefore, we can see the advantages and disadvantages of the constructor implementation of inheritance from the above results. It makes the reference properties of the parent class not shared, which optimizes the disadvantages of the first inheritance method. However, the disadvantages are obvious — only the instance properties and methods of the parent class can be inherited, not the prototype properties or methods.

Each of the above two inheritance methods has its own advantages and disadvantages. Combining the advantages of the two methods, the following combined inheritance method is produced.

Combination inheritance (the first two combinations)

This approach combines the advantages and disadvantages of the first 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() {
    // Call Parent3() the second time
    Parent3.call(this);
    this.type = 'child3';
  }

  // The first call to Parent3()
  Child3.prototype = new Parent3();
  // Manually hang the constructor, pointing to its own constructor
  Child3.prototype.constructor = Child3;
  var s3 = new Child3();
  var s4 = new Child3();
  s3.play.push(4);
  console.log(s3.play, s4.play);  // No mutual influence
  console.log(s3.getName()); // Output 'parent3' normally
  console.log(s4.getName()); // Output 'parent3 'normally
Copy the code

When you execute the above code, you can see the console output, and the previous problems with methods 1 and 2 are resolved.



But here’s a new problem: Parent3 is executed twice. The first time is when the Parent3 prototype is changed. The second time is when the Parent3 prototype is called through the call method. This is something we do not want to see. So is there a better way to solve this problem? The following parasitic combinatorial inheritance can better solve this problem.

This is more of a constructor-oriented approach, so how do you implement inheritance for normal objects in JavaScript?

Original type inheritance

The important thing to mention here is the object.create method in ES5. This method takes two arguments: an Object that is used as a prototype for the new Object and an optional Object that defines additional properties for the new Object. Let’s 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 child = Object.create(parent4);
  child.name = "tom";
  child.friends.push("jerry");

  let child2 = Object.create(parent4);
  child2.friends.push("lucy");

  console.log(child.name); // tom
  console.log(child.name === child.getName()); // true
  console.log(child2.name); // parent4
  console.log(child.friends); // ["p1", "p2", "p3", "jerry","lucy"]
  console.log(child2.friends); // ["p1", "p2", "p3", "jerry","lucy"]
Copy the code

The first result, “Tom”, is easier to understand. The child inherits the name attribute of parent4, but customizes it on top of that.

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

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

The last two outputs are the same, so you should be able to think about shallow copies. The Object. Create method can be used to create shallow copies for some objects.

The disadvantages of this inheritance method are also obvious. The reference type attribute of multiple instances points to the same memory, and there is the possibility of tampering. Next, let’s look at another inheritance method optimized on the basis of this inheritance — parasitic inheritance.

Parasitic inheritance

Inheritance that uses the original type of inheritance to get a shallow copy of the target object and then takes advantage of that ability to augment it by adding methods is called parasitic inheritance.

Although its advantages and disadvantages are the same as the original type inheritance, for ordinary object inheritance, parasitic inheritance still adds more methods on the basis of the parent class than the original type inheritance. So let’s see how the code does that.

   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 above code, you can see that Person5 is an instance generated through parasitic inheritance, and not only does it have a getName method, but it also ends up with a getFriends method, as shown in the figure below.

As you can see from the final output, Person5 adds a getFriends method to the clone method, which adds another method to the inheritance process of person5, a common object. This inheritance method is called parasitic inheritance.

Parasitic combinatorial inheritance

Some of the drawbacks of composite inheritance mentioned above, namely the waste of calling the constructor of the parent class twice, are addressed with parasitic composite inheritance.

Combined with the inheritance method mentioned in the original type inheritance and the Object. Create method to solve the inheritance problem of ordinary objects, we reformed on the basis of the advantages and disadvantages of the previous several inheritance methods, and got the parasitic combinative inheritance method, which is also the relatively optimal inheritance method among all inheritance methods. The code is as follows.

function clone (parent, child) {
    // Instead, use object.create to reduce the need for one more construct in composition 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 combinational inheritance method can basically solve the disadvantages of the previous several inheritance methods, better achieve the desired result of inheritance, but also reduce the number of constructs, reduce the performance overhead, let’s take a look at the execution results of the above section of code.



You can see that Person6 prints out the results, with the attributes inherited and the methods fine, producing the expected results.

As a whole, the parasitic combinatorial inheritance is the best inheritance among the six inheritance methods.

Es6 extends implements inheritance

We can use the extends syntax sugar in ES6 to make it easy to directly implement JavaScript inheritance using keywords.

class Person {
  constructor(name) {
    this.name = name
  }
  // Prototype method
  Person.prototype.getName = function() {}
  GetName () {... }
  getName = function () {
    console.log('Person:'.this.name)
  }
}
class Gamer extends Person {
  constructor(name, age) {
    // If a subclass has constructors, we need to call super() before using "this".
    super(name)
    this.age = age
  }
}
const asuna = new Gamer('Asuna'.20)
asuna.getName() // The parent class method was successfully accessed
Copy the code

Because of browser compatibility issues, if you run into a browser that doesn’t support ES6, you’ll need to use Babel, a compiler tool, to compile ES6 code into ES5 and make it work on browsers that don’t support the new syntax. The fact that compiled extends also uses parasitic combination inheritance proves that it is a better approach to dealing with inheritance.

conclusion

Create is used to divide the different types of inheritance. The last type of parasitic combination inheritance is the best type of inheritance modified by combination inheritance. The syntax sugar of extends is similar to that of parasitic combination inheritance.