I'm trying to extend the functionality of some methods of the 2dcontext object, however I can't get it to work the way I want: I want to override a method, but I want to call the original method from the overridden method like this:
//First get the original context
var ctx = canvas.getContext("2d");
//Create a class which uses ctx as it's prototype
var ExtendedContext = function (){};
ExtendedContext.prototype = ctx;
//And extend a method
ExtendedContext.prototype.fillRect = function(x, y, width, height) {
//Do some stuff
this.prototype.fillRect(x, y, width, height); //Doesn't work
//Do some more stuff
};
How can I call the original fillRect method from inside my own method?
3 Answers 3
You can store the reference of the original function just like that:
var oldFillRect = ctx.fillRect;
and then call it like
ExtendedContext.prototype.fillRect = function() {
//Do some stuff
oldFillRect.apply(this, arguments);
//Do some more stuff
};
This technique is sometimes called 'duck punching' or a 'function hook'. In this particular instance, you should also be able to use the Object.getPrototypeOf method to get the original function reference. This would look like
ExtendedContext.prototype.fillRect = function() {
//Do some stuff
Object.getPrototypeOf(ExtendedContext.prototype).fillRect.apply(this, arguments);
//Do some more stuff
};
So you don't even need to store a reference.
5 Comments
Object.getPrototypeOf should do it for you.ExtendedContext.prototype. Probably a bit clunky to use there.Object.getPrototypeOf(...) each time it is also possible to add the value of Object.getPrototypeOf(ExtendedContext.prototype) to the prototype of ExtendedContext, for example to ExtendedContext.prototype.par, such that I can simply use this.par.fillRect inside my own function.No need to save the old names in a separate object, use closures :
ExtendedContext.prototype.fillRect = (function () {
var oldf = ExtendedContext.prototype.fillRect;
return function () {
//Do some stuff
oldf.apply (this, arguments);
//Do some more stuff
};
}) ();
If you have a bunch to do this might help :
function extend (fnc) {
var mthd = (fnc.toString ().match (/^function\s+(\w+)\s*\(/) || ['', ''])[1];
if (mthd in ExtendedContext.prototype)
throw ('ExtendContext method ' + mthd + 'does not exist');
ExtendedContext.prototype['_' + mthd] = ExtendedContext.prototype[mthd];
ExtendedContext.prototype[mthd] = fnc;
}
Then you can call extend as follows
extend (function fillrect () {
// Do some stuff
this._fillrect.apply (this, arguments);
// Do some more stuff
});
To refer to the old method use its name prefixed with '_'
1 Comment
I'm a few months late, but I'm using a fairly simple design to accomplish this functionality.The structure of our JavaScript runs off of a global object to keep our code secured from global vars.
For each page/usercontrol we are modifying our global object to hold a new object, but some code needs different functionality in different places, requiring extension methods. We don't want to duplicate code and redefine the whole object for the extended instance, and we don't want the code to care how it is being extended.
Instead of punching a duck until it does what you want it to, why not create a generic extension method? Using our case, here is an example:
// Using a Global JavaScript object:
GlobalNameSpace.ExtensionFunction = function(oParam1, oParam2, oParam3)
{
/// <summary>All parameters are optional</summary>
return; // For instances when it is not being overwritten, simply return
}
//In the Code to be extended:
GlobalNameSpace.Control.ControlFunction(oSender, oArgs)
{
///<summary>Control's function</summary>
// Function-y stuff..
GlobalNameSpace.ExtensionFunction(oSender, oArgs);
}
//and finally in the code to extend the functionality
GlobalNameSpace.Page.Init
{
///<summary>Initializes the page</summary>
// redefine the extension function:
GlobalNameSpace.ExtensionFunction = function(oSender, oArgs)
{
// Call the extension function, or just code the extension here
GlobalNameSpace.Page.Function(oSender, oArgs);
}
}
The short coming of this method is if you want to do this for multiple objects at a time, at which point it may be a better idea to move an extension method into the code you are specifically wanting to extend. Doing this will make that extension code less generic, but that can be decided according to your needs.