JavaScript, as a prototype-based language, offers multiple ways to implement inheritance. From early prototype chain inheritance to modern ES6 class syntax, JavaScript’s inheritance mechanisms have evolved significantly. Below, I will detail the various inheritance methods in JavaScript and their characteristics.
Prototype Chain Inheritance
Prototype chain inheritance is the most fundamental inheritance method in JavaScript. It works by setting the child class’s prototype to an instance of the parent class.
function Parent() {
this.name = 'Parent';
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child() {
this.childName = 'Child';
}
// Key step: Set Child's prototype to a Parent instance
Child.prototype = new Parent();
const child1 = new Child();
child1.sayName(); // Output: Parent
console.log(child1.colors); // Output: ['red', 'blue', 'green']
// Issue 1: All child instances share parent instance properties
const child2 = new Child();
child1.colors.push('black');
console.log(child2.colors); // Output: ['red', 'blue', 'green', 'black']
// Issue 2: Cannot pass arguments to the parent constructorCharacteristics:
- Simple to implement
- All child instances share parent instance properties (problematic for reference types)
- Cannot pass arguments to the parent constructor
Constructor Inheritance (Classical Inheritance)
Constructor inheritance achieves inheritance by calling the parent constructor within the child constructor using call or apply.
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
// Key step: Call parent constructor
Parent.call(this, name);
this.age = age;
}
const child1 = new Child('Child1', 5);
const child2 = new Child('Child2', 6);
child1.colors.push('black');
console.log(child1.colors); // ['red', 'blue', 'green', 'black']
console.log(child2.colors); // ['red', 'blue', 'green'] - Arrays not shared
// Issue: Cannot inherit parent prototype methods
console.log(child1.sayName); // undefinedCharacteristics:
- Avoids sharing of reference type properties
- Allows passing arguments to the parent constructor
- Cannot inherit methods from the parent prototype
Combination Inheritance (Most Common)
Combination inheritance merges the strengths of prototype chain and constructor inheritance, making it the most commonly used approach.
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
// Inherit instance properties (constructor inheritance)
Parent.call(this, name);
this.age = age;
}
// Inherit prototype methods (prototype chain inheritance)
Child.prototype = new Parent();
Child.prototype.constructor = Child; // Fix constructor reference
Child.prototype.sayAge = function() {
console.log(this.age);
};
const child1 = new Child('Child1', 5);
const child2 = new Child('Child2', 6);
child1.colors.push('black');
console.log(child1.colors); // ['red', 'blue', 'green', 'black']
console.log(child2.colors); // ['red', 'blue', 'green'] - Arrays not shared
child1.sayName(); // Child1
child1.sayAge(); // 5
// Issue: Calls parent constructor twice (once in Child.prototype = new Parent(), once in Parent.call(this, name))Characteristics:
- Combines advantages of prototype chain and constructor inheritance
- Most commonly used inheritance method
- Calls the parent constructor twice (performance issue)
Prototypal Inheritance
Prototypal inheritance is similar to ES5’s Object.create() method, creating a new object based on an existing one.
function object(o) {
function F() {}
F.prototype = o;
return new F();
}
const parent = {
name: 'Parent',
colors: ['red', 'blue', 'green'],
sayName: function() {
console.log(this.name);
}
};
const child1 = object(parent);
child1.name = 'Child1';
child1.sayName(); // Child1
const child2 = object(parent);
child2.colors.push('black');
console.log(child1.colors); // ['red', 'blue', 'green', 'black'] - Shared reference type propertiesCharacteristics:
- Similar to ES5’s
Object.create() - Suitable for scenarios not requiring a separate constructor
- Reference type properties are shared among instances
Parasitic Inheritance
Parasitic inheritance builds on prototypal inheritance by enhancing the created object.
function createAnother(original) {
const clone = object(original); // Create object using prototypal inheritance
clone.sayHi = function() { // Enhance object
console.log('hi');
};
return clone;
}
const parent = {
name: 'Parent',
colors: ['red', 'blue', 'green']
};
const child = createAnother(parent);
child.sayHi(); // hi
child.sayName(); // ParentCharacteristics:
- Enhances objects based on prototypal inheritance
- Suitable for focusing on objects rather than custom types or constructors
- Reference type properties are shared, similar to prototypal inheritance
Parasitic Combination Inheritance (Most Ideal)
Parasitic combination inheritance is the most ideal approach for reference types, avoiding the issue of calling the parent constructor twice.
function inheritPrototype(child, parent) {
const prototype = Object.create(parent.prototype); // Create a copy of parent prototype
prototype.constructor = child; // Fix constructor reference
child.prototype = prototype; // Assign copy to child prototype
}
function Parent(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
Parent.prototype.sayName = function() {
console.log(this.name);
};
function Child(name, age) {
Parent.call(this, name); // Call parent constructor once
this.age = age;
}
// Key step: Parasitic combination inheritance
inheritPrototype(Child, Parent);
Child.prototype.sayAge = function() {
console.log(this.age);
};
const child1 = new Child('Child1', 5);
child1.sayName(); // Child1
child1.sayAge(); // 5
const child2 = new Child('Child2', 6);
console.log(child1.colors); // ['red', 'blue', 'green']
child1.colors.push('black');
console.log(child2.colors); // ['red', 'blue', 'green'] - Not sharedCharacteristics:
- Most ideal inheritance method
- Calls parent constructor only once
- Maintains the prototype chain
- Avoids drawbacks of combination inheritance
ES6 Class Syntax Sugar
ES6 introduced the class keyword, offering a cleaner syntax for object-oriented programming. It is syntactic sugar over prototype-based inheritance but provides clearer, more traditional object-oriented syntax.
class Parent {
constructor(name) {
this.name = name;
this.colors = ['red', 'blue', 'green'];
}
sayName() {
console.log(this.name);
}
}
class Child extends Parent {
constructor(name, age) {
super(name); // Call parent constructor
this.age = age;
}
sayAge() {
console.log(this.age);
}
}
const child1 = new Child('Child1', 5);
child1.sayName(); // Child1
child1.sayAge(); // 5
const child2 = new Child('Child2', 6);
console.log(child1.colors); // ['red', 'blue', 'green']
child1.colors.push('black');
console.log(child2.colors); // ['red', 'blue', 'green'] - Not sharedCharacteristics:
- Syntactic sugar, still based on prototypes
- Clearer syntax, closer to traditional object-oriented languages
- Built-in implementation of parasitic combination inheritance
- Provides
superkeyword to access parent class
Comparison of Inheritance Methods
| Inheritance Method | Advantages | Disadvantages |
|---|---|---|
| Prototype Chain | Simple to implement | Shared reference type properties, cannot pass arguments |
| Constructor | Avoids shared references, supports arguments | Cannot inherit prototype methods |
| Combination | Combines benefits of both | Calls parent constructor twice |
| Prototypal | Simple, based on existing objects | Shared reference type properties |
| Parasitic | Enhances objects | Shared reference type properties |
| Parasitic Combination | Most ideal, calls parent constructor once | Slightly complex implementation |
| ES6 Class | Clean syntax, built-in best practices | Requires ES6 support |



