Look at the following encyclopedia to explain (object-oriented inheritance)!

Through the above refined practical explanation, we can understand the basic role and function of inheritance! That is, you can make subclasses have the attributes and methods of their parent class, redefine, append attributes and methods, and so on.

Advertising: Help click “” grateful!

So, let’s explore the concept of JS inheritance with the following two questions:

  1. How many ways are there to implement JS inheritance?
  2. In what way is the extends keyword implemented in ES6?

JS implementation of several ways of inheritance

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.

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 above code appears to be fine, although the methods and attributes of the parent class are accessible, but there is a potential problem, and I’ll give you another example to illustrate the problem.

  var s1 = new Child2();
  var s2 = new Child2();
  s1.play.push(4);
  console.log(s1.play, s2.play);
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.

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.

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() {
    // 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?

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 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

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.

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.

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 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.

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) {
    // 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.

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
  }

  // 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

Because of browser compatibility issues, if you encounter a browser that does not support ES6, you will need to use Babel to compile ES6 code into ES5, so that some browsers that do not support the new syntax can run.

So what does extends look like in the end? Let’s take a look at the translated code snippet.

function _possibleConstructorReturn (self, call) { 
		// ...
		return call && (typeof call === 'object' || typeof call === 'function')? call : self; }function _inherits (subClass, superClass) { 
    // It can be seen here

	subClass.prototype = Object.create(superClass && superClass.prototype, { 
		constructor: { 
			value: subClass, 
			enumerable: false.writable: true.configurable: true}});if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; 
}


var Parent = function Parent () {
	// Verify if Parent constructs 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

As can be seen from the compiled source code above, it also adopts the parasitic combination inheritance method, so it also proves that this method is a better way to solve inheritance.

conclusion

Below I will JavaScript inheritance way to do a summary of the brain map, easy to remember!

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.

Pass by the old iron, stretch out your precious little hand, your every click can help me add a chicken leg!

Help click on this link: github.crmeb.net/u/xingfu, appreciated, and the open source inside the program help you give a star, no see you can shut down!