To recap, I recently found myself a little rusty in JavaScript inheritance.

Before reading this blog, it’s best to review the blog about prototype chains: sunra.top/2020/12/10/…

Design idea of prototype chain

In ancient times

To understand the design of Javascript, you have to start with its birth.

In 1994, Netscape released version 0.9 of its Navigator browser. It was the first full-fledged web browser in history, and it was a hit. 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 a web page asks for a “user name,” the browser can’t tell if the visitor actually filled it in, leaving it to the server. If not, the server returns an error and asks the user to fill it out again, which is a waste of time and server resources.

So Netscape desperately needed a web scripting language that would allow the browser to interact with the web. Engineer Brendan Eich is responsible for developing the new language. He said it didn’t need to be complicated, but that the language could do simple things, such as determine whether a user had filled out a form.

In 1994, at the height of object-oriented programming, C++ was the most popular language, and with version 1.0 of the Java language due the following year, Sun was on a roll.

Brendan Eich was definitely affected. All data types in Javascript are objects, much like in Java. But he then faced a dilemma: should he design an inheritance mechanism?

choice

If it were really a simple scripting language, there would be no need for “inheritance”. However, Javascript is full of objects, and there must be a mechanism to link them all together. So Brendan Eich ended up designing inheritance.

However, he is not going to introduce the concept of “classes,” which would make Javascript a full object-oriented programming language, which seems a bit too formal and makes getting started difficult for beginners.

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

C++ is written as:

ClassName *object = new ClassName(param);
Copy the code

Java is written as:

Foo foo = new Foo();
Copy the code

Therefore, he introduced the new command into Javascript to generate an instance object from a prototype object. However, Javascript doesn’t have “classes”, so how do you represent prototype objects?

Both C++ and Java use the new command to call the constructor of the class. Instead of a class, the new command is followed by a constructor in Javascript.

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

function DOG(name){

    this.name = name;

}
Copy the code

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

var dogA = new DOG('heavy');

alert(dogA.name); / / heavy hair
Copy the code

Notice the this keyword in the constructor, which represents the newly created instance object.

Disadvantages of the new operator

Using constructors to generate instance objects has the disadvantage of not sharing properties and methods.

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

function DOG(name){

    this.name = name;

    this.species = 'dog';

}
Copy the code

Then, two instance objects are generated:

var dogA = new DOG('heavy');

var dogB = new DOG('二毛');
Copy the code

The species property of these two objects is independent, and modifying one does not affect the other.

dogA.species = The 'cat';

alert(dogB.species); // Display "canine", not affected by dogA
Copy the code

Each instance object has its own copy of properties and methods. This not only fails to share data, but is also a huge waste of resources.

Introduction of the Prototype attribute

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

This property contains an object (the “Prototype object”) in which all instance objects share properties and methods. Properties and methods that do not need to be shared are stored in constructors.

The instance object, once created, automatically references the properties and methods of the Prototype object. That is, the properties and methods of an instance object are divided into two types, one is local and the other is referenced.

Using the DOG constructor as an example, now overwritten with the Prototype property:

function Dog(name) {
    this.name = name;
    this.spec = function() {
        console.log(this.species);
    }
}

Dog.prototype = { species: 'dog' }

const dog = new Dog();

dog.spec();
Copy the code

The Species property is now in the Prototype object and is shared between the two instance objects. Whenever you modify the Prototype object, both instances are affected.

DOG.prototype.species = The 'cat';

alert(dogA.species); / / the cat

alert(dogB.species); / / the cat
Copy the code

Since all instance objects share the same Prototype object, the Prototype object looks as if it is a prototype of the instance object, and the instance object looks as if it “inherits” the Prototype object.

This is the idea behind JavaScript inheritance.

JavaScript inheritance

ES5

Prototype chain inheritance

function Dog(name) {
    this.name = name;
}

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

Dog.prototype = new animal("Dog")

const dog = new Dog();

console.log(dog.species);
Copy the code

Principle:

Species is not an attribute on the dog instance, but dog.__proto__, which is dog.prototype, which is new Animal ()

Advantages:

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

Disadvantages:

You can add instance attributes to a subclass, but you need to add new stereotype attributes and methods after the new parent constructor

Multiple inheritance cannot be implemented

When creating a subclass instance, you cannot pass arguments to the superclass constructor

Borrow constructor inheritance

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

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);
Copy the code

Principle:

Animal. Call (‘ this’, ‘this’,’ dog ‘, ‘this’,’ dog ‘); You just hang the Species property directly on dog

Count is mounted on the dog instance when setCount is executed.

Advantages:

Resolved that subclass constructors pass arguments to superclass constructors

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

Disadvantages:

Methods are defined in constructors and cannot be reused

You cannot inherit stereotype properties/methods, only instance properties and methods of the parent class

Primitive inheritance (instance inheritance)

function Dog(name) {
    let instance = new Animal("Dog");
    instance.name = name;
    return instance;
}

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

const dog = new Dog();

console.log(dog.species);
Copy the code

Principle:

Here dog is actually an instanceof Animal, not an instanceof dog, so dog instanceof dog is false.

The way to do this is to create an instance of Animal and then create new properties on it.

Advantages:

There is no restriction on how to call

Simple and easy to implement

Disadvantages: Cannot inherit multiple times

Combinatorial inheritance

Call the parent class constructor, inherit the attributes of the parent class, and reuse the function by using the parent class instance as the prototype of the subclass

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."Dog");
}

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
Copy the code

Principle:

Constructor inheritance is used to create properties for each subclass instance, meaning that properties on each subclass are exclusive.

Methods on the parent prototype are shared by inheritance of prototype chain.

Disadvantages:

Because the parent class is called twice, two instances are generated

Advantages:

Functions can be reused

There is no reference attribute problem

You can inherit properties and methods, and you can inherit properties and methods of stereotypes

Parasitic combinatorial inheritance

/ / parent class
function People(name,age){
  this.name = name || 'wangxiao'
  this.age = age || 27
}
// Parent method
People.prototype.eat = function(){
  return this.name + this.age + 'eat sleep'
}
/ / subclass
function Woman(name,age){
  // Inherits the superclass attributes
  People.call(this,name,age)
}
// Inherits the parent method
(function(){
  // Create an empty class
  let Super = function(){};
  Super.prototype = People.prototype;
  // An instance of the parent class is the prototype of the subclass
  Woman.prototype = newSuper(); }) ();// Fix the constructor pointing problem
Woman.prototype.constructor = Woman;
let womanObj = new Woman();
Copy the code

Principle:

In combination, on the basis of inheritance, using the ideas of parasitic inheritance, modified prototype chain inheritance way, because the prototype chain will inherit way to create an instance of the parent class again, will cause the waste of memory, so they create an empty class, and then put the empty class prototype object as the prototype of the parent object, let subclasses __proto__ pointing to an instance of the empty class.

ES6

//class is equivalent to the es5 constructor
// When defining a method in a class, do not add function before or after it
// All methods defined in class are not enumerable
// Class can only define methods, not objects, variables, etc
// Strict mode is the default in both class and method
//es5 uses constructor as an implicit attribute
class People{
  constructor(name='wang',age='27'){
    this.name = name;
    this.age = age;
  }
  eat(){
    console.log(`The ${this.name} The ${this.age} eat food`)}}// Inherits the parent class
class Woman extends People{ 
   constructor(name = 'ren',age = '27'){ 
     // Inherits the superclass attributes
     super(name, age); 
   } 
    eat(){ 
     // Inherits the parent method
      super.eat() 
    } 
} 
let wonmanObj=new Woman('xiaoxiami'); 
wonmanObj.eat();
Copy the code

ES5 inheritance and ES6 inheritance

Es5 inheritance starts by creating your own this pointer in a subclass, and finally adds methods to this

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

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

Refer to the link

zhuanlan.zhihu.com/p/37735247

www.ruanyifeng.com/blog/2011/0…