Explain the implementation of ES5 class and inheritance in detail
1. Preface
Class implementation and class inheritance in ES5 is a high point in JS that has to be crossed and conquered. Whether in the process of learning, in order to better understand the implementation method of ES 6 class and learn the knowledge of prototype chain and constructor, or to deal with the interviewer in the interview, let the interviewer describe the principle of ES5 class and inheritance, or even manually implement ES5 inheritance (bloggers have encountered it in the interview). This high point must be won. Let's analyze it step by step and get to the bottom of it!
2. Implementation of classes in Es5
The constructor is used to implement the class in ES5. Before analyzing the code, take a brief look at the following concepts. It doesn't matter if you don't understand them. There is a detailed description in the example code:
- Constructor: in fact, it is a function in which attributes and methods are defined
- Instance: an instance is obtained through the new constructor.
- Instance property (method): this property (method) is defined in the constructor. Special note: only instances are available
- Prototype chain property (method): this property (method) is defined on the prototype chain of the constructor. In particular, the prototype chain properties are shared among instances, which are illustrated and demonstrated in the example
- Static property (method): this property (method) is defined directly on the constructor. Special note: only constructors can get
An example is given to illustrate the implementation of classes in ES5. Among them, Person is a constructor, name, age and star are instance attributes, init is instance method, nationality is prototype chain attribute, home is prototype chain method, universe is static attribute and shuttle is static method.
// Class ES5 // Constructor implementation class function Person(name, age) { // Instance property, which can only be used by an instance, cannot be obtained by the constructor this.name = name; this.age = age; this.star = 'Earth'; // console.log(Person.star); undefined // Instance methods can only be used by instances this.init = function() { console.log(`${this.name} is fighting: ${this.age}!`); }; }; // The prototype chain attribute can be shared by multiple instances. For example, when the prototype chain attribute is a reference value, it is shared. It can only be used by instances Person.prototype.nationality = ['China']; // console.log(Person.nationality); undefined // Prototype chain method Person.prototype.home = function() { console.log(`${this.name}'s home is in ${this.star}`); }; // Static property. The instance cannot be obtained. It can only be used by the constructor itself Person.universe = 'TheMilkyWay'; // console.log(arrow.universe); undefined // Static method Person.shuttle = function() { console.log('shuttling'); }; const arrow = new Person('Arrow', 12); console.log('example----------------------------'); console.log(arrow.name, arrow.age); // Arrow 12 instance properties of the parent class arrow.init(); // Arrow is fighting: 12! Instance method of parent class console.log(arrow.nationality); // ['China '] parent prototype chain attribute arrow.home(); // Arrow's home is in Earth parent prototype chain method console.log(Person.universe); // TheMilkyWay constructor static property Person.shuttle(); // shuttling constructor static method // Arrow 12 // Arrow is fighting: 12! // Arrow's home is in Earth // TheMilkyWay // shuttling const tom = new Person('Tom', 22); console.log('\n Prototype chain attribute sharing----------------------------'); console.log(tom.nationality); // Parent prototype chain attribute, reference type tom.nationality.push('French'); // Attributes on the prototype chain are shared console.log(tom.nationality); console.log(arrow.nationality); // [ 'China' ] // [ 'China', 'French' ] // [ 'China', 'French' ]
3. Implementation of class inheritance in Es5
First of all, it should be pointed out that the inheritance in ES5 mainly adopts composite inheritance (also known as pseudo classical inheritance), and adopts prototype chain + embezzling constructor to realize inheritance. However, in order to better trace the source and understand its root cause, the blogger will first explain the implementation methods, advantages and disadvantages of prototype chain inheritance and stolen constructor inheritance, and then explain combinatorial inheritance. Parent the Person constructor in Section 2.
3.1 prototype chain inheritance
Basic idea: inherit the properties and methods of multiple reference types through prototypes. The principle of implementation needs to review the relationship between constructors, prototypes and instances: each constructor has a prototype object, the prototype has an attribute that refers back to the constructor, and the instance has an internal pointer to the prototype. Imagine if the prototype is an instance of another type? Does that mean that the prototype itself has an internal pointer to another prototype, and accordingly, the other prototype also has a pointer to its corresponding constructor. In this way, a prototype chain is constructed between the instance and the prototype. This passage needs a good understanding.
// ES5 inheritance // 1. Prototype chain inheritance can inherit the instance properties and methods of the parent class and the properties and methods on the prototype chain of the parent class, but the subclass cannot pass parameters to the constructor of the parent class when instantiating function Man_1() {}; Man_1.prototype = new Person(); const bullet_1 = new Man_1(); console.log('bullet-1-----------------------------------------'); console.log(bullet_1.star); // Earth can get the instance property of the parent class console.log(bullet_1.nationality); // ['China '] you can get the attributes on the parent prototype chain bullet_1.init(); // undefined is fighting: undefined! You can use the instance method of the parent class, but no parameters are passed bullet_1.home(); // undefined's home is in Earth can use the method on the prototype chain of the parent class, but there is no parameter passing // Earth // [ 'China' ] // undefined is fighting: undefined! // undefined's home is in Earth
Advantages and disadvantages of prototype chain inheritance:
-
Advantages: it can inherit the instance properties and methods of the parent class and the properties and methods on the prototype chain of the parent class
-
Disadvantages: subclasses cannot pass parameters to the constructor of the parent class when instantiating
3.2 stealing constructor to implement inheritance
The basic idea of embezzling the constructor function is to inherit (constructor stealing, also known as object camouflage or classic inheritance): call the parent class constructor in the subclass constructor. Because, after all, a function is a simple object that executes code in a specific context, you can use the apply() and call() methods to execute the constructor with the newly created object as the context.
// 2. Stealing constructor (also known as object camouflage or classic inheritance) inheritance. Subclasses can inherit the instance properties and methods of the parent class. Meanwhile, subclasses can pass parameters to the constructor of the parent class when instantiating, but cannot inherit the properties and methods on the prototype chain of the parent class function Man_2(...args) { Person.call(this, ...args) }; const bullet_2 = new Man_2('bullet', 32); console.log('\nbullet-2-----------------------------------------'); console.log(bullet_2.name, bullet_2.age, bullet_2.star); // bullet 32 Earth can obtain the instance property of the parent class and pass parameters bullet_2.init(); // bullet is fighting: 32! You can get the instance method of the parent class and pass parameters console.log(bullet_2.nationality); // undefined cannot get the attributes on the parent prototype chain, nor can natural methods // bullet 32 Earth // bullet is fighting: 32! // undefined
Advantages and disadvantages of stealing Structure Inheritance:
-
Advantages: subclasses can inherit the instance properties and methods of the parent class. At the same time, subclasses can pass parameters to the constructor of the parent class when instantiating
-
Disadvantage: cannot inherit properties and methods on the parent prototype chain
3.3 combinatorial inheritance (prototype chain + stealing constructor)
Combinatorial inheritance (also known as pseudo classical inheritance) combines the prototype chain and stealing constructor, and integrates the advantages of both. The basic idea is to use the prototype chain to inherit the properties and methods on the prototype chain, and inherit the instance properties by stealing the constructor. In this way, the method can be defined on the prototype to realize reuse, and each instance can have its own properties.
// 3. Prototype chain + stealing constructor to realize composite inheritance (also known as pseudo classical inheritance) // The prototype chain inherits the properties and methods on the prototype chain // Embezzle constructor to inherit the passing of instance properties and parameters function Man(...args) { Person.call(this, ...args) }; Man.prototype = new Person(); const love = new Man('love', 18); console.log('\nlove-----------------------------------------'); console.log(love.name, love.age, love.star); // love 18 Earth can get the instance properties and parameter passing of the parent class love.init(); // love is fighting: 18! You can get the instance method and parameter passing of the parent class console.log(love.nationality); // ['China'] can get the prototype chain attribute of the parent class love.home(); // love's home is in Earth to get the prototype chain method of the parent class
4. Summary
Sometimes interesting, sometimes irritable, learning such knowledge needs to calm down, gnaw bit by bit and think over and over again.
P238 in Red Treasure Book 4 introduces inheritance in detail. More details can be found in the book.
Attach the complete code here!
// Class ES5 // Constructor implementation class function Person(name, age) { // Instance property, which can only be used by an instance, cannot be obtained by the constructor this.name = name; this.age = age; this.star = 'Earth'; // console.log(Person.star); undefined // Instance methods can only be used by instances this.init = function() { console.log(`${this.name} is fighting: ${this.age}!`); }; }; // Prototype chain attribute, which can be shared by multiple instances and can only be used by instances Person.prototype.nationality = ['China']; // console.log(Person.nationality); undefined // Prototype chain method Person.prototype.home = function() { console.log(`${this.name}'s home is in ${this.star}`); }; // Static property. The instance cannot be obtained. It can only be used by the constructor itself Person.universe = 'TheMilkyWay'; // console.log(arrow.universe); undefined // Static method Person.shuttle = function() { console.log('shuttling'); }; // ES5 inheritance // 1. Prototype chain inheritance can inherit the instance properties and methods of the parent class and the properties and methods on the prototype chain of the parent class, but the subclass cannot pass parameters to the constructor of the parent class when instantiating function Man_1() {}; Man_1.prototype = new Person(); const bullet_1 = new Man_1(); console.log('bullet-1-----------------------------------------'); console.log(bullet_1.star); // Earth can get the instance property of the parent class console.log(bullet_1.nationality); // ['China '] you can get the attributes on the parent prototype chain bullet_1.init(); // undefined is fighting: undefined! You can use the instance method of the parent class, but no parameters are passed bullet_1.home(); // undefined's home is in Earth can use the method on the prototype chain of the parent class, but there is no parameter passing // Earth // [ 'China' ] // undefined is fighting: undefined! // undefined's home is in Earth // 2. Stealing constructor (also known as object camouflage or classic inheritance) inheritance. Subclasses can inherit the instance properties and methods of the parent class. Meanwhile, subclasses can pass parameters to the constructor of the parent class when instantiating, but cannot inherit the properties and methods on the prototype chain of the parent class function Man_2(...args) { Person.call(this, ...args) }; const bullet_2 = new Man_2('bullet', 32); console.log('\nbullet-2-----------------------------------------'); console.log(bullet_2.name, bullet_2.age, bullet_2.star); // bullet 32 Earth can obtain the instance property of the parent class and pass parameters bullet_2.init(); // bullet is fighting: 32! You can get the instance method of the parent class and pass parameters console.log(bullet_2.nationality); // undefined cannot get the attributes on the parent prototype chain, nor can natural methods // bullet 32 Earth // bullet is fighting: 32! // undefined // 3. Prototype chain + stealing constructor to realize composite inheritance (also known as pseudo classical inheritance) // The prototype chain inherits the properties and methods on the prototype chain // Embezzle constructor to inherit the passing of instance properties and parameters function Man(...args) { Person.call(this, ...args) }; Man.prototype = new Person(); const love = new Man('love', 18); console.log('\nlove-----------------------------------------'); console.log(love.name, love.age, love.star); // love 18 Earth can get the instance properties and parameter passing of the parent class love.init(); // love is fighting: 18! You can get the instance method and parameter passing of the parent class console.log(love.nationality); // ['China'] can get the prototype chain attribute of the parent class love.home(); // love's home is in Earth to get the prototype chain method of the parent class