No sooner than I wrote about how to control a JavaScript function’s context with the Prototype Function.bind(), jQuery 1.4 is released and one of its new tricks is $.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.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 $.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 $.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 $.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.
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.
@JohnO It's mentioned in the second half of my last article.
http://www.jimmycuadra.com/blog/15-organizing-javascript-with-namespaces-and-function-prototypes
Great post. I was in doubt on how to access the DOM element, and e.currentTarget makes everything work perfectly. Thanks :)
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));
Great article! It was very interested
Proxy method only changes the value of varible "this" :)
Nice explanation. There's some interesting new concepts flying around at the moment.
Nice work!
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!