Tuesday 2 August 2011

Variable closure in Javascript

First off; why should we care about Javascript? Silverlight rocks hard and is vastly superior to Javascript both in expressiveness and in standard library toolbox brevity.

A few years into the future, the above question will hopefully be answered with "what the hell is Javascript"?

Unfortunately, Silverlight adoption has not spread to all devices and platforms. For normal development, that is not a problem. Your customers are very likely using Silverlight ready platforms anyway.

The real world, however, is a cruel mistress. Business decisions - or local law - may force you to make your application available to as wide an audience as possible. In those cases, you need to use Javascript. And, when using Javascript, you need to be very careful what you do. It is a terribly undisciplined language with a weak standard library, inefficient arrays, variable types, no proper object orientation, and really weird variable scope.

This short post will focus on the latter problem: keeping your variables properly scoped.

Take the following example code, which is basically an extension method to the String type. It is a handy utility to count the number of characters in a string. If you implement it in your project, the expression "this".count() will evaluate to 4:

String.prototype.count = function (idxParam) {
  idx = idxParam || 0;
  returnValue = 0;
  if (this[idx])
    returnValue = 1 + this.count(idx + 1);
  return returnValue;
};

While this code works perfectly (try it!), there is one small problem with it. Can you spot it? Look closely. Closer. No? Unless you are a seasoned Javascript developer, the problem is impossible to spot and will likely remain invisible for a long time.

Remember I said Javascript has weird variable scope? In the above code, the variables have not been properly closed off and will leak out of the function and into the global namespace. After executing the above function, the variables "idx" and "returnValue" will be available to all functions everywhere. This may not sound like a real problem, but if some other functions also use variables with those names, they will re-use the above variables instead of receiving their own. Needless to say, this is likely to cause problems down the road. (Especially if you introduce multithreading!)

To check this, try to run the following code:
"Hello world!".count();

if (typeof(idx) !== "undefined")
  alert("Oopsie!");

So, how do we fix this? You need to "close" the variables off, so to speak. By nulling them out, you signal to Javascript that they are no longer needed when you exit the function:

String.prototype.count = function (idxParam) {
  idx = idxParam || 0;
  returnValue = 0;
  if (this[idx])
    returnValue = 1 + this.count(idx + 1);
  return returnValue;
  // close variables
  var idx = null;
  var returnValue = null;
};

// Do a quick check that "idx" is now scoped.
idx = undefined;
"Hello world!".count();
if (typeof(idx) === "undefined")
  alert("It works!");

Simple as that. Note that the var keyword is vital here. Without it, the last two lines would be normal program statements, and consequently would not be executed as they occur after the return statement. The magic var statements, on the other hand, are always executed.

The above hack works everywhere - I've tested in IE, Opera, Mozilla, Chrome, in Windows and on mobile devices. In the world of Javascript, this means it is practically defined behaviour. I recommend you adopt this as a best practice and remember it whenever you happen to step in some Javascript.