1. Prototype Chain Inheritance
Core: Prototype a subclass with an instance of the parent class
First, you need to know the relationship between the constructor, the prototype, and the instance: the constructor has a prototype object, the prototype object contains a pointer to the constructor, and the instance contains a pointer to the prototype object.
function Father(){ this.name = 'The name of the parent class'; } Father.prototype.getFatherName = function(){ console.log('Method of parent class'); } function Son(){ this.name = 'Name of subclass'; } // If the prototype object with Son has methods or attributes at this time, Son.prototype = new Father() below, the methods and attributes on the prototype will be lost due to the prototype redirection Son.prototype.getAge = function(){ console.log('Age of subcategories') } Son.prototype = new Father(); // Core: Create an instance of the parent class and assign it to the prototype of the child class Son.prototype.getSonName = function(){ console.log('Methods of subclasses'); } var son = new Son(); son.getFatherName(); // Method of parent class Son.prototype.__proto__.getFatherName = function(){ // Disadvantage: If there are multiple instances of their parent prototype, they will interact console.log('The method by which a subclass alters its parent'); } son.getFatherName(); // The method by which a subclass alters its parent
Disadvantages:
- Attributes declared by the parent class using this (private and public) are shared by all instances and affect each other's operation on reference type data across multiple instances.
- When you create a subclass instance, you cannot pass parameters to the parent constructor.
2. Borrowing Constructor Inheritance (call)
Core: Use parent class constructors to enhance subclass instances, i.e. copy the instance properties of the parent class to the subclass
function Father(name, age){ this.name = name; this.age = age; } Father.prototype.getFatherName = function(){ console.log('Method of parent class'); } function Son(name, age, job){ Father.call(this,name, age); // Inherited from Father this.job = job; } var son = new Son('jacky', 22, 'Front End Development'); //son.getFatherName(); // Uncaught TypeError: son.getFatherName is not a function
Advantage:
- Parameters can be passed to the parent class, and this solves the problem in prototype chain inheritance where attributes declared using this are shared across all instances.
Disadvantages:
- Only attributes/methods declared by the parent class through this can be inherited, not attributes/methods on the parent class prototype.
- The parent function is executed each time a subclass is instantiated, redeclaring the methods defined in the parent class this, so the parent methods cannot be reused.
3. Combinatorial Inheritance
Core: Combine the above two methods, use prototype chain to inherit prototype attributes and methods, and use constructor technology to inherit instance attributes.
function Father(name, age){ this.name = name; this.age = age; this.sex = 'man'; } Father.prototype.getFatherName = function(){ console.log('Method of parent class') } function Son(name, age, job){ Father.call(this,name,age); // Second call: when creating an instance of a subtype this.job = job; } Son.prototype = new Father(); // First invocation: when prototyping an instance of a subtype Son.prototype.constructor = Son; // prototype constructor refers back to itself var son = new Son('jacky', 22, 'Front End Development'); son.getFatherName(); console.log(son)
Advantage:
- You can inherit properties from the parent prototype, pass them around, and reuse them.
- The constructor properties introduced by each new subclass object instance are private.
Disadvantages:
- Two calls to the parent function (new fatherFn() and fatherFn.call(this)) result in a performance penalty.
- When an instance object is created using a subclass, there are two identical properties/methods in its prototype.
Expand:
The role of constructor
Returns a reference to the Object constructor that created the instance object.
When we only have instance objects without references to constructors:
In some scenarios, we import and export instance objects in multiple rounds, and we don't know from which function the instance was constructed or which constructor to trace the instance.(It mainly prevents errors in situations where you explicitly use a constructor.For example, I don't know which function instances are instantiated from, but I would like to have one clone, and that would be the case - >instance.constructor)
At this point you can get a reference to the constructor from the constructor property of the instance object
let instance = new sonFn() // Instantiate Subclass export instance; // Multiple import + export rounds make sonFn tracing cumbersome or do not want to reintroduce sonFn in the file let fn = instance.constructor
So each time you override the prototype of a function, you should fix the constructor's pointing to keep the read constructor pointing consistent
4. Prototype Inheritance (Object.create())
Core: By using an empty object as a mediator, assigning an object directly to the prototype of the empty object constructor, and then returning the call to the function, the function becomes an instance or object that can add properties at will.
/* Object.create() Implementation principle */ // cloneObject() performs a shallow copy of the object passed in, pointing the prototype of constructor F directly at the incoming object. function cloneObject(obj){ function F(){} F.prototype = obj; // prototype that takes an incoming obj object as an empty function return new F(); // The prototype of this object is the inherited object. Look through the prototype chain to get the properties of the inherited object } var father = { name: 'jacky', age: 22, courses: ['Front end'] } // var son1 = Object.create(father); same //effect var son1 = cloneObject(father); son1.courses.push('back-end'); var son2 = cloneObject(father); son2.courses.push('Full Stack'); console.log(father.courses); // ["Front End", "Back End", "Full Stack"]
Advantage:
Derive new objects from existing objects without creating custom types
Disadvantages:
Same as prototype chain inheritance.Multiple instances share attributes of inherited objects, possibly with tampering, and cannot be passed along.
5. Parasitic Inheritance
Core: On the basis of prototype inheritance, a function is created to encapsulate the inheritance process only, which enhances the object in some form internally (some new methods and properties are added), and returns the object.
Use scenarios: Enhancements made specifically for objects in a fixed way.
function createAnother(obj){ var clone = Object.create(obj); clone.skill = function(){ // Enhance this object in some way console.log('run'); }; return clone; } var animal = { eat: 'food', drink: 'water' } var dog = createAnother(animal); dog.skill();
Advantage: Custom types are not created, as only shells are nested to add specific attributes/methods to return objects for the purpose of enhancing objects
Disadvantages:
Same as Prototype Inheritance: A prototype chain inherits reference type attributes from multiple instances pointing to the same, possibly with tampering, and cannot pass parameters
6. Parasitic Combinatorial Inheritance
Core: Implement inheritance with borrowed constructor pass-through parameters and parasitic patterns
- Inherit the properties/methods of the parent this declaration by borrowing a constructor (call)
- Inheriting methods through prototype chains
function Father(name, age){ this.name = name; this.age = age; } Father.prototype.getFatherName = function(){ console.log('Method of parent class') } function Son(name, age, job){ Father.call(this,name,age); // Borrowing Constructive Inheritance: Inheriting the parent class declares properties and methods on attributes of the child class instance through this this.job = job; } // Parasitic Inheritance: Encapsulates the process of prototyping the son.prototype object to inherit the father.prototype, and enhances the incoming object. function inheritPrototype(son,father){ var clone = Object.create(father.prototype); // Prototype inheritance: a shallow copy of the father.prototype object clone.constructor = son; // Enhance objects to compensate for default constructor properties lost by overriding prototypes son.prototype = clone; // Specify the object, assign the newly created object to the prototype of the subclass } inheritPrototype(Son,Father); // Point parent prototype to subclass // New Subclass Prototype Properties Son.prototype.getSonName = function(){ console.log('Methods of subclasses') } var son = new Son('jacky',22,'Front End Development'); console.log(son);
- Parasitic combinatorial inheritance has the following advantages over combinatorial inheritance:
- The parent Father constructor is called only once.Instead of calling the constructor to specify the prototype of a subclass, let Son.prototype access Father.prototype indirectly.
- Avoid creating unnecessary extra attributes on the subclass prototype.
The prototype of the parent class is inherited by the prototype, keeping the prototype chain context unchanged, and instanceof and isPrototypeOf() work as usual.
3. Parasitic combinatorial inheritance is the most mature inheritance method and the most common inheritance method now. It is also the inheritance scheme adopted by many JS libraries.
Disadvantages:
The hard word is to add properties and methods to the subclass prototype, after the inheritPrototype() method
7.ES6 extends inheritance (optimal)
Core: Classes inherit through the extends keyword, which is clear and convenient.class is just a grammatical sugar, and its core idea is still parasitic combinatorial inheritance.
class Father { constructor(name, age) { this.name = name; this.age = age; } skill() { console.log('Parent Skills'); } } class Son extends Father { constructor(name, age, job){ super(name, age); // Call the constructor of the parent class and use the this keyword only after super is called this.job = job; } getInfo() { console.log(this.name, this.age, this.job); } } let son = new Son('jacky',22,'Front End Development'); son.skill(); // Parent Skills son.getInfo(); // Front-end development of jacky 22
- If the subclass does not define a constructor method, this method will be added by default with the following code.That is, any subclass has a constructor method, whether or not it is explicitly defined.
The subclass must call the super method in the constructor method, or an error will be reported when creating a new instance.This is because the child class's own this object must be moulded through the parent class's constructor to get the same instance properties and methods as the parent class, then processed, plus the child class's own instance properties and methods.If you do not call the super method, the subclass will not get the this object.
Differences between ES5 inheritance and ES6 inheritance
- ES5 inheritance essentially creates instance objects of subclasses before adding methods of the parent class to this (Father.call(this)).
- The inheritance of ES6 is to create an instance object of the parent class, this, and then modify this with the constructor of the subclass.
- Since the subclass does not have its own this object, the super() method of the parent class must be called first.