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