Blog Series Tags

Emulating Classical Inheritance in Javascript

Javascript isn’t a classical language. It’s prototypal, which is a bit like every object being a class in waiting. You can create graphs of objects where one object inherits from another, but the idea of classes doesn’t really exist.

This can be a drawback, specially with code encapsulation. It becomes difficult to hide private implementation details from public interfaces to the object because Javascript provides no native access modifiers. There is however a number of methods for simulating classical inheritance and encapsulation in Javascript while things nice and clean

We start with function objects. In Javascript, functions are objects and as such can be members of other objects and can have objects in themselves. For example, the following code is valid Javascript.

function outerFunction() {
    var outerVar;
 
    function innerFunction() {
        var innerVar;
    }
 
    innerFunction();
}
 
outerFunction.innerFunction(); // Invalid
outerFunction().innerFunction(); // Invalid

Notice that outerFunction now contains innerFunction. You can’t call innerFunction directly (not yet) because it’s in the private scope of the outerFunction but code inside the outer function can call it. It’s worthwhile noting here that Javascript uses functional scoping instead of bracket scoping. This means to create a new scope you need to create a new function.

So great, functions can have functions inside them. What’s so cool about that, specially if you can’t access the inner function from the outer one. Here’s where closures comes in.

A Closure is when the innerFunction can access the variables of the outerFunction even when it’s called from a different scope. For example, consider the camel counter class below which you pass into a function that does the actual camel spotting.

function camelSpotter(incrementFunction) {
    // ... look for camels
    incrementFunction();
    // ... keep looking.
}
 
function camelCounter() {
    var camels = 0;
 
    function increment() {
        camels++;
    }
 
    camelSpotter(increment());
}

Now when the camelSpotter function calls back, the increment function will still have access to the scope of the outer function which contains the camel count. In otherwords every time increment is called by camelSpotter the variable camels would be incremented, it’s value increasing magically. The underlying script execution engine will keep references to the outer scope as long as there is a reference to increment somewhere.

One of the nice things you can do with closures and function objects is to create your own classes. This allows you to both hide internal state and create classical inheritance. Consider the following example, using the classical animal hierarchy:

var animal = function(paramName) {
    // Private variables and methods
     
    var instance = {},
        name = paramName;
     
    function sayName() {
        console.debug("Hello, my name is " + name);
    }
     
 
    // Public variables and methods
    instance.makeNoise = function() {
        sayName();
        console.debug("I can't make a specific sound...");
    };
 
    return instance;
};

We start by creating a named function called ‘animal’.

var animal = function(paramName) {...}

This function can be called like so:

var instance = animal();

Ah… things are starting to look classical now. Within the animal function we create a new, empty object called ‘instance’ which is a container for all the private and public variables and functions that we wish to imbue to our object.

var instance = {};

Next, whenever we want a private object we add it into the scope of the animal function. Since this scope is protected from the outside, and accessible from the inside by functions within the animal function (due to closure) we are now simulating classical private member variables. The following are private:

var name;
function sayName() {};

Notice we can use the parameters to the animal function to simulate a parameterised constructor. The parameter paramName is passed in through this method. Finally notice that all public functions (and variables if required) are being set on the instance object.

instance.makeNoise = function() {};

Which is then returned to the caller of the animal function. Now a typical use of the animal ‘class’ involves those exposed public methods. Private methods defined within the scope of the animal function itself is not accessible.

var myAnimal = animal();
myAnimal.makeNoise();
 
myAnimal.sayName(); // Can't do

The sayName function does not exist within the instance of the animal returned from the pseudo constructor. The makeNoise function on the other hand does exist and has access to the sayName function because of it’s location within the animal functions closure. A note on the code convention. We use camel case for classes we create this way (‘animal’ instead of ‘Animal’) because Javascript has it’s own speodo-classical new operator and confusing class syntax which requires classes to be written with PascalCase (like ‘Animal’). A side note here that the Javascript new operator syntax does result in classical class hierarchies but prototypal ones, which is why it’s confusing.

Finally lets deal with inheritance. We can create a new dog class like so:

var dog = function(name) {
    var instance = animal();
 
    // Public variables and methods
    instance.makeNoise = function() {
        instance.sayName();
        console.debug('Bowwow!');
    };
 
    instance.bite = function () {
        console.debug('CHOMP!');        
    };
 
    return instance;    
};

Notice that we first create an instance of animal. Then we replace the makeNoise function (effectively overloading it) and add a function of our own (bite). Now someone wanting to create a dog instead of a regular animal would create it like so:

myDog = dog('James');

Which brings us to the end of this episode. There are a couple of weaknesses in this method. For example, it becomes impossible to check if a particular object belongs to a particular class because we are dealing with plain Javascript objects and functions, but that’s just the way life is right now with Javascript.