Home :: Tutorials :: JavaScript

JavaScript's object oriented programming techniques are quite unique. This tutorial is an attempt to introduce you to OOP in JavaScript.

Creating subclasses of a class is what makes object oriented programming so powerful. Unfortunately, JavaScript does not have full built-in support for the style of OOP that you may be accustomed to in Java, C#, or C++.

The biggest limitation that I ran into with JavaScript is the fact that there is no way to call a superclass method that has been overridden by a sub-class. I discovered a trick that provides this functionality. This tutorial will show the technique I use, but first we need to cover a little background on how to create objects and how to setup inheritance in JavaScript.

To start with, you need to create a new JavaScript object to serve as your base class. Creating a new JavaScript object consists of two steps. In the first step, you create a function whose name will be the name of the new JavaScript object that you are creating. This function is typically referred to as a "constructor". In the second step, you create an instance of the object by invoking the "new" operator followed by the function name and any parameters needed to initialize the object. The following code creates a Person function and then creates a Person object using the "new" command.

function Person(first, last) {
    this.first = first;
    this.last  = last;
}

var person = new Person("John", "Dough");

The "new" operator will call your constructor passing in the parameters included with the "new" statement. A special variable called "this" exists within the scope of the constructor. "This" points to the current object instance with which you are working. Using "this" allows you to add and modify properties on the object.

Next, you need to add object methods so that you can start manipulating your new object. It is useful to create a "toString" method for each of your objects so that you can display the object in alert dialogs.

Person.prototype.toString() {
    return this.first + " " + this.last;
}
alert( person.toString() ); // displays "John Dough"
alert( person ); // same result since alert calls toString()

When you create a function, you create a global property whose name matches the function name. In the first line, we are accessing the global "Person" property which is really just our constructor.

Every JavaScript object has a prototype property. This property is what makes OOP possible in JavaScript, but it is a bit unusual if you come from other OO languages. Here's how it works. When you access an object property, the interpreter will look at the current object's properties to see if one by that name exists. If the name does not exist there, then the interpreter looks at the prototype property of the object to see if that object, the one pointed to by the prototype property, has the named property. If there is no property there, then the interpreter looks to see if the prototype property has a prototype property. If it does, then this process continues until either the property is found or until there are no more prototype properties to search.

We decide that we like the Person object, but we need to add more information when that person is an employee. It's time for a subclass of the Person object.

Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
Employee.superclass = Person.prototype;

function Employee(first, last, id) {
    // initialize properties here
}

The first line sets up the prototype inheritance chain. Whenever we lookup a property in an Employee object, if it can't be found, then the interpreter will work its way up the prototype chain. Person will be the first object in the chain because of line one.

The constructor property is used in scripts to determine an object's type. When we redefined the Employee's prototype, we effectively changed the constructor to Person. We need to fix this and that is done in the second line.

The third line is part of the trick that will allow you to call methods in a superclass that are hidden by redefined methods in a subclass. More on that later. Note that I had to use a class property instead of a prototype property. Prototype properties will not work here.

If we try to use our new subclass, we will find that JavaScript does not allow us to call a superclass constructor method. The specific problem that we have here is that we want the Person constructor to set the "first" and "last" properties while the Employee constructor should set the "id" property only. I've adopted an idiom that unfortunately adds code, but it does allow us to effectively call a superclass' constructor. Move the initialization code into a new method and then call that method from the subclass.

function Person(first, last) {
if ( arguments.length > 0 )
    this.init(first, last);
}

Person.prototype.init = function(first, last) {
    this.first = first;
    this.last  = last;
}

Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
Employee.superclass = Person.prototype;

function Employee(first, last, id) {
    if ( arguments.length > 0 ) {
        this.init(first, last);
        this.id = id;
    }
}

You may notice that I've added a check in the constructors to see if any arguments are passed in. When you setup your inheritance chain, you do so by setting the prototype property to newly created object. For the purposes of inherticance, we don't need the object to be initialized. We just want it for its prototype property. We assume the object instance is being used for inheritance when no properties are passed to the constructor. You can view this as a slight optimization (memory usage and execution time), but I've found this to be a practical approach in cases where the object initialization has side-effects. For instance, your object initialization may result in the SVG DOM being altered, but you would not want the DOM to be altered if you are creating the object solely for inheritance.

When you create a new Person object, the Person constructor will pass the two arguments to the object's init method which then initializes the object properties.

When you create a new Employee object, the Employee constructor will call the object's init method. Since no init method is defined by the Employee object, the interpreter starts crawling up the prototype chain. The next object in the chain is the Person object, so the Person init method is called which then initializes the object properties. The last line of the Employee constructor takes care of initializing the "id" property.

We've created a new problem. What happens when you want to subclass the Employee object? Let's create a Manager object.

Manager.prototype = new Employee;
Manager.prototype.constructor = Manager;
Manager.superclass = Employee.prototype;

function Manager(first, last, id, department) {
    if ( arguments.length > 0 ) {
        this.init(first, last);
        this.department = department;
    }
}

We want the Person object to handle the "first" and "last" properties. We want the Employee object to handle the "id" property. And we want the Manager object to handle the "department" property. The way things are set up now, this will not happen: the id property is never set.

Let's follow the logic we used before. Let's create an init method for the Employee object.

Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
Employee.superclass = Person.prototype;

function Employee(first, last, id) {
    if ( arguments.length > 0 )
        this.init(first, last, id);
}

Employee.prototype.init = function(first, last, id) {
    //this.init(first, last); // Oops...infinite recursion
     this.id = id;
}

Manager.prototype = new Employee;
Manager.prototype.constructor = Manager;
Manager.superclass = Employee.prototype;

function Manager(first, last, id, department) {
    if ( arguments.length > 0 ) {
        this.init(first, last, id);
        this.department = department;
    }
}

Here's the problem. When we call the init method with an Employee object, the interpreter will find the init method defined in the Employee. Before, we did not have the init method defined in Employee, so the interpreter found the init method in Person. We have effectively blocked access to the Person init method, but we need it to initialize our "first" and "last" properties. Here's where the "superclass" class property comes in.

We know that the interpreter works its way back through the prototype chain until it finds the property name its searching for. Since we don't want the interpreter to use the init method in our current object, we somehow need to get it to start looking at the prototype property one above the current one. If we're in the Employee object, then we don't want the interpreter to start looking at Employee.prototoype. We want it to start looking at Person.prototype and that's exactly how we defined the superclass class property.

Great. Now we know where the interpreter needs to start looking to find our "hidden" init method, but how do we get it to start looking there? Here's the trick.

Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
Employee.superclass = Person.prototype;

function Employee(first, last, id) {
    if ( arguments.length > 0 )
        this.init(first, last, id);
}

Employee.prototype.init = function(first, last, id) {    
    // Call superclass method
    Employee.superclass.init.call(this, first, last);

    // init properties
    this.id = id;
}

Every function in JavaScript has two methods that allow you to invoke the method as if it were a method of an object that you specify. These methods are "call" and "apply". We are using "call" in this example.

The first parameter to "call" is the object that you want to use in the method you are calling. All parameters following the object will be passed in as parameters to the method being called. "Apply" works the same way; however, the second parameter is an array of values that will be passed into the method as if you had listed them separately. That is useful when you want to pass all arguments used in the current method to the method you are "applying".

I recommend that you create an init method for every JavaScript object that you create. You never know when you'll want to subclass the object and by setting up the init method in advance, the object will be ready to subclass using the methods described in this tutorial.

Here is the final code. I've used the same superclass method calling mechanism in the toString methods so you can see more examples of this technique in use.

/*****
*
*    Person constructor
*
*****/
function Person(first, last) {
    if ( arguments.length > 0 )
        this.init(first, last);
}

/*****
*
*    Person init
*
*****/
Person.prototype.init = function(first, last) {
    this.first = first;
    this.last  = last;
};

/*****
*
*    Person toString
*
*****/
Person.prototype.toString = function() {
    return this.first + "," + this.last;
};


/*****
*
*    Setup Employee inheritance
*
*****/
Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
Employee.superclass = Person.prototype;

/*****
*
*    Employee constructor
*
*****/
function Employee(first, last, id) {
    if ( arguments.length > 0 )
        this.init(first, last, id);
}

/*****
*
*    Employee init
*
*****/
Employee.prototype.init = function(first, last, id) {    
    // Call superclass method
    Employee.superclass.init.call(this, first, last);

    // init properties
    this.id = id;
}

/*****
*
*    Employee toString
*
*****/
Employee.prototype.toString = function() {
    var name = Employee.superclass.toString.call(this);

    return this.id + ":" + name;
};


/*****
*
*    Setup Manager inheritance
*
*****/
Manager.prototype = new Employee;
Manager.prototype.constructor = Manager;
Manager.superclass = Employee.prototype;

/*****
*
*    Manager constructor
*
*****/
function Manager(first, last, id, department) {
    if ( arguments.length > 0 )
        this.init(first, last, id, department);
}

/*****
*
*    Manager init
*
*****/
Manager.prototype.init = function(first, last, id, department){
    // Call superclass method
    Manager.superclass.init.call(this, first, last, id);

    // init properties
    this.department = department;
}

/*****
*
*    Manager toString
*
*****/
Manager.prototype.toString = function() {
    var employee = Manager.superclass.toString.call(this);

    return employee + " manages " + this.department;
}