Tuesday, April 08, 2008

Javascript Inheritance with super

One thing that has always bothered me about Javascript is there is no implementation of "super". Most texts suggest to simply call the super function explicitly via its prototype. It makes for refactoring your inheritance hierarchy no fun because of the direct calls. And let's not mention all the duplication.

Let's think for a minute. Everything in Javascript is an object. What if we added the super automatically to the overriden functions so we could get access to it? And while we are at it, why not make our object definitions a bit more explicit along the way?

So, I whipped up the following code:

Function.prototype.subclassFrom=function(superClassFunc) {
if (superClassFunc == null) {
this.prototype={};
} else {
this.prototype=new superClassFunc();
this.prototype.constructor=this;
this.superConstructor=superClassFunc;
}

}
Function.prototype.methods=function(funcs) {
for (each in funcs) if (funcs.hasOwnProperty(each)) {
var original=this.prototype[each];
funcs[each].superFunction=original;
this.prototype[each]=funcs[each];
}
}

It's two very short functions. But, it allows me to do this:

function Parent(z) {
this.z=z;
}
Parent.subclassFrom(null);
Parent.methods({
methodOne: function(x) {...},
methodTwo: function() {...}
});

function Child(z,y) {
arguments.callee.superConstructor.call(this,z);
this.y=y;
}
Child.subclassFrom(Parent);
Child.methods({
methodOne: function(x) {
arguments.callee.superFunction.call(this,x);
...
},
methodThree: function() {...}
});

"arguments.callee.superFunction.call" is a little verbose, but it's better than duplicating the name of my "super" implementation prototype all over the place. Plus, how often do you call "super". I generally think too many calls to "super" is a design smell.

I like this approach mostly. The only thing I don't like is the names I chose. But, I'll keep working on it. I was just happy in two functions, I have "super" in my constructors and functions. If anything, it's a different way of thinking about the problem.

Coming next, how to do aspects and annotations in Javascript.

6 comments:

Randal L. Schwartz said...

Take a look at Joose (http://use.perl.org/~malte/journal/35836 et. seq.) which is a port of the Moose meta-object model first from Perl6 to Perl5, and now to Javascript. It might have exactly what you're trying to build.

Stephen Compall said...

Also take a look at Prototype's Class.addMethods. It is less verbose, and also requires you to declare in the arglist that you want to send to super, which meshes well with "super sends should be avoided".

Unknown said...

Unfortunately Javascript is still on my to-learn list, but to me this looks like a problem best solve either with list based or standard delegation, like you would find in Slate and Self respectively... or by creating a derive function that automatically assigns a super field.

If I'm understanding you correctly Javascript has no mechanism for super calls, so you might need to derive a proxy object that can delegate automatically... I'm assuming here that Javascript provides a catch all field not found message that you can implement (it's been a while since I last looked at the design of Javascript).

With such a proxy object, you simply use a custom constructor method that assigns to its clone the delegate field, and hey presto... super calls. You could even automatically add these catch all methods to automatically call the super object for you, if you wanted to.

Blaine Buxton said...

Thanks for all of the comments. Joose was cool as was Prototype. I like looking how different developers looked at this issue. Anyway, I guess I should have not used the word "class". I really wanted to keep the Javascript prototype.

On Javascript:
There is no doesNotUnderstand or method_missing in Javascript so it makes proxies difficult. Also, unlike Self, Javascript's prototype implementation only gives you on slot so that you can not do what you would normally do in Self. You get one slot and the only way to access a a function that you inherit from (via the prototype immutable slot) is to call it explicitly. I find that quite repulsive.

If I had a wishlist, I would have a method_missing implementation, cheap syntactical functions and adding more of the Self prototype implementation (parent slots, etc).

Sadly, I don't think of any of those are in the ECMA Standards for future versions nor are they in ActionScript.

Sean Mahan said...

Waiting with baited breath for the aspects and annotations post...

MikeHoss said...

How about YUI's extend? I've used that many times with wonderful results.