This literally changes
Monday, November 30th, 2009
You should know that I’ve built up a lot of half-written posts recently, mostly about javascript, so this blog, as it emerges from semi-hibernation, is gonna get code-ier and more out of date as I work backwards.
So to begin at the end, I recently came across a bug which is far from obvious to track down. Look at the code below.
var MyClass = function() {
this.a = 'dolphin';
this.getA = function() {
return this.a;
}
}
myObject = new MyClass();
var callbacks = {callback1: myObject.getA}
newA = callbacks.callback1();
What will the value of newA be?
In my application, in which I used callbacks to store the callback functions for a series of custom-made dialog boxes, I was expecting the value to be a, but it isn’t. The reason for this is that callbacks is an object, and saving a function as a property of an object is, in effect, creating a method of that object, and methods of objects, when called using the ‘.’ syntax, will tend to have this refer to the object (not always, but normally). So in creating the callbacks object you inadvertently cause getA to fail as this no longer points to the myObject object, but to the callbacks object literal.
Fair enough. But what makes this error particularly difficult to track down is that firebug shows console.log(callbacks) to be an empty object. Why this is, I don’t know – it seems unlikely to me that the properties wouldn’t be created due to the value of this being reassigned, so maybe it’s a mistake in firebug ( a firebug-bug, if you will). A similar error occurs when using an array as callbacks instead of an object, but firebug reports that the function is stored in the array, and that it returns an undefined value, so it’s a lot easier to see what the problem is. But once the bug is found, the workaround for the array and the object is the same.
The method I’ve chosen is to define
var that = this;
at the top of the MyClass function, and anytime you use this but think it may be called in such a way that the context may change, substitute that for this. But this is far from perfect – you have to second guess when you will need to do this as it’s not always obvious, and you could easily miss an instance which doesn’t occur very often, but which is fatal when it does. Another solution is to use the prototype.apply method to explicitly set the context of the method, though it looks ugly and a bit indecipherable. On the other hand it is a lot more bulletproof.
I’m sure you’ll make the right decision.


