Search code examples
javascripthtmlcanvasprototype-programming

Modify native CanvasRenderingContext2D functions


So, you can do this:

window.alert=function(a) {
    return function(b) {
        a(b+'!')
    }
}(window.alert)

Now suddenly alert('Hi') will alert Hi!. So, the window's alert function has been successfully modified. Fun.

Now the problem I'm having is applying this same concept to HTML5's CanvasRenderingContext2D (canvas). I'm not sure if prototype is the problem or if it's something else, but it's returning an "illegal invocation" error when I try to call the newly modified lineTo function.

CanvasRenderingContext2D.prototype.lineTo=function(a) {
    return function(b,c) {
        a(b,c)
        return this
        }
    }(CanvasRenderingContext2D.prototype.lineTo)

Can anyone get this to work or at least find out what exactly is causing it?

Also, in case you're wondering, my reason for wanting to do this is to create chaining with canvas functions (e.g. context.lineTo(10,15).lineTo(20,15).lineTo(20,20)).


Solution

  • First of all, mandatory warning against modifying host objects.

    Now that you know what you're getting into, here's the problem with your snippet:

    The new function that's assigned to CanvasRenderingContext2D.prototype.lineTo calls old function simply as a(b, c). When a function is called like this — a(b, c) — also known as "called as function", it's executed with this being global object.

    So context.lineTo(10, 15) will now execute old lineTo function (aliased under a) with this being Global Object rather than context. When lineTo doesn't have context to operate upon, it obviously can't do much, hence the error.

    How to fix this?

    Well we can call function with proper this value:

    CanvasRenderingContext2D.prototype.lineTo = function(a) {
      return function(b,c) {
        a.call(this, b, c);
        return this;
      };
    }(CanvasRenderingContext2D.prototype.lineTo);
    

    Notice a.call(this, b, c) which calls a function with correct this.