What's This?

If you've ever tried to write heavily-interactive web pages you've probably come across the special javascript variable this. People usually make the mistake of thinking this is the same thing as self in other languages (wrong), and in fact you might come across code like this:

var Foo = {
message: 'hi there!',

bar: function() {
var self = this;
var div = document.getElementById('some_id');
div.onclick = function() { alert(self.message); };
}
};


If in that onclick function you instead wrote this.message you'd alert a null value instead of 'hi there!'. "What the hell?", you ask, wondering how that onclick that clearly is within the scope of Foo (after all you can refer to self, defined outside the function body), all of a sudden have a different this? What the hell is this, anyway? The answer, as with a lot of nerdy questions, is "It depends".

  • In an object method ( Foo.bar(...) ): this is a reference to the object (i.e. Foo).
  • In an independent function call ( doStuff(...) ): this is a reference to the global scope (generally the window object).
  • In an event handler ( <div onclick='alert(this);'>): this is a reference to the DOM element that fired off the event (event.srcElement).
So in our case this starts off referring to Foo, but once the code in that onclick function is run it will be in the scope of an event handler, so this will refer to the div that was clicked. Javascript does have closures, however, so you can refer to any variables defined in the same closure that the function was (in our case this means referring to self).

This can be a total pain to work with since you have a variable that silently changes based on execution scope. Furthermore, this is in a language with functions as first-class objects, letting you apply these functions anywhere. Don't you wish you paid more attention during that Lisp course?

The best solution I've come across is to (1) grokk Scope and Lambdas and then (2) use prototype.js's Function.bind(). Calling bind on a function in javascript will wrap the function in another, locking its execution scope to an object specified by the argument. So we can now write the original code like this:
var Foo = {
message: 'hi there!',

bar: function() {
var div = document.getElementById('some_id');
div.onclick = function() {
alert(this.message);
}.bind(this);
}
}
There doesn't appear to be a huge gain here over the original code. Today is just a teaser. This method, however, opens up some much more powerful things to get some really nice OOP behaviour with DOM objects that I'll be writing about tomorrow (and hopefully demo'ing a little bit).

2 comments:

egometry said...
This comment has been removed by the author.
egometry said...

If you haven't seen it, I absolutely love http://www.kryogenix.org/code/browser/secrets-of-javascript-closures/

It covers the front and the back of closures in a wet way. And I do love wet learning.

It beats the hell out of forcing dry material down my brain's throat.