JavaScript Inheritance Pattern

Mon, 01 Mar 2010

More and more people on the Web digging into JavaScript and using it for daily tasks. It is always fascinating to watch how they approach inheritance in JavaScript. Most of the time two patterns are used:

First pattern:

function o1() {}
o1.prototype.a = function () {return "a";};
o1.prototype.b = function () {return "b";};

function o2() {}
o2.prototype = new o1;

o2.prototype.c = function () {return "c";};

var john = new o1,
   bill = new o2;
bill.a(); // "a"

The only problem with this one is that it uses the constructor of the first object to create middle object, which is going to be a prototype for o2. There is no guarantee that this constructor will be empty. Adding an alert in it will make the code unusable. A simple workaround leads us to second pattern.

Second pattern:

function o1() {}
o1.prototype.a = function () {return "a";};
o1.prototype.b = function () {return "b";};

function o2() {}
var f = function () {};
f.prototype = o1.prototype;
f.prototype.constructor = o2;
o2.prototype = new f;

o2.prototype.c = function () {return "c";};

var john = new o1,
   bill = new o2;
bill.a(); // "a"

Now, it is important to note that there is no way to do clean inheritance in ECMAScript 3. Fixing constructor in line: “f.prototype.constructor = o2;” makes it enumerable, which means it will show up in a “for in” loop. This pattern creates a real prototype chain. Changing o1 prototype will affect not only john, but also bill. Depending on the model this could be good or bad thing. You inherit everything: to remove a method, one has to overwrite it with undefined.

Downsides: bill.a is two steps away. The more you use this pattern the more steps away it will move, costing time and processing. And, as I mentioned, the constructor property is fake. It is enumerable.

There is a third pattern:

function o1() {}
o1.prototype.a = function () {return "a";};
o1.prototype.b = function () {return "b";};

function o2() {}
for (var method in o1.prototype) {
   o2.prototype[method] = o1.prototype[method];
}

o2.prototype.c = function () {return "c";};

var john = new o1,
   bill = new o2;
bill.a(); // "a"

This does the same as the others without prototype chain creation. Method “a” is nicely shared across two objects. You could alter o1 prototype without worrying about o2. Just as in previous case, depending on the model this could be a good or bad thing.

In my experience, the most common case is when you create objects’ constructors in the beginning and don’t alter their prototypes later on. Altering prototypes during your script is a tricky and dangerous procedure. I guess there are some cases when it is the best solution, but I can’t think of any of them off top of my head.

What I am trying to say is that the second pattern above will serve 99% of the current scripts where “inheritance” is used but long prototype chains make every call to the object’s property run through the whole chain and those runs are not free.

This pattern also makes the “instanceof” operator work, but I am not sure how useful it is in general. (I mean is (obj instanceof o1 === "a" in obj) always true? No. So, what the point?)

I reckon the second pattern only makes sense when you are about to mutate your objects during your script and benefit from this connection between prototypes. Otherwise you are creating infrastructure you are not going to use, but still have to pay the maintenance for. Why do that?

I prefer the third pattern for most cases.