Say you want to override a method by adding a new impl directly to the instance. callSuper should be callable from that new overriding method and it should result in the closest ancestor impl being called…even if it only has to go as far as the instance prototype (technically, the instance’s constructor’s prototype). Well…the code I had up wouldn’t really support that. It would skip over the instance prototype. The latest will support this.
Maybe I should start versioning
I definitely need to have tests.
March 21, 2006 at 10:16 am
I have been using you way of providing inheritance for a while, but one thing that keeps nagging me, is the property “superClass”. Call it nitpicking, but “superClass” is not only misleading (It’s not a class - it’s a prototype), but it’s also incoherent when compared to other reserved words, which are generally all lowercase (such as prototype or instanceof).
This had me thinking of a better name. “super” would be good, but unfortunately it’s a reserved word. Recently it occurred to me that “supertype” might be used. Thinking further about it though, I think that “supertype” has so much resemblence with “prototype”, that one might expect that it points not to the “superClass”, but to the prototype of “superClass” (superClass.prototype).
This lead me to the kind of superficial word “superstructor” (It’s in the dictionary though) instead of “superClass” and then introduce a new property “supertype”, which points to superstructor.prototype.
The latter would be convenient for calling the parent-class’ implementation of an overridden method. While I’m impressed by the callSuper method, I wouldn’t want to use it in the wild - it’s just too much of a hack, and it’s relying on deprecated features. In the rare event, I would prefer the more verbose, but less magic this.supertype..apply(this, arguments);
It’s a matter of words, but in my experience, words are important when it comes to programming.
Am I completely loosing my mind ?
March 21, 2006 at 5:47 pm
troels, thanks for giving it a whirl…good to hear its getting some use. I totally hear you on the not using callSuper “in the wild”. In my opinion, the sub-’class’ usually knows specifically whom it is inheriting from and can call the overridded functions directly via SomeSuper.prototype.someMethod.call(this, arg1, argN);. But there are some folks here at work that feel callSuper is easier and more readable…and others online that seemed to want it. So, I had an idea and tried it out.
It does feel like a bit of magic…but I do believe it is using non-deprecated features. From the 1.5 spec for arguments.callee:
Which sounds to me like it will still be available as I’m using it…it just won’t be available as a property of the function object (e.g. SomeSuper.prototype.someMethod.arguments.callee). It does use a non-standard prop: the Function:caller property.
As far as the name superClass, I’m not completely married to it. I thought about calling it uber for a bit…borrowing from crockford as well as my perl past. I don’t even mind calling it superConstructor or even superConstructorFunction. It’s actually only there to provide a means of building the prototype chaing programmatically when needed (rare) and for use in callSuper. I’ve never had a need to access it directly from outside of Class.js.
I guess I should have called it ‘_super’ then huh? Of course then that really says “don’t use me” and I dunno if I want to lock it out that much.
March 24, 2006 at 12:45 am
superconstructor would work. As you said, it’s only ever used to chain constructors, so I think verbose is better than easy-to-type. What about the supertype property instead of callSuper ? It’ll allow you to use this.supertype.someMethod.call(this, arg1, argN); which is slightly more flexible than SomeSuper.prototype.someMethod.call(this, arg1, argN);
Another solution could be something like :
Class.extend = function(subclass, superclass) {
var inline = function(){};
inline.prototype = superclass.prototype;
subclass.prototype = new inline();
subclass.prototype.constructor = subclass;
subclass.prototype.superconstructor = function() {
superclass.apply(this, arguments)
this.supertype = {};
var bind = function(meth, obj) {
return function() { meth.apply(obj, arguments) };
}
for (var key in superclass.prototype) {
this.supertype[key] = bind(superclass.prototype[key], this);
}
}
}
This allows you to do this.supertype.someMethod(arg1, argN); which is much more intuitive than the call()/apply() syntax, but it does introduce some overhead, I’m afraid.
I think _super is way off btw. To me, the underscore suggests protected access (which you already touched in another blogpost)
March 24, 2006 at 10:24 am
When does superconstructor get called? I assume from the subclass constructor…which assumes that it needs to which isn’t all that big a deal…but this requires that it must call super class constructor if subclass wants supertype. I get the point though.
This was one of the first approaches I took a stab at (although I like your use of bind)…the problem was that when this.supertype.blah is called multiple times in the same chain, you get an infinite loop. So if you have ClassC extends ClassB extends ClassA….ClassB.prototype.toString calls this.supertype.toString, and ClassC.prototype.toString calls this.supertype.toString, you will get an infinite loop because for an instance of C, this.supertype is ALWAYS going to be the one created via the call to this.superconstructor() from ClassC’s constructor. So when ClassB says this.subtype.toString() he actually will get his OWN toString method again…infinite looping.
I should probably also say that my ‘home’ language is Java…and so it is for the folks I work with. So what you described would be ideal…as that is what is used in the java syntax. I actually tried to mirror that exactly…have supertype be a function object…when called directly it would call the supertype constructor…and it would have all the supertype functions as you have described…so i could say
this.supertype();
from the constructor, and
this.supertype.toString();
from a method.
But I don’t think it is possible since supertype will always be replaced on the instance with the last class in the heirarchy…removing the possibility of its use by ancestor classes. This is what lead me to just callSuper…callSuper does the ‘magic’ to find out where in the chain it was invoked…then calls that method from that point in the chain (effectively cutting the chain and allowing the js engine to find the first impl of that method from there).
As for _super…I think I agree. The point was that if superconstructor is really only meant to be used internally (which it really is in my experience used within Class.js) should the name reflect that intent? Or does it stay open (no _) and just allow it to be accessed. Ah, but the problem here is that we are creating a new property on a provided object…not Class.js’, so maybe _super should NOT be subclass’s private member…but ours (maybe a private map where the key is subclass and val is superclass). The subclass constructor fn object is just a very convenient place to stick it
That seems to be a different issue entirely…in general I suppose that deserves its own topic: conventions for storing helper properties on another object. But I think this is a super-special case…Class.js is actually providing a property that one would natually expect to be part of a class object…what is its superclass. And I suppose that would mean it stays without an _.