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
- Create an empty object
- Inheriting the function prototype to the new object
__proto__
The prototype object whose property is assigned to the constructor - Inside the constructor
this
Point to a new object - Executing function body
- 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,
- in
new
when - in
call
when
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 ~