Menu

Subclasses and Inheritance in JavaScript

A class is a blueprint for something you want to model. You can use a class to create many similar objects. In JavaScript, a class is simply a function (or a constructor) that creates objects where each object created from the constructor function will have the same properties. Additional properties can be added to each object as well. I will be using the following class for the first couple of examples.

In addition, all examples are written using the pseudoclassical instantiation pattern.

var SuperClass = function() {  
  this.prop1 = 50;
};
SuperClass.prototype.superMethod = function() {  
  console.log('This is a super method.');
};

A subclass is a class that inherits all the properties of another class, and at the same time, the subclass can add or modify properties. Therefore, a property can be defined in the parent class and shared with the subclass without having to define that property over again in the subclass. Any failed lookups of properties on the subclass will delegate up the prototype chain of parent classes. This is done by setting the subclass's prototype to point to the prototype chain of its parent.

Before we look at the correct way to create subclasses, let's look at the wrong ways first.

var SubClass = function() {  
  this.prop2 = 100;
};
SubClass.prototype = SuperClass.prototype;  
SubClass.prototype.superMethod = function() {  
  console.log('This is a sub method.');
};

var superObj = new SuperClass();  
superObj.superMethod(); // logs 'This is a sub method.'  
var subObj = new SubClass();  
subObj.superMethod(); // logs 'This is a sub method.'  

In the above example, SubClass's prototype is being assigned to the same object as SuperClass's prototype. If a property is then defined on SubClass.prototype with the same name as a property in SuperClasssuperMethod in the example—then it will override that property in SuperClass, and it would affect all instances of SuperClass. Again, this is an example of how not to create subclasses.

Another common mistake that is still being used is the following:

var SubClass = function() {  
  this.prop2 = 100;
};
SubClass.prototype = new SuperClass();  

Sure, this works. Calling new SuperClass() will create a new object that delegates to SuperClass.prototype. However, using this pattern is not best practice, and there will be times where this will not work. For example, what if we slightly change SuperClass to take in an argument and use it during instantiation?

var SuperClass = function(value) {  
  this.prop1 = value * 2;
};
var SubClass = function() {  
  this.prop2 = 100;
};
SubClass.prototype = new SuperClass();  
var subObj = new SubClass();  
console.log(subObj.prop1); // logs NaN  

Notice that when calling subObj.prop1, it didn't return a number. This breaks our expectation on having a number for the property and may even break functionality in the application.

Even if SuperClass doesn't make use of any arguments, it is still a bad practice to set the prototype as a new instance of the parent class because doing so would create exactly that, a new instance of the parent class. Why create another object when you're not going to interact with it?

So here is the correct way to create subclasses as of ES5.1 (ECMA-262):

var SuperClass = function(value) {  
  this.prop1 = value * 2;
};
SuperClass.prototype.superMethod = function() {  
  console.log('This is a super method.');
};
var SubClass = function(value) {  
  SuperClass.call(this, value);
  this.prop2 = 100;
};
SubClass.prototype = Object.create(SuperClass.prototype);  
SubClass.prototype.constructor = SubClass;  
SubClass.prototype.superMethod = function() {  
  console.log('This is a super method on sub.');
};
SubClass.prototype.subMethod = function() {  
  console.log('This is a sub method.');
};

var superObj = new SuperClass(25);  
var subObj = new SubClass(30);  
console.log(superObj.prop1); // logs 50  
superObj.superMethod(); // logs 'This is a super method.'  
console.log(subObj.prop1); // logs 60  
console.log(subObj.prop2); // logs 100  
subObj.superMethod(); // logs 'This is a super method on sub.'  
subObj.subMethod(); // logs 'This is a sub method.'  

Key takeaways about this approach:

  1. When instantiating a new instance of SubClass, it is not going to instantiate a new instance of SuperClass
  2. subObj.prop1 evaluates correctly because when SubClass creates a new instance, it is invoking the SuperClass function with call() and passing in value
  3. superMethod on SubClass does not override superMethod on SuperClass
  4. A new instance of SuperClass is not created when setting SubClass.prototype but SuperClass's prototype chain is still passed to SubClass's prototype
  5. SubClass.prototype.constructor is set to refer to the SubClass constructor function because otherwise, it would use the same constructor function as SuperClass since that's the constructor in SuperClass.prototype (remember, failed lookups on the subclass will delegate up the prototype chain)

That's it. Look out for ES6. It introduces the class keyword for creating classes.