Classes and Objects in JavaScript colorful lead photo

JavaScript supports object-oriented programming, with classes, objects, methods, and inheritance even though it doesn't have a class keyword. (This article is about JavaScript 1.5 and the EMCA-262 V3 standard and applies to all modern browsers. It is not about the future 2.0 proposal to the language.) Instead, JavaScript uses prototype-based inheritance to provide these features. It also provides several standard classes that serve as examples of what a proper JavaScript class should look like. This essay explains how to get the full benefit of object-oriented programming in JavaScript. It is not a primer on either object-oriented concepts or basic JavaScript syntax: I assume you're already familiar with both (or at least willing to look them up.)

First, a word about frameworks. Good frameworks such as ExtJS and Prototype.js have utilities that help you define classes correctly (e.g. Ext.extend() and Class.create()) Such functions are basically doing what I describe here, then adding their own non-standard, idiosyncratic, and incompatible features. For this reason, if you're using a framework, I recommend always using its class definition mechanism. Not doing so may introduce incompatibilities.

Constructors

Classes in JavaScript are synonymous with constructors. To define a new class, you write a constructor function which initializes new objects of that class. It's traditional but not required to start constructor names with an uppercase letter.

// A plain-text virtual post-it note
function Note(text) {
    this.text = text || "";
}

Notice that the constructor doesn't return anything. If we called it as an ordinary function, it would return undefined. To call it as a constructor, we use the new operator:

var note = new Note("Sarah's #: 555-1212");

The new operator does two things. Before the function is called, it creates a new this object in a special way. After the constructor is called, it returns that new object. So when we write a constructor, we know two thing: that this will be the new object to initialize, and that we don't have to return anything.

If you do return something from a function, this will be thrown away and the returned object used. This is occasionally useful, for example for writing Singletons or Factories, but normally constructors should not have a return statement.

Because of these rules, it doesn't make sense to call a constructor without the new operator. That's why it's helpful to distinguish constructors by starting them with an uppercase letter: it's a hint to other programmers that they should use new.

Prototype

When the new operator is used with a constructor to create a new object, that object is given a special relationship with the constructors prototype property. Here's how the standard describes it:

When a reference is made to a property in an object, that reference is to the property of that name in the first object in the prototype chain that contains a property of that name. In other words, first the object mentioned directly is examined for such a property; if that object contains the named property, that is the property to which the reference refers; if that object does not contain the named property, the prototype for that object is examined next; and so on.

Basically, the prototype provides defaults for the object. By adding properties to the classes prototypes, we can supply default properties for all objects of that class.

Note.prototype = {
    constructor: Note,

    cssClass: 'yellow-note',

    render: function() {
        return '<div class="' + this.cssClass + '">' + 
            this.text + 
        '</div>';
    }
};

Now, every Note object appears to have cssClass and render properties.

Notice that I gave Note.prototype a property called constructor. Every standard JavaScript object has a constructor property, so our classes should too. Here's the bare essentials for a base class (A base class doesn't extend any other class. We'll discuss extending classes below.):

function SomeClass() {
}
SomeClass.prototype = {
    constructor: SomeClass
};

Methods

Now, one of the default properties we added to Note.prototype was the function render. JavaScript does special handling to make function properties behave like methods. You see how I'm referring to this twice in the render method? When we call it like so:

var noteHtml = note.render();

this will be the note object. this.text is the string "Sarah's #: 555-1212" set in the Note constructor, and this.cssClass is "yellow-note", the default from the prototype. Note that this is identical to method invocation syntax in Java and C++. This is by design and should make it easy to remember. However, unlike C++ and Java, you must use this before member variables and other methods when writing methods. Example:

SomeClass.prototype = {
    constructor: SomeClass,

    idPrefix: 'some-thing-',

    createId: function() {
        return this.idPrefix + idSequence.next();
    },

    getId: function() {
        if ( !this.id ) {
            this.id = this.createId();
        }
        return this.id;
    }
};

By adding functions to the class's prototype that refer to the current object as this, we can give our class methods. That's basically all you need to know to define and use methods, but this other essay discusses some advanced usages.

Subclasses

Above, I showed a pattern for defining a base class. It's also possible to extend/inherit-from/subclass a class. Here's the obvious way to do that:

// a subclass of Note that appears on a certain date.
function Reminder(text, date) {
    Note.call(this, text);
    this.date = new Date(date);
}
Reminder.prototype = new Note();
Reminder.prototype.constructor = Reminder;
Reminder.prototype.cssClass = 'reminder-note';
Reminder.prototype.isDueToday = function() {
    var today = new Date();
    return today.getDateString() === this.date.getDateString();
}

// example of creating a Reminder
var reminder = new Reminder("Sarah's birthday.", "1/23/2010");

What's going on here? The key is setting Reminder.prototype to a Note object. This sets up a prototype chain: reminder -> Reminder.prototype -> Note.prototype. So, when we access a property on a Reminder, JavaScript first looks on that object, then the Reminder's prototype, then the Note's prototype. Let's break that down for the different properties:

  • text and date are on the reminder instance.
  • cssClass is at the Reminder class level. We "overrode" the default we inherited from Note with our own value.
  • We're still using the render method we inherited from Note. It still works, and will now use reminder-note CSS class.
  • isDueToday is a new method that is only defined for the Reminders, not for Notes.

You see in the constructor, were it says Note.call(this, text)? This applies the superclass's constructor to the new object (sometimes called constructor chaining.) In this example, that's actually when the instance's text method is initialized. (This is a special case of invoking a superclass method, described below.) In most cases, you should call the superclass's constructor on the first line of code inside the subclass. Occasionally it's helpful to do other setup before calling it, and you have freedom about exactly what you pass into it. However, I've never seen a case where it would be appropriate to not call the superclass constructor at all. If you're coming from Java, be aware that JavaScript doesn't automatically invoke the superclass's default constructor if it's omitted. You always have to explicitly call it yourself.

In short, everything works just the way you'd expect if you're familiar with inheritance in other OO languages.

Unfortunately, the above is a little too simple. The problem is this line of code:

Reminder.prototype = new Note();

It creates a new Note by calling the Note constructor with no arguments. That happens to work OK for Note, but many classes can't be correctly constructed with no arguments. And since we're going to call superclass constructor from our constructor anyway, the superclass's constructor ends up getting applied twice. How can we set up a correct prototype chain, while avoiding invoking the superclass's constructor on the prototype?

The following syntax seems like it should work, but in fact is exactly the same as above, executing the constructor with no arguments. (This behavior is specified in the standard; it is not a bug or browser specific.)

Reminder.prototype = new Note;

Happily, there's a technique to accomplish this which is standard-compliant, works in all browsers, and has been used in at least two major JavaScript libraries that I know of (ExtJS and Mochikit) and probably many others. What you do is, you use an empty function, temporarily set its prototype to the superclass's prototype, call the empty function with the new operator, and make the result to the subclasses prototype. If you think it through logically, the resulting prototype chain is exactly the same, which is all we care about. Using this technique we can come up with a generalized function to handle all the details for us. This implementation uses my clone() method, described elsewhere.

// see the clone essay for more about this function.
function Clone() { }
function clone(obj) {
    Clone.prototype = obj;
    return new Clone();
}

// Pass in the subclass constructor, superclass constructor,
// and an Object of methods/static properties to
// add to the subclass.  The function correctly sets up
// subclass.prototype.
function extend(subclass, superclass, methods) {
    subclass.prototype = clone(superclass.prototype);
    subclass.prototype.constructor = subclass;
    for ( var key in methods ) {
        subclass.prototype[key] = methods[key];
    }
}

This extend() function captures the three essential steps for inheritance in JavaScript: setting up the prototype chain, setting the constructor property, and adding stuff to the subclasses prototype. Here's how it could be used to streamline the Reminder example:

// a subclass of Note that appears on a certain date.
function Reminder(text, date) {
    Note.call(this, text);
    this.date = new Date(date);
}
extend(Reminder, Note, {
    cssClass: 'reminder-note',

    isDueToday: function() {
        var today = new Date();
        return today.getDateString() === this.date.getDateString();
    }
});

Some frameworks go a little further and write the constructor for you. That way, they can make sure the superclass constructor is invoked. However, there are some disadvantages to that approach. The generated constructor can't have the right name unless eval() is used. It also eliminates the freedom to invoke the superclass constructor when and how you want. For these reasons, it's better to write the subclass constructor explicitly.

Frameworks tend to do other stuff at this point too. For example, ExtJS adds a superclass property to the base class, so you can use BaseClass.superclass to get a reference to the superclass's prototype, similar to Java's super, which is kinda useful. (super is a reserved word in JavaScript.) Unfortunately, there's no consistency, so consult your framework's docs for details and avoid such features if you want your code to be portable between frameworks.

Calling Superclass Methods

When you override a method, it's common to invoke the overridden superclass method. JavaScript doesn't have a native super keyword, so we do this "C++ style," using the explicit name of the superclass. We also have to invoke the superclass method with call() or apply() to get the scope right. I've covered [the this keyword][FSJS] and [the arguments collection][JSA] in detail elsewhere, so I'll just give the basic patterns here:

// the superclass method
SuperClass.prototype.someMethod = function(arg1, arg2) {
    // does some important logic.
    // returns some important result.
}

// variant 1: using Function::call()
SubClass.prototype.someMethod = function(arg1, arg2) {
    // does some additional logic first
    return SuperClass.prototype.someMethod.call(this, arg1, arg2);
}

// variant 2: using Function::apply()
SubClass.prototype.someMethod = function(arg1, arg2) {
    // does some additional logic first
    return SuperClass.prototype.someMethod.apply(this, arguments);
}

// variant 3: doing stuff after the superclass method.
SubClass.prototype.someMethod = function(arg1, arg2) {
    // maybe some additional logic first
    var ret = SuperClass.prototype.someMethod.apply(this, arguments);
    // more additional logic afterwards
    return ret;
}

Using apply(this, arguments) is more future-proof: if arguments are added to the superclass method later (and adding additional option arguments to a function is a very common way for code to evolve) then all arguments will still be passed through correctly to the superclass method. So variant 2 is usually preferred, or variant 3 only if the precise order of execution is important, or if we need to change or use the return value in some way.

Calling the superclass method is optional; often you want to completely replace the method, instead of adding behavior to it. The only exception is the constructor: it's a very bad idea to not invoke the superclass constructor from the subclass. Skipping that step will not give the superclass a chance to initialize its private (logically private, I mean: privacy isn't enforced in JavaScript) state, which places severe restrictions on the future evolution of the superclass. With methods, it depends: sometimes it's useful to delegate the core behavior of the method to the superclass and just add our own on top of that, and sometimes it's useful to completely redefine how the method behaves.

instanceof Operator

The instanceof operator is JavaScript's built-in way of doing type checking on objects. It tells you if a given object is an instance of a given class, or equivalently, if it was created by a given constructor.

note instanceof Note; // true
note instanceof Reminder; // false

It also searches up the class hierarchy, and returns true if the object is an instance of a subclass of the given class:

reminder instanceof Reminder; // true
reminder instanceof Note; // true: a Reminder is-a Note

The logic is simple: search of the prototype chain of the object on the left and return true if you encounter the prototype property of the constructor on the right. (instanceof always returns false if the left-hand-side is not of type "object"; use the typeof operator if you need do distinguish other types such a "string" or "number".) If you follow the above pattern for extending a class, the instanceof operator will work correctly for you.

Private, Protected, and Public

JavaScript has no concept of "private" or "protected" members. Often, programmers simulate these with some kind of convention, like the leading underscore ("_protected") convention borrowed from Python. That works fine for protected members, which are very similar to public ones: it's just a matter of whether they're interesting to every user of the class, or just to those programmers who want to extend it. Private is another matter. The equivalent idiom from Python of a double underscore ("__private") doesn't really work, because Python is actually mangling double-underscore names to implicitly include the class name, and JavaScript doesn't do anything like that.

To see why the double underscore doesn't help, imagine programmer A writes a class with a "private" member __id and two public methods, getId() and setId(). Because __id is private and encapsulated, it's not documented or mentioned anywhere. Later, programmer B extends that class, and decides to use his own private __id member for another purpose. Now there's a conflict; a conflict that wouldn't exist in C++, Java, Python, or any other language with explicit support for private members.

The most anal retentive solution is to manually mangle private variable names using some formula of the class name, perhaps like _SuperClassName_memberName. That's pretty tedious. The 80/20 solution is to list your private variables, without describing what they are, and politely ask subclass implementers not to use those names. However, this does not protect subclasses when new members are added to the superclass later. The laissez faire solution is to do nothing, and let subclass implementors read the source code or inspect objects in a debugger to discover which names to avoid. You'll have to find the right balance for your project. My advice would be to avoid standardizing on anything with high overhead until it becomes a problem in practice.

Multiple Inheritance

JavaScript doesn't support multiple inheritance. An object can have at most one prototype. There are ways do achieve equivalent effects in many cases, though. For example, one might implement Mixins like so:

function createMixin(methods) {
    return function(TargetClass) {
        for ( var key in methods ) {
            TargetClass.prototype[key] = methods[key];
        }
    }
}

var mixinLock = createMixin({
    lock: function() {
        if ( this.locked ) {
            throw new Exception("Already locked.");
        }
        this.locked = true;
    },

    unlock: function() {
        this.locked = false;
    }
});

mixinLock(SomeLockableClass);

In this example, we're defining a reusable concept of a lockable object, then applying it to some class that we want to be lockable. However, this kind of stuff is limited and non-standard. For example, it is very difficult to define a Mixin utility that can handle overriding methods.

Another common use of multiple inheritance is to implement interfaces, by inheriting from some abstract base class that defines the interface. If JavaScript, this is unnecessary and you'll find that Duck Typing is a better approach: simply document which methods and properties a class should have to implement the interface, then implement those methods on classes that support it. Unit tests can substitute for type checking in this case.

The instanceof operator will never work with any pseudo-multiple-inheritance trick.

The bottom line is, JavaScript doesn't support multiple inheritance. In some sense you're on your own, but you're not totally out of luck because the dynamic features can also be used to implement similar features.

Is It Worth It?

Yes, eventually. There are some limitations and complexities you need to get past to write object oriented code in JavaScript, but it does pay off after a few thousand lines of code. Many project, where simple DHTML scripts slightly enhance mostly-server-generated content, will never reach that point: use jQuery and don't worry about this stuff. But for JavaScript-heavy projects that want to see some level of code re-use, these techniques really do help keep things organized, and really don't cause significant headaches or excessive wheel-spinning.

In a very real sense, this is how JavaScript was meant to be written. prototype, this, instanceof and the rest are there for a reason: to support programming in exactly this way. We're not swimming upstream, trying to get some hopeless scripting language to jump through object-oriented hoops: we're going with the flow, using standard language features as intended, following the example of the standard classes. That's why it works.

- Oran Looney November 24th 2009

Thanks for reading. This blog is in "archive" mode and comments and RSS feed are disabled. We appologize for the inconvenience.