Detailed Explanation of Inheritance Methods in JavaScript
Detailed Explanation of Inheritance Methods in JavaScript
Description
Inheritance in JavaScript refers to an object's ability to access the properties and methods of another object. Since JavaScript is prototype-based, inheritance is primarily achieved through the prototype chain. ES5 has several classic inheritance patterns, while ES6 introduced the class syntactic sugar. Understanding different inheritance approaches helps in writing maintainable object-oriented code.
Step-by-Step Explanation
-
Prototype Chain Inheritance
- Principle: Set the child class's prototype object to an instance of the parent class, thereby accessing parent properties via the prototype chain.
- Example:
function Parent() { this.name = 'Parent'; } Parent.prototype.sayName = function() { console.log(this.name); }; function Child() {} Child.prototype = new Parent(); // Core: Prototype points to parent instance const child = new Child(); child.sayName(); // Outputs "Parent", accessed via prototype chain - Drawbacks:
- All child instances share parent's reference-type properties (e.g., arrays), modifications affect each other.
- Cannot pass arguments to the parent constructor.
-
Constructor Inheritance
- Principle: Call the parent constructor within the child constructor, using
callorapplyto change thethiscontext. - Example:
function Parent(name) { this.name = name; this.colors = ['red']; } function Child(name) { Parent.call(this, name); // Core: Execute parent constructor in child context } const child1 = new Child('Child1'); child1.colors.push('blue'); console.log(child1.colors); // ['red', 'blue'] (independent property) const child2 = new Child('Child2'); console.log(child2.colors); // ['red'] (unaffected by child1) - Drawback: Cannot inherit methods from the parent's prototype.
- Principle: Call the parent constructor within the child constructor, using
-
Combination Inheritance
- Principle: Combines prototype chain and constructor inheritance to inherit prototype methods while keeping instance properties independent.
- Steps:
- Use constructor inheritance for parent instance properties.
- Use prototype chain inheritance for parent prototype methods.
- Example:
function Parent(name) { this.name = name; } Parent.prototype.sayName = function() { console.log(this.name); }; function Child(name, age) { Parent.call(this, name); // 1st Parent call: Inherit instance properties this.age = age; } Child.prototype = new Parent(); // 2nd Parent call: Inherit prototype methods Child.prototype.constructor = Child; // Fix constructor reference const child = new Child('Tom', 10); child.sayName(); // "Tom" - Drawback: Parent constructor is called twice (performance waste).
-
Prototypal Inheritance
- Principle: Create a new object based on an existing object; ES5's
Object.create()implements this pattern. - Example:
const parent = { name: 'Parent', friends: ['Alice'] }; const child1 = Object.create(parent); // Create object with parent as prototype child1.name = 'Child1'; child1.friends.push('Bob'); const child2 = Object.create(parent); console.log(child2.friends); // ['Alice', 'Bob'] (shared reference property) - Use Case: Simple object inheritance, no constructor needed.
- Principle: Create a new object based on an existing object; ES5's
-
Parasitic Combination Inheritance
- Principle: Optimizes combination inheritance by avoiding two parent constructor calls. Uses an intermediate object to link child and parent prototypes.
- Steps:
- Use constructor inheritance for instance properties.
- Create an empty function and set its prototype to the parent prototype.
- Set the child's prototype to an instance of the empty function.
- Example:
function inheritPrototype(Child, Parent) { const F = function() {}; // Empty constructor F.prototype = Parent.prototype; Child.prototype = new F(); // Inherit prototype chain only, no parent constructor call Child.prototype.constructor = Child; } function Parent(name) { this.name = name; } function Child(name, age) { Parent.call(this, name); // Only 1 constructor call this.age = age; } inheritPrototype(Child, Parent); - Advantage: The most perfect inheritance method before ES6.
-
ES6 Class Inheritance
- Principle:
classandextendsare syntactic sugar, still based on prototype chain under the hood;supercalls the parent. - Example:
class Parent { constructor(name) { this.name = name; } sayName() { console.log(this.name); } } class Child extends Parent { constructor(name, age) { super(name); // Equivalent to Parent.call(this, name) this.age = age; } } const child = new Child('Tom', 10); child.sayName(); // "Tom" - Note: Must call
super()before usingthis.
- Principle:
Summary
- Prefer ES6 class inheritance for its simplicity and lack of historical baggage.
- Understanding the underlying prototype chain mechanism aids in debugging complex inheritance relationships.
- Parasitic combination inheritance is key to understanding how class inheritance works under the hood.