Monday, 17 March 2008

When you absolutely, positively need new scope

Just a quick one today, one that in many ways is a solution in search of a problem... ;-)

As you probably know, in most of the languages with vaguely C-like syntax (C, C++, Java, C#, ...), a new block introduces new scope. E.g., this Java code:

String myJavaMethod()
{
String s;

s = "outer string";

{
String s;

s = "inner string";

// (Presumably do something with 's' in here)
}

return s;
}
...while strange, is perfectly valid. It returns a String with the text "outer string". The s variable declared within the inner block is scoped only to the inner block.

A similar-looking JavaScript function has a completely different result:
function myJavaScriptMethod()
{
var s;

s = "outer string";

{
var s;

s = "inner string";

// (Presumably do something with 's' in here)
}

return s;
}
It's still valid, but it returns the string "inner string" rather than "outer string". Blocks do not introduce new scope in JavaScript, only functions do that. (If you're thinking that the second var declaration should be an error, well, I agree -- but it isn't, details here.)

But what if you absolutely, positively need new scope within a function? It's actually pretty easy to achieve; just use a closure. If I really needed my JavaScript function above to behave like the Java version, I could rewrite it like this:
function myJavaScriptMethodUpdated()
{
var s;

s = "outer string";

(function()
{
var s;

s = "inner string";

// (Presumably do something with 's' in here)
})();

return s;
}
Looks weird, eh? What I've done is create an anonymous inline function and then immediately execute it. Since it's a closure (all JavaScript functions are closures), it has access to all of the arguments and variables defined in its containing function except for the ones it's masked by declaring its own (which it's done with s).

Naturally, as this is defining and calling a function, there's a performance aspect although setting up a function call should be fairly quick in any decent JavaScript implementation -- and again, you're only doing this when you absolutely, positively need new scope, right?

So, okay, how is this useful? Well, as I say, this may well be a solution in search of a problem. It mostly gave me an excuse to highlight the fact that blocks don't introduce new scope in JavaScript. And frankly, in most cases if you need to set up new scope within a function like that, it probably means you haven't broken things up into small enough pieces; if you do that, you'll probably find that you don't absolutely, positively need new scope because your pieces are small enough that things are scoped properly.

That said, though, think in terms of sandboxing and namespacing -- if you wrap an entire external library in a closure and then execute that closure, any globally-declared vars in that library become local variables within the closure and can't conflict with others defined at global scope. So there may be something useful there, although it's far from a real sandboxing or namespacing solution...

No comments: