Classical Inheritance in JavaScript

© 7 April, 2013, Martin Rinehart

Prerequisite: solid JavaScript object programming (see OP and OOP, above).
This page defines "inheritance" as it is used in class-based OOP and then shows how the same definition fits JavaScript, with or without a prototype chain.

Class-Based (Classical) OOP

Object-oriented programming (OOP) was developed in Simula and Smalltalk. Seeing its benefits, Bjarne Stroustrup (at Bell Labs) wrote a C pre-compiler to bring some of the benefits of objects to his own work. He called his language additions "C++". It caught on like wildfire among C programmmers.

C++ not only spread within the C community, it spawned other languages, such as Java. Although Stroustrup omitted a great deal of Smalltalk's object model, C++'s OOP became a standard. It is correctly called "class-based" OOP and commonly called "classical OOP."

C++'s class-based inheritance is the model for a family of languages, including Java and C#. Class-based OOP is also used in many modern languages, such as Ruby and Python (though they both extend the model significantly). It is also used in OOP retrofits to many earlier languages, such as Microsoft's Visual Basic. Most important languages (Fortran and Cobol, for examples) have adopted OOP extensions and most have adopted the C++'s class-based OOP.

Class-Based (Classical) Inheritance

In class-based OOP we can start with one class. For clarity of the discussion, let's say we have a class named Base. Our class will define a number of properties for the object instances of Base. The properties will have data values (aka "state") attached to each object instance and instance methods, stored in the class, that manipulate these data.

It happens many times that a group of objects is required that is a superset of the Base class, having additional properties and methods, commonly dealing with one more specific type of Base. Assume that we have such a group and model it in a class we call Extend. In whatever language's syntax, we will say that Extend "inherits" from Base. This means that all the properties of Base are properties of instances of Extend.

If Extend has its own methods, the code for these is stored in the Extend class. (This is a logical view, not a prescription for a specific implementation.) If object instances of Extend can take advantage of methods of the Base class, for example base_method(), we can say extend.base_method() as if base_method() were part of the Extend class.

It is important to have a clear picture of how this works. There are two separate sets of methods. The set of Base methods are stored with the Base class and instances of the class may call them. A second set of methods is stored with Extend. These are not available to instances of the Base class. Instances of Extend may call methods in either set if Extends inherits from Base.

JavaScript Prototypal Inheritance

We can do exactly the same thing in JavaScript. This means that we can use the same terminology dealing with both models.

In JavaScript, every object has a "prototype" (excepting the host objects that interface to the DOM). When code requires an object property (data or method) JavaScript will look for that property, attached to the object. In obj.prop_name it will look for a property named "prop_name" attached to the obj object. If no property of the given name is attached to the object, it will look to see if such a property is part of the object's prototype.

The standard prototype object for JavaScript is the object's constructor's "prototype" property. If base is created by calling base = new Base(...) the property Base.prototype is the prototype object for base (and all other instances of the Base constructor). This lets all instances of Base run the code stored in Base.prototype just as class-based instances find their code stored in the class. This is not called "inheritance."

In JavaScript you also want instances of the Extend class to access methods found in Extend.prototype or, for inheritance, in Base.prototype. The "prototype chain" provides this service very easily. If a property is not found in an object, the object's prototype is asked to provide the property. These prototypes are themselves objects, so that lookup can be recursive: if a prototype object does not have a requested property, its own prototype is then asked for the property. (This chain terminates at Object.prototype, which is the start of the chain and has no prototype.)

If extend does not, itself, have a prop_name property, it's prototype is searched. It's prototype is Extend.prototype. If Extend.prototype does not have a prop_name, the prototype of Extend.prototype is searched. If we have arranged for the prototype of Extend.prototype to be Base.prototype then extend.prop_name could be found there, just as in class-based OOP a property of Base will appear to be a property of an instance of Extend.

Fortunately, it is simple to have Base.prototype become the prototype of Extend.prototype:

Extend.prototype = new Base(...);

It is not obvious how assigning a default object instance of Base to Extend.prototype achieves the desired result, however. Because of this fundamental lack of clarity, we do not recommend the above statement. It demonstrates pure "prototypal inheritance" but this is only one of many ways to achieve inheritance in JavaScript.

JavaScript Inheritance

Inheritance allows the instances of one class, such as Extend, to access the properties of another class, such as Base, as if they were part of the extending class. We can enjoy this capability by using the JavaScript prototype chain. But this is not a particularly good way to get the capability.

Using the prototype chain to search for methods requires a lookup at every step back through the chain. If inheritance chains are long, this could become a performance issue. In practice, JavaScript chains are much shorter than inheritance chains in class-based languages, but there is no reason to search past the object instance's immediate prototype while continuing to enjoy the ability to "inherit" from base classes. The simplest way to get inheritance without a prototype chain is this:

Extend.prototype = Base.prototype;

That makes all of the methods of Base immediately available as methods of Extend. It is also self-exlanatory: the two prototype refences are now pointing to a single object.

The disadvantage is that all methods are the same. You almost never want the toString() of Base and Extend to return an identical result, as one example where the methods should not be the same. Let's assume you have a library routine that makes a shallow copy¹ of an object. This formulation avoids the problem:

Extend.prototype =
    shallow_copy( Base.prototype );

Note that this copies references, not code. Note also that this copying is done once per class, not for each instance. With the shallow copy you can then modify Extend.prototype without changing Base.prototype:

Extend.prototype.toString = function ...

In class-based OOP, this ability to "override" methods is considered key. The extending class may add its own methods to the base and it may also provide methods that are used in lieu of the ones (having identical names) in the base class. In the above example, extend.toString() calls a separate method from the one called by base.toString().

JavaScript's flexibility provides numerous other ways of achieving the desired result. "Mix-in" classes, for example, let you add capabilities without inheritance. (Java programmers who depend on interfaces in lieu of elaborate inheritance chains will find these invaluable.)


¹A shallow copy routine probably isn't in your libraries. It's easier to type one than to look it up! JavaScript's object programming would look like this:

function shallow_copy( obj ) {
    var ret = {};

    for ( var prop_name in obj ) {
        ret[ prop_name ] = obj[ prop_name ];
    }

    return ret;
}

You'll need to add a hasOwnProperty() filter if you allow modifications to Object.prototype. (This is another reason why we don't allow it.)


Feedback: MartinRinehart at gmail dot com

# # #