Implementation method of JS inheritance and detailed explanation of its advantages and disadvantages

  • 2021-08-10 06:35:11
  • OfStack

Preface

JS is an object-oriented weakly typed language, and inheritance is one of its most powerful features. So how do you implement inheritance in JS? Let's wait and see.

Implementation of JS Inheritance

Since we want to implement inheritance, we must first have a parent class with the following code:


//  Definition 1 Animals 
function Animal (name) {
 //  Attribute 
 this.name = name || 'Animal';
 //  Instance method 
 this.sleep = function(){
  console.log(this.name + ' Sleeping! ');
 }
}
//  Prototype method 
Animal.prototype.eat = function(food) {
 console.log(this.name + ' Eating: ' + food);
};

1. Prototype chain inheritance

Core: Take the instance of the parent class as the prototype of the subclass


function Cat(){ 
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

//   Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true

Features:

Very pure inheritance relationship, the instance is an instance of a subclass and an instance of a parent class A new prototype method/prototype attribute is added to the parent class, which can be accessed by all subclasses Simple and easy to implement

Disadvantages:

To add properties and methods to a subclass, it must be executed after a statement like new Animal () and cannot be placed in the constructor

Unable to implement multiple inheritance

All attributes from the prototype object are shared by all instances (reference attributes from the prototype object are shared by all instances) (see Appendix Code: Example 1 for details)
Cannot pass parameters to parent class constructor when creating subclass instance

Recommended index: ★ ★ (3 and 4 fatal defects)

2017-8-17 10:21:43 Added: Thanks to MMHS for pointing out. Wrong description in Disadvantage 1: You can add instance attributes to the Cat instance in the Cat constructor. If you want to add prototype properties and methods, they must be executed after statements like new Animal ().

2018-9-10 00:03:45 Added: Thanks for pointing out IRVING_J. The description in Disadvantage 3 is insufficient. Corrected to: All attributes from prototype objects are shared by all instances.

2. Construct inheritance

Core: Using the constructor of the parent class to enhance the subclass instance is equivalent to copying the instance attribute of the parent class to the subclass (no prototype is used)


function Cat(name){
 Animal.call(this);
 this.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

Features:

It solves the problem that subclass instances share the reference attributes of parent class in 1 When you create a subclass instance, you can pass parameters to the parent class Multiple inheritance can be implemented (call multiple parent class objects)

Disadvantages:

An instance is not an instance of a parent class, but an instance of a subclass You can only inherit instance properties and methods of the parent class, not prototype properties/methods Function reuse cannot be realized, and each subclass has a copy of the parent class instance function, which affects performance

Recommended index: ★ ★ (Disadvantage 3)

3. Instance inheritance

Core: Add a new feature to the parent class instance and return it as a subclass instance


function Cat(name){
 var instance = new Animal();
 instance.name = name || 'Tom';
 return instance;
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false

Features:

There is no restriction on how to call, whether it is an new subclass () or a subclass (), the returned object has the same effect

Disadvantages:

An instance is an instance of a parent class, not a subclass Multiple inheritance is not supported

Recommended index: ★ ★

4. Copy inheritance


function Cat(name){
 var animal = new Animal();
 for(var p in animal){
  Cat.prototype[p] = animal[p];
 }
 Cat.prototype.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

Features:

Support multiple inheritance

Disadvantages:

Low efficiency and high memory consumption (because you want to copy the attributes of the parent class) Unable to get non-enumerable method of parent class (non-enumerable method, not accessible using for in)

Recommended index: ★ (Disadvantage 1)

5. Combination inheritance

Core: Inheriting the attributes of the parent class and retaining the advantages of passing parameters by calling the parent class construction, and then realizing function reuse by taking the parent class instance as the subclass prototype


function Cat(name){
 Animal.call(this);
 this.name = name || 'Tom';
}
Cat.prototype = new Animal();

//  Thank you  @ There's always something new to study c  Combination inheritance also needs to be fixed as pointed by the constructor. 

Cat.prototype.constructor = Cat;

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // true

Features:

It remedies the defect of Mode 2, and can inherit instance attributes/methods as well as prototype attributes/methods Is an instance of both a subclass and a parent class There is no reference property sharing problem Transmissible parameter Function reusability

Disadvantages:

The parent constructor is called twice, and two instances are generated (the subclass instance masks the one on the subclass prototype)
Recommended index: ★ ★ ★ (only 1 extra point of memory is consumed)

6. Parasitic combination inheritance

Core: Cut off the instance attribute of the parent class by parasitic way, so that when calling the construction of the parent class twice, the instance method/attribute will not be initialized twice, thus avoiding the disadvantage of combined inheritance


function Cat(name){
 Animal.call(this);
 this.name = name || 'Tom';
}
(function(){
 //  Create 1 Classes without instance methods 
 var Super = function(){};
 Super.prototype = Animal.prototype;
 // Prototypes with instances as subclasses 
 Cat.prototype = new Super();
})();

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true

 Thank you  @bluedrink  Remind that this implementation does not fix constructor . 

Cat.prototype.constructor = Cat; //  Need to fix the following constructor 

Thanks for the @ bluedrink reminder, this implementation does not fix constructor.

Cat. prototype. constructor = Cat; //Need to fix the lower constructor

Features:

Be called perfect

Disadvantages:

The implementation is more complicated

Recommended index: ★ ★ ★ (complicated implementation, deduct 1 star)

Appendix code:

Example 1:


function Animal (name) {
 //  Attribute 
 this.name = name || 'Animal';
 //  Instance method 
 this.sleep = function(){
  console.log(this.name + ' Sleeping! ');
 }
 // Instance reference property 
 this.features = [];
}
function Cat(name){
}
Cat.prototype = new Animal();

var tom = new Cat('Tom');
var kissy = new Cat('Kissy');

console.log(tom.name); // "Animal"
console.log(kissy.name); // "Animal"
console.log(tom.features); // []
console.log(kissy.features); // []

tom.name = 'Tom-New Name';
tom.features.push('eat');

// Changes to the parent class instance value type members do not affect 
console.log(tom.name); // "Tom-New Name"
console.log(kissy.name); // "Animal"
// Changes that refer to type members for parent class instances will affect other subclass instances by 
console.log(tom.features); // ['eat']
console.log(kissy.features); // ['eat']

Cause analysis:

Key point: Attribute lookup process

To execute tom. features. push, first look for the instance property of the tom object (not found),

Then look for it in the prototype object, which is an instance of Animal. If it is found, it will be directly in the object's
features property.

In console. log (kissy. features); When. Same as above, there is no kissy instance, so look for it on the prototype.

If it happens to exist on the prototype, it will be returned directly, but notice that the features attribute value in this prototype object has changed.


Related articles: