Saturday, February 10, 2007

Power JavaScript : Closures, Objects, and "this"

For those of you who attended my Power JavaScript talk at CodeCamp '07 in Miramar, thank you so much for attending. You can download the PowerPoint and the code samples by clicking on this sentence. JavaScript is a rich topic, and we really need a whole day to go over some of the advanced topics. Closures, mentioned at the end, I will try to address here.

The simplest way to describe a closure is that it's an enclosing function that returns an inner function declared inside of it. To really understand them, I strongly suggest reading the following very accessible article,
JavaScript Closures for Dummies. Other articles out there on the net are academic and very confusing. Beware.

Nonetheless, their true importance comes from managing the "this" pointer when you are using OO JavaScript and events. Consider the following button:


<input type="button" value=" Increment Age " id="ageIncButton" />

Now consider the following simple JavaScript class definition:

function Person(pName, pAge) {
this.name = pName;
this.age = pAge;
}

Person.prototype.toString = function() {
return this.name + ", " + this.age;
}

Person.prototype.incrementAge = function() {
this.age++;
alert(this.toString());
}


You might think that we might be able to create an object instance and easily attach it to the button's onclick event in the following simple manner:

var person = new Person("Liza", 60);
incButton = document.getElementById("ageIncButton");
incButton.onclick = person.incrementAge;


Try it...you will find the behavior not as expected. The "this" pointer, when accessed after the event firing in the incrementAge method, does not point to the Liza instance. In fact, it refers to the global object!

Instead, we have to take advantage of the closure's object scoping to get the intended "this" pointer - the one that should be pointing back to Liza all along. The invocation would look like this:

var person = new Person("Liza", 60);
incButton = document.getElementById("ageIncButton");
incButton.onclick = attachObserver(person, "incrementAge");

...where the attachObserver method is defined as follows...

function attachObserver(objectInstance, methodName) {
return function() { objectInstance[methodName](); }
}

The trick to understanding the syntax of this closure is the objectInstance[methodName] expression. Remember that in JavaScript, everything is an object, including functions themselves. And all objects can have their members referenced with dot or bracket notation.

Play with the sample code, read the aforementioned closures article, and drop me a line if you are having troubles with this.

And remember an important thing: unhook your DOM events from your JavaScript code! IE will start leaking memory. For a discussion of the leak topic, take a look at the IE Leak Patterns article.

1 Comments:

At 3:00 AM , Anonymous Anonymous said...

Thanks for writing this.

 

Post a Comment

Subscribe to Post Comments [Atom]

<< Home