JavaScript - - By Harry Fuecks

Javascript Inheritance

Searching Google for “Javascript Inheritance” returns some interesting but, IMO, also frustrating results such as Douglas Crockford’s various takes in Classical Inheritance in JavaScript or Kevin Lindsey’s approach, the former not really providing a clear answer while the latter relies on the convention of attaching a “superclass” property to your objects and effectively requires glueing your objects together for each instance you create.

Meanwhile libraries like prototype and MochKit have evolved their own conventions and strategies which you may or not may not appreciate. I’m grumpy in each case . prototype, despite having some very cool code in it, extends built-in Javascript types like Object which I don’t appreciate – makes me nervous about mixing in other Javascript libraries from other sources. Meanwhile looking at MochKit (which may be the best AJAX implementation out there, having made good reference to twisted) suffers from the kind of smartness summarized here, when it comes to it’s “Base”.

An Alternative

There is another way which probably gives you most of what you expect if you’re coming from a language like Java (ooops – can’t seem to stop it ;)). This is thanks to Troels Knak-Nielsen (author of indite – the realtime validating wysiwyg widget) who told me about it a long time ago on the JPSpan mailing list here.


function copyPrototype(descendant, parent) {
    var sConstructor = parent.toString();
    var aMatch = sConstructor.match( /s*function (.*)(/ );
    if ( aMatch != null ) { descendant.prototype[aMatch[1]] = parent; }
    for (var m in parent.prototype) {
        descendant.prototype[m] = parent.prototype[m];
    }
};

Note: above code updated after initial post re this comment

At first glance, with that regex, it looks like a hack but if you allow it to sink in (which has taken me more than a year), you realise that it isn’t. It’s worth bearing in mind what Troels had to say about it;

First off – I fully understand and agree to the point of not extending language functionality. That said I think the point in relation to javascript and inheritance is often misunderstood. It is central to javascript that it is a “typeless objectoriented language”. This goes for ECMAscript3.0 all the same. Where people most often get it wrong is when they mistake prototypes for classes – they are not.

In a typed OO-language the objects code lies with the class – objects are just instances. In js, the code lies in the object. Therefore, the object is not tied to the prototype. It is ok in js to manipulate the prototype runtime, even after objects has been instantiated. Consider doing that with a typed language.

Anyway. I have been working a lot with js recently, and found that one-level inheritance is simple enough using prototypes, but further inheritance cause trouble. I guess you came to the same conclusion with JPSpan. My first googling on the subject turned up a lot of wierd hacks, that I don’t appreciate. I eventually invented a solution witch I use, and witch I’m very happy with. It may look as a hack, but it works so well, that I kind of forgive it for that.

To defend myself further I’ll point out that it’s not a language extension, if you just understand what it does. It’s merely a utility that extends a prototype – Which should not be confused with class inheritance (though the result is the same).

It’s easiest to see Troels point by trying it…

Basic Inheritance

The way you’d expect…


function Animal() {
    this.species = "animal";
};

Animal.prototype.category = function() {
    alert(this.species);
};

function Dog() {
    // Call the parent constructor to setup
    // the parent object properties...
    this.Animal();
};

// Dog "inherits" from Animal
copyPrototype(Dog, Animal);

var d = new Dog();
d.category();

…and I get the alert “animal”

Another spin on that, this time without calling the parent constructor…


function Animal() {
    this.species = "animal";
};

Animal.prototype.category = function() {
    alert(this.species);
};

function Dog() {
    // No call to parent constructor
    this.species = "canine";
};

// Dog "inherits" from Animal
copyPrototype(Dog, Animal);

var d = new Dog();
d.category();

This time the alert reads “canine”.

Many generations

Often a problem with different approaches to Javascript inheritance, here’s how Troels approach deals with it;


function Animal() {
    this.species = "animal";
};

Animal.prototype.category = function() {
    alert(this.species);
};

function Dog() {
    this.Animal();
    this.species += ":dog";
};

// Dog "inherits" from Animal
copyPrototype(Dog, Animal);

function Poodle() {
    this.Dog();
    this.species += ":poodle";
};

// Poodle "inherits" from Dog
copyPrototype(Poodle, Dog);

var p = new Poodle();
p.category();

…alerts: “animal:dog:poodle”

Overriding Parent Methods

By default this approach simply replaces methods. That means you need to make sure you call copyPrototype before assigning any methods to the subclass prototype, otherwise the parent methods will override the child methods;


function Animal() {
    this.species = "animal";
};

Animal.prototype.category = function() {
    alert(this.species);
};

function Dog() {
    this.Animal();
    this.species += ":canine";
};

// Dog "inherits" from Animal
copyPrototype(Dog, Animal);

// Override parent method _after_ calling copyPrototype
Dog.prototype.category = function() {
    alert(this.species.toUpperCase())
    
};

… displays “ANIMAL:CANINE”

It’s also possible, although slightly painful, to call a parent method which has been overridden, with help from apply. Modifying the above Dog.prototype.category method demonstrates this;


Dog.prototype.category = function() {
    // Call overridden parent method...
    Animal.prototype.category.apply(this);
    alert(this.species.toUpperCase())
};

…which results in two alerts. If you needed to pass on method parameters, you’d need to pass the arguments array as the second argument to apply().

Mixins

MixIns (basically multiple inheritance) also works in a fairly predictable fashion;


function Animal() {
    this.species = "animal";
};

Animal.prototype.category = function() {
    alert(this.species);
};

function Quadruped() {};

Quadruped.prototype.run = function() {
    alert('Running...');
}

function Dog() {
    this.Animal();
    this.species += ":canine";
};

// Dog "inherits" from Animal
copyPrototype(Dog, Animal);

// Dog "inherits" from Quadruped
copyPrototype(Dog, Quadruped);

var d = new Dog();
d.category();
d.run();

…two alerts: “animal:canine” and “Running…”

Cautions

Two particular warnings with this approach…

1. When you call copyPrototype matters, as already mentioned. In practice it means you’re best off calling it immediately after declaring a child constructor.

2. Do not declare a toString method for the parent class Function object as this may break the regular expression in copyPrototype. You can still modify a parent prototype without doing any harm though. In other words this will break copyPrototype;


Animal.toString = function() {
    return "this is an animal";
}

But this is OK;


Animal.prototype.toString = function() {
    return "this is an animal";
}

In Summary

Of all the approaches I’ve seen, to me this has become the most convincing – the best way to keep your code DRY without having to modify core Javascript types and introduce strange side effects. It’s also only 8 lines of code, so doesn’t cost much. Credit to Troels for sublime lateral thinking.

Sponsors