Skip to content

Instantly share code, notes, and snippets.

@karanth
Last active January 2, 2016 20:39
Show Gist options
  • Save karanth/8358038 to your computer and use it in GitHub Desktop.
Save karanth/8358038 to your computer and use it in GitHub Desktop.
Notes on Javascript OOP - Part 3 - Inheritance

Internals - Inheritance

Inheritance in Javascript is called prototypal inheritance. It is different than the classical inheritance that we see in OOP languages like Java or C++. Inheritance in OOP is required for 2 reasons,

  • Automatic casting of references for objects of the same class family. If inheritance was not allowed, it would require explicit casting of object references. This is particularly useful in a strongly typed language setting. However, it is not relevant in the setting of Javascript, a loosely-typed language where casting is not necessary.
  • Code reusability is another very important reason for inheritance in OOP. Inheritance allows us to group methods at appropriate levels of abstraction and not worry about duplicating the code at a different level of abstraction. For example, if the Car class has a certain function, let us say a brake method to decelerate the car, a Sedan class, a sub-class of the Car class need not duplicate the same method.

When compared to classical inheritance, prototypal inheritance has the following advantages,

  • Prototypal inheritance allows a programmer to extend a class dynamically (Class Augmentation). Adding or deleting methods from a class can be done on the fly and all present and future objects will have those changes.
  • It allows a programmer to create slightly different variants of an instantiated object (Object Augmentation). Similar to how properties and methods can be added or removed from a class, they can be added or removed from an object as well. In a language supporting classical inheritance it requires defining a new class to support this particular object.
  • It facilitates multiple inheritance in a very flexible way by allowing one to inherit necessary methods and properties from different parent classes (Swiss Inheritance?) unlike all or none pattern of classical inheritance.

When an object is created using the constructor method in Javascript, all the necessary properties and methods are created. Every object created from the same constructor will contain all the properties and methods of the other objects (Of course, property values may be different, but methods are replicated). This is redundancy that can be avoided leading to lesser memory pressures, especially with methods. Generally, methods are common for a particular class of objects and it doesn't make sense to replicate them everytime an object is created. The same holds good for constant properties as well. Every Javascript object has a property called prototype. This property holds all shared properties and methods for objects constructed by that particular function.

When a property or method is called on an object, first instance methods/properties are looked up for a match. If there is no-match, the prototype property of the child searched for a match, then its parent and so on. This method of matching helps in simulating overriding behavior of classical inheritance by defining methods/properties of the same name downstream in the hierarchy. In the code below, the Car constructor is extended to be a Sedan. The key here is to set the child class prototype object to that of the parent constructor.

 function Car(numberOfCylinders, carType){

  //Private member variables. They can be set only during object construction.
  var type = carType;
  var cylinders = numberOfCylinders;
  var volumeOfCylinder = 250;

  this.getCC = function(){
                return cylinders * volumeOfCylinder * this.factor;
              };

  //Public properties. These properties can be set from outside the object.
  this.color = 'White';
  this.sunRoof = false;
  this.factor = 1;


}


  //Shared method
Car.prototype.start = function(){
                  console.log('The car with ' + this.getCC() + ' engine and color ' + this.color + ' is starting...');
              }

Car.prototype.stop = function(){
                  console.log('The car has stopped');
                  }



function Sedan(numberOfCylinder, carType){
    //Calling the base constructor. All private vars and other properties are initialized. 
    Car.call(this, numberOfCylinder, carType);
    //Instance method. The first in the method resolution chain.
    this.start = function(){
                     console.log('The Sedan with ' + this.getCC() + ' engine and color ' + this.color + ' is starting...');
                 }
}


//Set the inheritance - this is the magic line
Sedan.prototype = new Car();
Sedan.prototype.constructor = Sedan;



var honda = new Sedan(4, 'sedan');
honda.start();
honda.stop();

Notes on the example,

  • The start and the stop methods are now part of every Car object instance, but a single copy of the function definition is present. This is because it has been moved to the prototype property to enable sharing.
  • The inheritance is done by initializing the prototype property of the Sedan function to the parent, in this case it is the Car function.
  • It is generally good practice to set the prototype's constructor property to the child constructor.
  • To demonstrate how a property/method name is resolved, the start method has been overridden in the Sedan prototype by making it an instance method. This will make the Javascript runtime to first look for it in the instance, before moving up the prototype chain. But, every Sedan instance is going to have its own copy of the start method. A better practice would be to move it to the Sedan.prototype property especially if it is common functionality among all Sedans.
  • The base object (Car) constructor is being called using the call method. An alternative is to actually assign all the properties of the base object. Using the call method keeps the code cleaner and in most cases reduces the risk of base object property omissions. It will also prepare private variables and closures that are present in the base object.
  • The use of the prototype property demonstrates class augmentation. Adding a property/method to the prototype property, makes all the instances instantly have that particular property/method. Object augmentation is already very natural in Javascript. Instead of defining the start method in the constructor function, it could've been defined after the honda object instance was created.
  • Multiple inheritance is also quite straightforward. The properties from one object class come in via setting the prototype property and the other object class can be inherited by enumerating the properties/methods and adding it to the child object class prototype explicitly. Of course, properties of both object classes can be enumerated and added. The latter gives the programmer the flexibility to pick and choose properties that need to be inherited (Swiss Inheritance?).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment