Basically is there a good elegant mechanism to emulate super
with syntax that is as simple as one of the following
this.$super.prop()
In the spirit of completeness (also thank you everyone for this thread it has been an excellent point of reference!) I wanted to toss in this implementation.
If we are admitting that there is no good way of meeting all of the above criteria, then I think this is a valiant effort by the Salsify team (I just found it) found here. This is the only implementation I've seen that avoids the recursion problem but also lets .super
be a reference to the correct prototype, without pre-compilation.
So instead of breaking criteria 1, we break 5.
the technique hinges on using Function.caller
(not es5 compliant, though it is extensively supported in browsers and es6 removes future need), but it gives really elegant solution to all the other issues (I think). .caller
lets us get the method reference which lets us locate where we are in the prototype chain, and uses a getter
to return the correct prototype. Its not perfect but it is widely different solution than what I've seen in this space
var Base = function() {};
Base.extend = function(props) {
var parent = this, Subclass = function(){ parent.apply(this, arguments) };
Subclass.prototype = Object.create(parent.prototype);
for(var k in props) {
if( props.hasOwnProperty(k) ){
Subclass.prototype[k] = props[k]
if(typeof props[k] === 'function')
Subclass.prototype[k]._name = k
}
}
for(var k in parent)
if( parent.hasOwnProperty(k)) Subclass[k] = parent[k]
Subclass.prototype.constructor = Subclass
return Subclass;
};
Object.defineProperty(Base.prototype, "super", {
get: function get() {
var impl = get.caller,
name = impl._name,
foundImpl = this[name] === impl,
proto = this;
while (proto = Object.getPrototypeOf(proto)) {
if (!proto[name]) break;
else if (proto[name] === impl) foundImpl = true;
else if (foundImpl) return proto;
}
if (!foundImpl) throw "`super` may not be called outside a method implementation";
}
});
var Parent = Base.extend({
greet: function(x) {
return x + " 2";
}
})
var Child = Parent.extend({
greet: function(x) {
return this.super.greet.call(this, x + " 1" );
}
});
var c = new Child
c.greet('start ') // => 'start 1 2'
you can also adjust this to return the correct method (as in the original post) or you can remove the need to annotate each method with the name, by passing in the name to a super function (instead of using a getter)
here is a working fiddle demonstrating the technique: jsfiddle