Understanding jQuery 1.4's $.proxy() method

No sooner than I wrote about how to control a JavaScript function's context with the Prototype Function.prototype.bind, jQuery 1.4 is released and one of its new tricks is jQuery.proxy, which we can use for exactly the same purpose.

To refresh you, the problem that needed solving was how to preserve a reference to the calling object when the value of this is changed. Consider the following:

MyModule = function() {
  this.$div = $('#testdiv');
  this.myString = "Hi! I'm an object property!";

  this.$div.click(this.handleClick);
};

MyModule.prototype.handleClick = function() {
  console.log(this.myString); // undefined
};

var m = new MyModule();

When you click on the div, we are given undefined, because this now refers to the DOM element that triggered the event and not the instance of MyModule we've stored in the variable m. The element does not have a myString property, and hence, it is undefined. So how do we access the myString we want? The solution I wrote about previously was to use Function.prototype.bind from the Prototype library, which allows us to control what this will refer to inside the function we're calling. But now in version 1.4 of jQuery, we can handle this situation with the new jQuery.proxy method. The method has two signatures:

jQuery.proxy( function, scope )
jQuery.proxy( scope, name )

In the first form, the function argument is the function we're calling, and the scope argument sets the context the function should be called in. In the second form, the scope comes first, and the name argument is a string that gives the name of the function we're calling. Note that the function provided in name should be a property of whatever object we're using as the scope. Let's look at an example to make this more clear.

MyModule = function() {
  this.$div = $('#testdiv');
  this.myString = "Hi! I'm an object property!";

  this.$div.click($.proxy(this.handleClick, this));
};

MyModule.prototype.handleClick = function() {
  console.log(this.myString); // Hi! I'm an object property!
};

var m = new MyModule();

Now when we click on the div, we see the result we want, because this is now bound to our m object, which is an instance of MyModule. This uses jQuery.proxy's first signature. We could achieve exactly the same effect using the second signature by using this line instead:

this.$div.click($.proxy(this, "handleClick"));

The small "gotcha" that remains is how to access the DOM element that triggered the event, now that we've changed the value of this within our function. The good news is that, like before, all event handling functions are passed an event object with details about the event that occurred. Within that event object is a property called currentTarget, which holds exactly what we're looking for: the DOM element that triggered the event. Here is an example of how to access it:

MyModule.prototype.handleClick = function(event) {
  console.log(this.myString); // Hi! I'm an object property!
  console.log(event.currentTarget); // <div id="testdiv">
};

Thanks to jQuery.proxy, we now have the ability to control function scope with jQuery alone. To read more about all the new features in jQuery 1.4, check out John Resig's overview on The 14 Days of jQuery.

Comments

JohnO JohnO commented
January 15, 2010

Where is your example with bind() (I don't see it in a quick glance of your recent posts).

I'd like to see the difference in use.

Jimmy Cuadra Jimmy Cuadra commented
January 15, 2010

@JohnO It's mentioned in the second half of my last article.

http://jimmycuadra.com/posts/organizing-javascript-with-namespaces-and-function-prototypes

Morten Morten commented
January 15, 2010

Great post. I was in doubt on how to access the DOM element, and e.currentTarget makes everything work perfectly. Thanks :)

Morten Bo Morten Bo commented
January 16, 2010

Instead of calling handleClick, you could do something like this:

this.$div.click($.proxy(function() {

// now 'this' refers to the MyModule obj
console.log(this.myString);

}, this));

SMiGL SMiGL commented
January 18, 2010

Proxy method only changes the value of varible "this" :)

Jamie Thompson Jamie Thompson commented
January 20, 2010

Nice explanation. There's some interesting new concepts flying around at the moment.

Joe McCann Joe McCann commented
January 20, 2010

Nice work!

Kevin Bosak Kevin Bosak commented
January 20, 2010

Morten Bo, in that case you could just do this:

var self = this; // this is a MyObject

self.$div.click(function() {
console.log(self.myString);
// 'this' is still the DOM object for the click
});

Which is unfortunately what I have been doing. Now that I have the proxy() method I'll be writing much cleaner code. Great article!