3

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?

asked May 3, 2012 at 11:09

3 Answers 3

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.

answered May 3, 2012 at 11:14
Sign up to request clarification or add additional context in comments.

5 Comments

I forgot to mention, but there are more methods I need to override, so if possible I don't want to save each function I override manually. But I guess I could simply loop over all functions, and copy all of them to some object. Edit: That did the trick! Thanks!
@Tiddo: see the update. Object.getPrototypeOf should do it for you.
I responded to soon: Your last example doesn't seem to work: Object.getPrototypeOf(this).fillRect seems to refer to ExtendedContext.prototype.fillRect, so it'll get into an infinite loop. ExtendedContext.prototype.fillRect overrides the ctx.fillRect method since the prototype points to the ctx object.
@Tiddo: you're right. You would need to explicitly get the prototype of ExtendedContext.prototype. Probably a bit clunky to use there.
Thanks to your last suggestion I found an even cleaner solution imo: Instead of calling 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.
1

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 '_'

answered May 3, 2012 at 12:26

1 Comment

That's a really nice solution as well!
0

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.

answered Oct 25, 2012 at 15:45

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.