Skip to content

Instantly share code, notes, and snippets.

@jashkenas
Created November 1, 2011 17:45
Show Gist options
  • Select an option

  • Save jashkenas/1331310 to your computer and use it in GitHub Desktop.

Select an option

Save jashkenas/1331310 to your computer and use it in GitHub Desktop.
// Demonstration of dynamic super() calls.
// Because of JS reserved words, "ssuper()" is the method name,
// and is passed the current object, as well as the name of
// the current method.
function GreatGrandParent() {};
GreatGrandParent.prototype.method = function() {
console.log("In the GreatGrandParent.");
};
function GrandParent() {};
GrandParent.prototype = new GreatGrandParent;
GrandParent.prototype.method = function() {
ssuper(this, 'method');
console.log("In the GrandParent.");
};
function Parent() {};
Parent.prototype = new GrandParent;
Parent.prototype.method = function() {
ssuper(this, 'method');
console.log("In the Parent.");
};
function Child() {};
Child.prototype = new Parent;
Child.prototype.method = function() {
ssuper(this, 'method');
console.log("In the Child.");
};
function ssuper(object, method) {
// Initialize an object-specific super depth counter. If desired, the counter
// can be specific to per-object-per-method-name.
var depth = object._superCount || (object._superCount = 1);
var proto = object.__proto__;
// Walk the prototype chain to the correct level of "super" ness.
while(depth--) proto = proto.__proto__;
// Increment the super counter.
object._superCount++;
// Actually call super().
proto[method].call(object);
// Decrement the super counter.
object._superCount--;
// We're done with this particular recursive super() call. Remove the record.
if (object._superCount <= 1) delete object._superCount;
};
(new Child).method();
// Pasting the above block of code into a browser console yields:
//
// In the GreatGrandParent.
// In the GrandParent.
// In the Parent.
// In the Child.
//
@jashkenas
Copy link
Author

@rauschma: Nope, and nope.

The correct behavior is for an external call to start out at the bottom of the inheritance chain, and work upwards through super() calls. Any call that just jumps to a parent implementation of a method that is overridden further below seriously breaks encapsulation. If you extend a class, and override a method with enhanced behavior, you expect your implementation to be called, not skipped arbitrarily.

If there are objects in the prototype chain that don't have the method ... it doesn't make any difference, because the method name resolves to nearest parent that does have the method. That's how a lookup of any property along the prototype chain works.

@rauschma
Copy link

rauschma commented Nov 3, 2011

@jashkenas: Example.

function A() {
}
A.prototype.desc = function() {
    console.log("A");
}

function B() {
}
B.prototype = Object.create(A.prototype);
B.prototype.constructor = B;
B.prototype.desc = function() {
    console.log("B");
    ssuper(this, "desc");
}

function C() {
}
C.prototype = Object.create(B.prototype);
C.prototype.constructor = C;

new C().desc()

Output:

B
B
A

When B.prototype.desc() makes the super-call, it initially calls itself.

I contend that you need to somehow record in _superCount where you actually found a given property. For example: gist.github.com/1331748

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment