Created
November 1, 2011 17:45
-
-
Save jashkenas/1331310 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // 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. | |
| // |
Author
@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
@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.