Helma Logo
main list history

Mocha Inheritance

In its current incarnation, Javascript prototype inheritance has some shortcomings. In particular, prototypes do not inherit their parent's constructor and methods in child prototypes can not dynamically access an inherited method they override.

After Hannes' and Jürg's suggestions for improvements to Dean Edwards' Base Class approach to Javascript inheritance, I made an attempt to solve the problem by focusing more intensely on fixing Javascript's prototype inheritance than adding class sugar around it.

What I came up with is this:

nametypesize
mocha.htmlundefined bytes
mocha.jsundefined bytes

Mocha Inheritance does its magic in these 20 lines of code:

 Function.prototype.inherit = function(fnc) {
    var constr = this;
    var Constructor = function(){
        if (fnc)
            fnc.apply(this, arguments);
        constr.apply(this, arguments);
    };
    Constructor.prototype = new (fnc || this)();
    Constructor.prototype.constructor = fnc || this;
    return Constructor;
 };
 Function.prototype.applySuper = function(method,obj,args) {
    var that = arguments.callee.caller.__parent || this;
    do {
        if (that.prototype[method] && that.prototype[method] != obj[method]
              && that.prototype[method] != arguments.callee.caller) {
            that.prototype[method].__parent = that;
            return that.prototype[method].apply(obj,args);
        }
        that = that.prototype.constructor;
    } while (that != Object);
 };

Creating a new prototype "Mensch" with mocha inheritance from "Animal":

 var Mensch = Animal.inherit();

Letting a previously created constructor "Mensch" mocha inherit from "Animal":

 Mensch = Mensch.inherit(Animal);

And here an example of a Mensch method "evolve" that overrides what it inherits from Animal, but still manages to do nothing different:

 Mensch.prototype.evolve = function(){
   return this.constructor.applySuper('evolve', this, arguments);
 }

With some more syntactic sugar around it, I think this is quite a nice approach.

Comments

#1 by Lehni at 2007/01/12 21:30

Interesting! Since posting my last version I made quite a few changes to my code that I will put online soon, and one of them was moving the extend / inject functions to Function.prototype, so they can work on any existing constructor, just like you are doing here.
There are other parallels, e.g. fixing the .constructor property (JS gets this one wrong when inheriting).

#2 by zumbrunn at 2007/01/13 11:24

I just realized I left a line of debugging code in there. So, it's now even one line less than before.

#3 by zumbrunn at 2007/01/15 09:43

I changed the syntax a bit, to what I think you'll agree is easier to read.

#4 by matthias at 2007/02/05 13:36
#5 by zumbrunn at 2007/02/11 12:48

I just looked through Douglas Crockford's slides for that presentation again. He does point out that the constructor inheritance in Javascript "doesn't actually work" and explains why, but he doesn't really suggest a fix. He basically says "it's broken, so let's not use it and roll our own instead". The mocha inheritance code above on the other hand, fixes the prototypal inheritance of Javascript and allows us to use it the way it was intended.

I believe the fact that the constructor property doesn't get set correctly during prototype inheritance in Javascript was an oversight or maybe even just a bug that crept in during the development of the language at Netscape while the project was still called Mocha, hence the name "mocha inheritance" ;-)

#6 by Anders Engblom at 2007/03/25 01:34

Hi,

I'm using this approach in my latest application but I cannot get it to work with nested inheritance with multiple overrides. I have the following structure:

Window (inherits Object)
ModalWindow (inherits Window)
MessageBox (inherits ModalWindow)

All of the classes have a 'show' method and in ModalWindow and MessageBox I call the super method like this:
this.constructor.callSuper('show', this, arguments);

The problem occurs in ModalWindow.show() - in that method 'this' refers to MessageBox, not ModalWindow. This leads to MessageBox.show calling itself over and over.

Any thoughts on this?

#7 by zumbrunn at 2007/03/26 15:53

The applySuper method wasn't properly keeping track of "that". The best solution I can think of right now is to add a "__parent" property to the inherited method, which points to the constructor that defined it.

I updated the code accordingly.

I also changed callSuper not to rely on applySuper, in order to avoid having to make applySuper more complicated just for the benefit of being callable by callSuper.

#8 by Anders Engblom at 2007/03/26 22:54

Works like a charm!
Thanks for the quick reply!

//Engblom