JavaScript Inheritance

Recently, I suddenly found myself a little rusty with JavaScript inheritance, so let’s review it again.

It’s best to review the blog about prototype chains before reading this one: https://sunra.top/posts/7a3d3c75/

Design idea of prototype chain

Ancient times

To understand the design concept of JavaScript, we must start from its birth.

In 1994, Netscape released version 0.9 of the Navigator browser. This is the first relatively mature web browser in history, and it was a sensation. However, this version of the browser can only be used for browsing and does not have the ability to interact with visitors. For example, if there is a column “username” on the webpage that requires filling in, the browser cannot determine whether the visitor really filled in, only let the server side judge. If it is not filled in, the server side returns an error and asks the user to fill it out again, which is a waste of time and server resources.

Therefore, Netscape urgently needed a web scripting language to enable browsers to interact with web pages. EngineerBrendan EichIn charge of developing this new language. He felt that there was no need to design it very complicated. This language only needed to be able to complete some simple operations, such as judging whether the user had filled out a form.

1994 was the heyday of object-oriented programming, C++ was the most popular language at the time, and version 1.0 of the Java language was about to be released the following year, and Sun was building momentum.

Brendan Eich is undoubtedly affected. All data types in Javascript are objects, which is very similar to Java. However, he immediately encountered a dilemma. Should we design an “inheritance” mechanism?

Choice

If it is really a simple scripting language, there is no need for “inheritance” mechanism. However, Javascript is full of objects, and there must be a mechanism to connect all objects. So, Brendan Eich finally designed “inheritance”.

However, he does not intend to introduce the concept of “classes”, because once there are “classes”, Javascript is a complete Object Oriented programming language, which seems a bit too formal and increases the difficulty of getting started for beginners.

He considers that both C++ and Java languages use the new command to generate instances.

C++ is written as:

1
ClassName *object = new ClassName(param);

Java is written as:

1
Foo foo = new Foo();

Therefore, he introduced the new command into Javascript to generate an instance object from a prototype object. However, Javascript does not have a “class”, how to represent a prototype object?

At this point, he thought that C++ and Java use the new command, they all call the constructor of the “class”. He made a simplified design, in the Javascript language, the new command is followed not by the class, but by the constructor.

For example, there is now a constructor function called DOG that represents the prototype of a dog object.

1
2
3
4
5
function DOG(name){

this.name = name;

}

Using new on this constructor generates an instance of the dog object.

1
2
3
Var dogA = new DOG ('big hair');

Alert (dogA.name);//Damao

Note the constructor functionthis关键字, which represents the newly created instance object.

Disadvantages of the new operator

One disadvantage of using constructor functions to generate instance objects is that properties and methods cannot be shared.

For example, in the constructor function of the DOG object, set the common property species of an instance object.

1
2
3
4
5
6
7
function DOG(name){

this.name = name;

This.species = 'Canidae';

}

Then, generate two instance objects.

1
2
3
Var dogA = new DOG ('big hair');

Var dogB = new DOG ('Ermao');

The species properties of these two objects are independent, and modifying one will not affect the other.

1
2
3
dogA.species = 'Feline';

Alert (dogB.species);//show "Canidae", not affected by dogA

Each instance object has its own copy of properties and methods. This is not only unable to achieve data sharing, but also a great waste of resources.

Introduction of prototype property

With this in mind, Brendan Eich decided to set a prototype property for the constructor function.

This property contains an object (hereinafter referred to as the “prototype object”), and all the properties and methods that instance objects need to share are placed in this object; those that do not need to be shared are placed in the constructor function.

Once the instance object is created, it will automatically reference the properties and methods of the prototype object. That is, the properties and methods of the instance object are divided into two types, one is local and the other is referenced.

Or take the DOG constructor function as an example, and now rewrite it with the prototype property.

1
2
3
4
5
6
7
8
9
10
11
12
function Dog(name) {
this.name = name;
this.spec = function() {
console.log(this.species);
}
}

Dog.prototype = {species: 'Canidae'}

const dog = new Dog();

dog.spec();

Now, the species property is placed in the prototype object and is shared by the two instance objects. As long as the prototype object is modified, it will affect both instance objects at the same time.

1
2
3
4
5
DOG.prototype.species = 'Feline';

Alert (dogA.species);//felidae

Alert (dogB.species);//felidae

Since all instance objects share the same prototype object, from the outside, the prototype object appears to be the prototype of the instance object, and the instance object appears to “inherit” the prototype object.

This is the design idea of JavaScript inheritance mechanism.

How JavaScript inherits

ES5

Prototype chain inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
function Dog(name) {
this.name = name;
}

function animal(species) {
this.species = species;
}

Dog.prototype = new animal("犬科")

const dog = new Dog();

console.log(dog.species);

Principle:

Species is not a property on the dog instance, but proto, which is Dog.prototype, which is the property on the new animal () instance

Advantages:

Simple and easy to implement, new instances of the parent class and attribute subclasses can access

Disadvantages:

Instance properties can be added in the subclass. If you want to add new prototype properties and methods, you need to add them after the new parent class constructor function

Multiple inheritance cannot be achieved

When creating a subclass instance, parameters cannot be passed to the parent class constructor

Borrowing constructor function inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function Dog(name) {
this.name = name;
Animal.call (this, "Canidae");
}

function Animal(species) {
this.species = species;
this.setCount = function(count) {
this.count = count;
}
}

const dog = new Dog();

dog.setCount(2);

console.log(dog.species);
console.log(dog.count);

Principle:

Species is on the dog instance, because when new Dog () calls the Animal.call method, this is the latter dog instance, so in fact, the Animal method is called in the context of dog, that is to say, when Animal is executed, the context is dog, and the species attribute is directly hung on dog

Count is mounted on the dog instance when executing setCount.

Advantages:

Resolved subclass constructor passing parameters to parent class constructor

Multiple inheritance (call or apply multiple parent classes) can be implemented.

Disadvantages:

Methods are defined in constructor functions and cannot be used to reuse

Cannot inherit prototype properties/methods, only instance properties and methods of parent classes can be inherited

Prototype inheritance (instance inheritance)

1
2
3
4
5
6
7
8
9
10
11
12
13
function Dog(name) {
let instance = new Animal("犬科");
instance.name = name;
return instance;
}

function Animal(species) {
this.species = species;
}

const dog = new Dog();

console.log(dog.species);

Principle:

The dog here is actually an instance of Animal, not an instance of Dog, that is, dog instanceof Dog is false.

This method is to first create an Animal instance and then create new properties on the instance.

Advantages:

Unlimited calling method

Simple and easy to implement

Disadvantages: Cannot inherit multiple times

Combined inheritance

Call the parent class constructor function, inherit the properties of the parent class, and use the parent class instance as a subclass prototype to realize the function to reuse

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
function Animal(species) {
this.species = species;
this.setCount = function(count) {
this.count = count;
}
}

Animal.prototype.setAge = function(age) {
this.age = age;
}

function Dog(name) {
this.name = name;
Animal.call (this, "Canidae");
}

Dog.prototype = new Animal("quanke");
Dog.prototype.constructor = Dog;

const dog = new Dog('first');

dog.setCount(2);
dog.setAge(12);

console.log(dog.species);
console.log(dog.count);
console.log(dog.age);

//Canine
//2
//12

Principle:

Use constructor function inheritance to create properties for each subclass instance, which means that properties on each subclass are exclusive.

Use prototype chain inheritance to share methods on parent class prototypes.

Disadvantages:

Since the parent class was called twice, two instances are generated

Advantages:

Functions to reuse

There is no reference attribute problem

Can inherit properties and methods, and can inherit properties and methods of prototypes

Parasitic combined inheritance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//parent class
function People(name,age){
this.name = name || 'wangxiao'
this.age = age || 27
}
//parent class method
People.prototype.eat = function(){
return this.name + this.age + 'eat sleep'
}
//subcategory
function Woman(name,age){
//Inherit parent class properties
People.call(this,name,age)
}
//Inherit a parent class method
(function(){
//Create an empty class
let Super = function(){};
Super.prototype = People.prototype;
Instances of the parent class serve as prototypes for subclasses
Woman.prototype = new Super();
})();
//fix constructor function pointing problem
Woman.prototype.constructor = Woman;
let womanObj = new Woman();

Principle:

On the basis of combined inheritance, the idea of parasitic inheritance is used to transform the prototype chain inheritance method, because the prototype chain inheritance method will create an instance of the parent class again, which will cause a waste of memory, so an empty class is created, and then The prototype object of the empty class is set as the prototype object of the parent class, and then the proto of the subclass points to an instance of the empty class.

ES6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//class is equivalent to the constructor function in es5
//When defining methods in a class, functions cannot be added before and after, and all are defined in the protopyte attribute of the class
All methods defined in the class are not enumerable
Class can only define methods, not objects, variables, etc
//Both class and method are in strict mode by default
//constructor is an implicit property in es5
class People{
constructor(name='wang',age='27'){
this.name = name;
this.age = age;
}
eat(){
console.log(`${this.name} ${this.age} eat food`)
}
}
//Inherit from parent class
class Woman extends People{
constructor(name = 'ren',age = '27'){
//Inherit parent class properties
super(name, age);
}
eat(){
//Inherit a parent class method
super.eat()
}
}
let wonmanObj=new Woman('xiaoxiami');
wonmanObj.eat();

** Difference between ES5 Inheritance and ES6 Inheritance **

Es5 inheritance first creates its own this pointer in the subclass, and finally adds the method to this

Child.prototype=new Parent() || Parent.apply(this) || Parent.call(this)

Es6 inheritance is to use the keyword to first create an instance object of the parent class this, and finally modify this in the subclass class.

Reference link

https://zhuanlan.zhihu.com/p/37735247

http://www.ruanyifeng.com/blog/2011/06/designing_ideas_of_inheritance_mechanism_in_javascript.html