This is the 23rd day of my participation in the August More Text Challenge

πŸ“’ Hello everyone, MY name is Xiao Cheng. This article mainly writes about 7 inheritance methods in JavaScript, deeply understand the problems of each method and how to solve the problems of the new method

πŸ“’ Thank you very much for reading

πŸ“’ may your life be bright and lovely

πŸ’Œ preface

In the last article we looked at the mechanism of the prototype chain and some of the properties associated with it, but the one that is closely related to the prototype chain is inheritance. Why is that?

In JavaScript Advanced Programming, there’s a quote

“Implementing inheritance is the only way ECMAScript supports inheritance, and this is mostly done through prototype chains.”

It is conceivable that the prototype chain plays a crucial role in inheritance

Before the full text begins, may wish to have a look at the outline of this article

πŸ’Ÿ 6 types of inheritance that are closely related

Before ES6 came along, inheritance based on ES5 implementations optimized the problems of the previous generation in each generation, and this is also something we can learn from the JavaScript language. We encounter problems, solve problems, and optimize. Now let’s see how they are implemented step by step

🍏 1. Prototype chain inheritance

1. Basic ideas

The basic idea of stereotype chain inheritance is to inherit properties and methods of multiple reference types through stereotypes

The basic idea is to use the constructor to instantiate the object, using the new keyword, the constructor instance object as the prototype object of the subclass function

2. Implementation method

// Define the parent function
function Father() {
    // Define the parent class attributes
    this.name = 'father'
}
// Add a method to the parent class's prototype
Father.prototype.say = function () {
    console.log('I am the father');
}
// Create a subclass function
function Son() {}
// Implement inheritance
Son.prototype = new Father()
// Print references
console.log(Son.prototype) // FatherΒ {name: "father"}
Copy the code

To explain the above code, we first define a parent function and a child function, and add some properties and methods

Prototype = new Father() How does it understand that

First we need to look at the execution of the new operator

  1. Create an empty object
  2. Inheriting the function prototype to the new object__proto__The prototype object whose property is assigned to the constructor
  3. Inside the constructorthisPoint to a new object
  4. Executing function body
  5. Return this new object

Now that we understand the new process, we know that when we operate on new Father(), this step packages the Father constructor’s prototype object into the Father instance object, Father. __proto__ = father. Prototype; father.proto__ = father. Prototype; This also creates a connection between the subclass and the parent class

Prototype = new Father()

3. Existing problems

From the above analysis, there seems to be no problem overall, but let’s look at this example, right

function Father() {
	// Define the parent class attribute as a reference data type
	this.a = [1.2.3.4]}Copy the code

We change the value of A in the above code to the reference data type, and we know that for reference data types only the reference to it is saved, which is the memory address.

Let’s start by creating two subclasses son1, son2 that inherit from this superclass

let son1 = new Son()
let son2 = new Son()
Copy the code

And then we want to add a value of 5 to the ARRAY a in SON1, and we’re going to do that

son1.a.push(5)
Copy the code

We don’t have to think about it too much son1 has been added, but if we print son2 now, we’ll see that its ARRAY of A’s has been changed as well

This is the problem with the stereotype chain inheritance approach where reference data types are shared by subclasses

4. Advantages and disadvantages

Advantages:
  • Methods of the parent class can be reused
  • The operation is simple
disadvantages
  • For reference data types the data will be shared by subclasses, so change one and change all the others
  • Cannot pass arguments to the parent constructor when creating a subclass instance.

πŸ‰ 2

In order to solve the problem that reference values cannot be shared due to prototype chain inheritance, a method of “stolen constructor inheritance” is developed

1. Basic ideas

In order to solve the problem of reference value sharing, we cannot give subclasses the reference value directly on the prototype object.

Therefore, the superclass constructor can be called in the subclass constructor.

Let’s start with a simple piece of code

function Son() {
	this.a = [1.2.3.4]}Copy the code

What happens if we rewrite our subclass code like this?

When we instantiate the instance objects through the Son constructor, the variable A in each instance object is independent and belongs to itself, and when we modify one, the value of the other is not affected

This is how embezzled constructor inheritance works

2. Implementation method

function Father() {
    this.a = [1.2.3.4]}function Son() {
    Father.call(this)}let son1 = new Son()
let son2 = new Son()
son1.a.push(5)
console.log(son1, son2)
Copy the code

We can see that in the above implementation, there is no direct use of this.a… We use Father. Call (this)

If this. A is used directly, is it still called inheritance? Isn’t it

Father. Call (this)

We originally called this.a directly in a child class. This is similar to calling Father in a child class, except that this refers to the problem

If we call Father() directly in a subclass, its this will point to the window, so we can’t bind the data to the instance, so we need to change this to point to the current subclass constructor

This way you can bind data to each instance object

And since our key statement uses call, we can pass arguments to the superclass constructor to implement passing arguments

3. Existing problems

As you can see from the implementation code above, I intentionally ignored the prototype and did not add methods to the superclass constructor prototype, which is the problem with this method

Father.prototype.say = function () {
    console.log(111);
}
Copy the code

Cannot find say method on subclass

4. Advantages and disadvantages

Advantages:
  • Fixed an issue where reference values could not be shared
  • Being able to pass parameters
Disadvantages:
  • Only instance attributes and methods of the parent class can be inherited, not stereotype attributes and methods of the parent class
  • The parent method cannot be reused. Each time a child class is instantiated, the parent class function is executed. Redeclare a method defined by the parent class. It cannot be reused.

🍊 3. Combination inheritance

In the first two methods, there are certain drawbacks, so they are rarely used in isolation. Therefore, a new inheritance approach was born: composite inheritance (pseudo-classical inheritance), which combines the advantages of both prototype chain and usurped constructor inheritance.

1. Basic ideas

The attributes and methods of the parent prototype are inherited by prototype chain inheritance, and the attributes of the instance are inherited by using stolen constructors

In this way, the implementation defines methods on prototypes for reuse, while ensuring that each instance has its own attributes

2. Implementation method

Put the two methods together

function Father() {
    this.a = [1.2.3.4]
}
Father.prototype.say = function () {
    console.log(111);
}
function Son() {
    Father.call(this)
}
Son.prototype = new Father()
let son1 = new Son()
let son2 = new Son()
Copy the code

All it really does is steal the constructor and add the key code for prototype chain inheritance

Son.prototype = new Father()
Copy the code

In the above code, property A is inherited from the parent class instance by stealing the constructor method, and the parent class’s prototype object is inherited by the prototype chain

As for the specific process, it is only a combination of the two. You can refer to the previous explanation

3. Existing problems

So let’s first print son1 and son2

With this output, we see that there is also an attribute A on its prototype object, but this seems to be the initial value. Let’s think about why.

We bound the Father instance to the Son prototype, but we did it by stealing the constructor method

The attributes of Father are manually added to Son, so there will be an A attribute on the object instantiated by Son, and there will also be an A attribute on the prototype

So what’s the problem with that?

Before we answer that question, let’s count how many times we call the Father constructor,

  1. innewwhen
  2. incallwhen

So on the one hand there are performance issues and on the other hand there are two properties

4. Advantages and disadvantages

Advantages:
  • Solve the problem that attributes are shared in prototype chain inheritance
  • Solve the problem of borrowing a constructor to solve the problem of not inheriting a superclass prototype object
Disadvantages:
  • A parent function that was called twice has a performance problem
  • The two calls result in the same properties or methods on the instance and prototype

πŸ‹ 4. Inheritance of original type

I can’t seem to find the existence significance of this inheritance mode, I don’t know what problem it solves in the combination mode?

1. Basic ideas

The idea behind the primitive inheritance implementation is to assign an object directly to the constructor’s prototype

2. Implementation method

function object(obj) {
    function F(){};
    // Assign the object to the constructor's prototype
    F.prototype = obj;
    // Returns the new object created during new
    return new F();
}
Copy the code

The object function creates a temporary constructor, assigns the passed object to the constructor’s prototype, and returns an instance of the temporary constructor

So how do we use it

let student = {name:'xxx'}
let another = object(student)
Copy the code

We need to prepare a parent object, that is, the object to be inherited, and pass it in as an argument to the object function, which returns an object based on that parent object

3. Existing problems

In fact, it has the same problem as stereotype chain inheritance, properties and methods are shared

let student = {name: ['ljc']}
let one = object(student)
let two = object(student)
one.name.push('aaa')
two.name.push('bbb')
Copy the code

Let’s add attributes to the name array of the one and two objects, and print one.name

This creates the problem of being shared

4. Advantages and disadvantages

Advantages:
  • Good compatibility, simple
  • There is no need to create a separate constructor
Disadvantages:
  • Multiple instances share inherited attributes, which can be tampered with
  • Unable to pass parameters

The object.create () method added in ES5 can replace the Object method above. It also provides specifications for original type inheritance

🍌 parasitic inheritance

1. Basic ideas

Create a function that encapsulates inheritance only, that internally enhances the object in some way, and then returns the object.

That is, to enhance objects on the basis of the original type inheritance.

2. Implementation method

function createAnother(original) {
    let clone = object(original); // Inherits an object to return a new function
    clone.sayHi = function () {
        console.log('hi');
    }; 
    return clone; // Return this object
}
Copy the code

In this code, it seems that we just add a method to the object on the basis of the original object, and encapsulate it as a function for us to use directly

3. Advantages and disadvantages

Advantages:
  • Just focus on the object itself, not the type and constructor scenarios
Disadvantages:
  • Functions are hard to reuse
  • Multiple instances share inherited attributes, which can be tampered with
  • Unable to pass parameters

πŸ‘ 6. Parasitic combination inheritance

There are still efficiency issues with composite inheritance, the main one being that the superclass constructor is always called twice

1. Basic ideas

Combining combined inheritance and parasitic inheritance to reduce the number of calls to the parent class, so as to achieve the goal

2. Implementation method

In the composite inheritance method we call once and new once, resulting in two calls to the parent class, whereas in the parasitic inheritance method we can call the API to implement the prototype of the inherited parent class

We put the two together

The new keyword is no longer used to change prototypes

function Father() {
    this.a = [1.2.3.4]
}
Father.prototype.say = function () {
    console.log(111);
}
function Son() {
    Father.call(this)
}
Son.prototype = Object.create(Father)
let son1 = new Son()
let son2 = new Son()
Copy the code

Use object. create to override the prototype of a subclass, thus reducing calls to the parent class

So if we print SON1 on the console, we’ll see that the problem is solved

3. Existing problems

There are also problems with this approach when we have methods on our subclass prototype

These methods are lost when the prototype is rewritten

Let’s add a sayHi method at the top of the code

Son.prototype.sayHi = function() {
	console.log('Hi')}Copy the code

To solve this problem, you can add the method of subclassing the stereotype after it has been overwritten

4. Advantages and disadvantages

Advantages:
  • It’s basically the best possible succession, and of course the Holy Grail succession

  • The superclass constructor is called only once, saving performance.

  • Avoid generating unnecessary attributes

Disadvantages:
  • The subclass stereotype is rewritten

These are the six inheritance methods in ES5

Inheritance in ES6 🎭

Because inheritance before ES6 was too complex and had too much code, a new approach to inheritance, extends inheritance, has been introduced in ES6

Inheritance is implemented using the extends keyword

class Father {}
class Son extends Father {
    constructor() {
        super()}}Copy the code

This enables the subclass to inherit from the parent class. The key here is to add the super keyword to the subclass’s constructor

One thing to note

The constructor method of a subclass must refer to the super method, otherwise new instances will fail. This is because the subclass’s own this object must first be molded through the superclass constructor to get the properties and methods of the superclass

Then add the subclass’s own properties and methods

If there is no super method, the subclass has no this object and will report an error

There are many more things about class that I won’t go into here

reference

JavaScript Advanced Programming

8 Common JavaScript Inheritance Schemes


The above is about the JS implementation of inheritance 7 methods, of course, there will be some other inheritance methods, holy Grail mode inheritance, copy inheritance and so on a lot of not to mention

That’s all for this article, hope you like πŸ’›, any questions can be left in the comments section oh ~