Class declaration and instantiation

Classes are generally declared in one of two ways

/ / class declaration var Animal = function (name) {enclosing name = name | | 'Animal'; }; Animal.prototype.eat = function(food="bone"){ console.log(`${this.name} is eating ${food}`); } / / ES6 class declares class Animal1 {constructor (name) {enclosing name = name | | 'es6_dog'; } eat(food="bone"){ console.log(`${this.name} is eating ${food}`); } } let dog = new Animal("dog"); let dog1 = new Animal1(); dog.eat('fish'); dog1.eat(); Output: dog is eating fish ES6_dog is eating boneCopy the code

How to implement inheritance

There are two main ways to implement inheritance:

Inheritance is implemented with constructors

Let’s do an example

function Parent1 () { this.name = 'parent1'; } function Child1 () { Parent1.call(this); This. type = 'child1'; } console.log(new Child1());Copy the code

The output

As you can see, Child1 is generated with the parent attribute name, which implements inheritance. Why is inheritance implemented?

Parent1.call(this) is executed in Child1; Inheritance is implemented by executing the parent constructor in the body of the child class and changing the context in which the function is run (i.e. the reference to this) so that this refers to the class Child1. This causes the properties of the parent class to be mounted on the child class.

However, this method of inheritance has a disadvantage. It only inherits the properties of the parent class, but not the properties of the parent class. Continue with the code above

Parent1.prototype.say = function () {
    console.log("Parent1 prototype")
};

new Child1().say()
Copy the code

As can be seen from the result, there is no “say” method in Child1, because “say” is added to the prototype of the parent class. This inheritance method only changes the orientation of the constructor of the parent class in the body of the child class, and does not inherit the properties of the prototype.

Inheritance with prototype chain

The prototype chain is directly used here, and I won’t go into detail about it. If you don’t know much about the prototype chain, it is recommended to take a look at this first and talk about the Javascript prototype chain in detail

function Parent2 () { this.name = 'parent2'; this.play = [1, 2, 3]; } function Child2 () { this.type = 'child2'; } Child2.prototype = new Parent2(); // By pointing the prototype of Child2 to Parent2Copy the code

Check it out in your browser

As you can see, the instance of Child2 has the Parent2 attribute in its __proto__ attribute, thus enabling Child2 to inherit from Parent2.

But this inheritance method also has its drawbacks. Let’s look at the code

var s1 = new Child2();
var s2 = new Child2();
s1.play.push(4);
Copy the code

console.log(‘s1.play:’+s1.play); console.log(‘s2.play:’+s2.play);

Print the result

We only changed the s1 properties in this instance, only to find that Child2 other instance attributes are changed, because s1 to modify its prototype properties, the prototype of the property modification, any attributes of a class inherits from the prototype will change together, therefore Child2 between instances is not isolated, it is clearly not what we want.

Optimize the combination mode of 1

The combination method is the combination of the first two methods, the above two ways have shortcomings, this way to solve the above two ways.

Look at the code

function Parent3 () { this.name = 'parent3'; this.play = [1, 2, 3]; } function Child3 () { Parent3.call(this); This. type = 'child3'; } Child3.prototype = new Parent3(); Var s3 = new Child3(); var s3 = new Child3(); var s4 = new Child3(); s3.play.push(4); console.log(s3.play, s4.play);Copy the code

Print the result

As you can see, modifying the properties of an instance does not change the properties of the parent class.

This inheritance method combines the advantages of the constructor and the inheritance method of the prototype chain, and makes up for the shortcomings of the two. There are no shortcomings in the function.

This approach is not perfect, however, because when an instance of a subclass is created, the constructor of the superclass is executed twice.

The constructor is executed twice each time an instance is created. This is unnecessary because the parnet property is already executed in the child when the constructor (parent3.call (this)) is inherited, and there is no need to execute it again when the outside prototype chain is inherited. So, let’s make another optimization of this method.

Optimization 2. Optimization of combination mode

The problem with the above inheritance approach is that inheriting the prototype again executes the constructor of the superclass, so optimization starts from that point.

In order to solve the disadvantages of inheritance by constructor (the first one in this article), the attributes in the prototype of the parent class can not be inherited, so the prototype of the child class is pointed to the parent class.

However, the properties of the superclass already exist in the subclass, and the subclass only lacks the properties of the prototype of the superclass, so we make an optimization based on this.

function Parent4 () { this.name = 'parent4'; this.play = [1, 2, 3]; } function Child4 () { Parent4.call(this); this.type = 'child4'; } Child4.prototype = Parent4.prototype; Var s5 = new Child4(); var s5 = new Child4(); var s6 = new Child4(); console.log(s5, s6); console.log(s5 instanceof Child4, s5 instanceof Parent4); console.log(s5.constructor);Copy the code

In this method of inheritance, we do not directly point the prototype of the subclass to the superclass, but to the prototype of the superclass. In this way, the second execution of the superclass constructor is avoided, and the optimization for the combination mode is completed. But there’s a little bit of a problem, so let’s look at the output

You can see that s5 is derived from new Child4(), but its constructor is Parent4.

This is because the class Child4 does not have a constructor. Its constructor is taken from one level up in the prototype chain, Parent4. So here, finally, can accept the most perfect inheritance to everyone.

And then…

Optimization 3 Perfect optimization of combination

Let’s look at the code first

function Parent5 () { this.name = 'parent5'; this.play = [1, 2, 3]; } function Child5 () { Parent5.call(this); this.type = 'child5'; } // Point the subclass's prototype to the intermediate Object created by object.create. / / the Child5 prototype constructor to own Child5 prototype. The constructor = Child5; Var s7= new Child5(); console.log(s7 instanceof Child5, s7 instanceof Parent5) console.log(s7.constructor);Copy the code

Create (parent5.prototype). In this example, the subclass is separated from the superclass constructor by pointing the prototype to object.create (parent5.prototype). However, the subclass does not have its own constructor, so the subclass constructor is set immediately after, thus achieving perfect combination inheritance.

The test results

 

Optimize 4 ES6 inheritance
// If you want to define a method in a class, you can only define a method in a class. // If you want to define a method in a class, you can only define a method in a class. Class People {constructor(name = 'god', age = 100) {this.name = name; this.age = age; } class Women extends People {constructor() {console.log(' ${this.name} ${this.age} eat food ')}} class Women extends People {constructor(name =) 'people', age = 27) {// inherit superclass attribute super(name, age); } let womenObj = new Women('xiaoxiami');} let womenObj = new Women('xiaoxiami'); womenObj.eat(); // Es5 inheritance creates the instance object of the subclass, and then adds the method of the Parent class to this (parent-.apply (this)). // Es6 inheritance is to create an instance object of the parent class using the keyword super, and then modify this in the subclass class.Copy the code

Remarks (call,apply,bind) implementation principle

Function.prototype.myCall = function (context, ... args) { let ctx = context || window; // Define the currently invoked method on cxt.func.(in order to bind this as an object call) // Create a unique Symbol variable to avoid duplication. ctx[func] = this; let res = args.length > 0 ? ctx[func](... args) : ctx[func]; delete ctx[func]; return res; } let arg = {length: 2, 0: 'king', 1: 'brook'}; / can/will be length attribute object into an Array of the let argArr = Array. The prototype. Slice. Call (arg); console.log(argArr[0]); // The first part of the call is the same as the first part of the call. But type must be an array or an array of the Function. The prototype. MyApply = Function (the context, the args = []) {let CTX = context | | window; // Define the currently invoked method on cxt.func.(in order to bind this as an object call) // Create a unique Symbol variable to avoid duplication. ctx[func] = this; let res = args.length > 0 ? ctx[func](... args) : ctx[func](); delete ctx[func]; return res; } Function.prototype.myBind = function (context, ... Args) {// create a new variable and assign it to const fn = this (const fn = this) // Determine if there is an argument passed in, and assign it to [] args = args? Return function newFn(...) return function newFn(...) newFnArgs) { if (this instanceof newFn) { return new fn(... args, ... newFnArgs) } return fn.apply(context, [...args, ...newFnArgs]) } }Copy the code