abc def foo bar
abc def foo bar

Reputation: 2388

Super function when modifying a prototype in javascript

Is there a function like super when you are modifying a prototype in javascript?

Upvotes: 2

Views: 247

Answers (4)

Jan Miksovsky
Jan Miksovsky

Reputation: 1339

It's possible to use JavaScript's .caller facility to create a general solution that lets you invoke a superclass' implementation of a function with the same name as the calling function. The basic idea is to use a reference to the calling function, walk up the class hierarchy to find which class actually implements that function, and then go up one more step to get the super class.

See https://gist.github.com/1265237 for an example.

This allows a call to a super function without needing to specify the name of the current class. E.g., if A is a superclass of B, then:

A.prototype.calc = function ( x ) {
    return x * 2;
}
B.prototype.calc = function ( x ) {
    return this._super( x ) + 1;
}

var b = new B();
b.calc( 3 );         // = 7

The _super implementation in the above gist does more work the first time it's called, but speeds up in subsequent calls. This uses the JavaScript function .caller (and falls back to the older arguments.callee.caller in older browsers). It's widely claimed that callee/caller are slow because they can't be optimized, but "slow" is a relative term. Depending on your use case, this approach might well suffice.

Upvotes: 1

Jean Vincent
Jean Vincent

Reputation: 12435

There is unfortunately no super keyword or equivalent in ECMAScript. This has been a major source of confusion and difficulty to implement real-life inheritance.

Lot's of people have implemented complex solutions (Prototype library, John Resig, and many others), all these solutions create extra closures, manipulate functions code, use special names that may clash with upcoming standards changes, or other complex tricks. Some of these solutions just do not work if there is more than one level of inheritance ( A > B > C ) because these use some form of "this.super" that always refers to C's and therefore creates an infinite loop if B also attempts to call its super method (which happens in real code).

There exists however a very simple an much more efficient solution that I found and use successfully in my projects, and that works for any levels of inheritance, and does not run any risk of name collision. No closure required, no magic, just simple straightforward JavaScript that works on all browsers, here it is:

// Create base class
Shape = function() {};

// Add method draw() to base class
Shape.prototype.draw = function() {};

// Create new class
Box = function() {};

// Make Box inherit from base class using Douglas Crockford Object.create()
// that is now part of ECMAScript 5 (which is why I recommend it)
Box.prototype = Object.create( Shape.prototype );
// Reset the constructor property erased by the former statement
Box.prototype.constructor = Box;

// Create draw() method for Box that calls it's base class draw() method
Box.prototype.draw = function() {
  Shape.prototype.draw.call( this ); // Here you go!
}

The only drawback of this solution is that it is requires to type a few more keystrokes than the other solutions. But because there are no closures involved, there is no unnecessary function calls so it will likely be more efficient than any other solution I have seen so far.

Alternatively, if you want to pass all arguments of draw() to the super class method, you can use this form:

Shape.prototype.draw.apply( this, arguments );

Or if the parameters are known and differ from that of the super class you can use:

Shape.prototype.draw.call( this, param1, param2 );

I hope this helps others :)

Upvotes: 4

Sean Vieira
Sean Vieira

Reputation: 159875

Not in the language itself -- but there is no reason you cannot add it when you are creating your class. There is actually a very nice exposition on the way that Coffeescript manages prototypal inheritance by Justin Reidy on his blog

The key part is reproduced here:

var __hasProp = Object.prototype.hasOwnProperty;
function __extends(child, parent) {
    for (var key in parent) { 
        if (__hasProp.call(parent, key)) {
            child[key] = parent[key];
        }
    }
    function ctor() { this.constructor = child; }
    ctor.prototype = parent.prototype;
    child.prototype = new ctor;
    child.__super__ = parent.prototype;
    return child;
}

You will also probably want to read @bobince's introduction to working with classes in JavaScript.

Upvotes: 2

Zevan
Zevan

Reputation: 10235

not by default, but if you are looking for a way to do classical inheritance in javascript you could consider using a library like prototype or mootools... or if you don't want to go that route, John Resig, the creator of jQuery provides a really nice light solution:

http://ejohn.org/blog/simple-javascript-inheritance/

It's just important to note that inherited arrays and objects are not copied to instances with this implementation - so if you inherit an array you'll want to redifine its values in the subclasses constructor function (otherwise all instances will share a single array) - I can post an example for you if you want to see, there is also one in the comments of that post.

Upvotes: 1

Related Questions