Wednesday, July 14, 2010

JavaScript: Checking for undeclared and undefined variables

Up until now, the JavaScript I've written has typically been in pursuit of a bigger goal. This means that I worked around language quirks on the spot and moved on. I never stopped to consider the nitty gritty details of the language for commitment to long-term memory, because I wanted to get a working end product.

Recently, someone asked me about the best way to check for undefined JavaScript variables. I found myself at a loss, which was alarming to me since doing these kinds of checks is immensely important, practical, and a frequent fact of everyday programming no matter what programming language you're using.

The fact of the matter is that most of the time, I know where my variables are coming from. Something like this would work:

    var x;

if (x)
x.some_method();
else
alert("we can't use x");

In the vast majority of cases where I had to perform a null check, I knew that the variable was declared somewhere because it was of my own making.

What if you're counting on something having been declared somewhere else and being there? Let's try this.

    if (y)
y.some_method();
else
alert("we can't use y");

In this case, you should get a ReferenceError thrown. You'll see this if you're using the Firebug JavaScript console or the developer tools in Safari or Chrome.

After much experimenting and looking around the web, here's my definitive, reliable, robust, cross-browser way to perform this kind of check:

    if (typeof(z) != "undefined")
z.some_method();
else
alert("we can't use z");

It also works for when you're the one in charge of declaring a variable, too.

    var w;

if (typeof(w) != "undefined")
w.some_method();
else
alert("we can't use w");

So that fixes our problem. But look closer, and you may have a couple of nagging questions.

Why in the world would you be referencing a variable you didn't declare? If we only ever had to deal our own code, we should always know where the variables that we're using are coming from. But that is not the case for many of us; we rely on using others' code, third-party modules, or frameworks all the time.

An easy example is if you want to send debugging output to console.log in Firebug. When Firebug is disabled, console isn't a declared JavaScript object. (Safari and Chrome have their developer tools more integrated so this problem doesn't appear.) It helps to first check if there's a debugging console available before you start writing to it.

You could also find yourself in a situation where you're expecting some object to be initialized because it's an external library. If it's loaded over the network, and loaded separately from the rest of your code, you'll want to recover gracefully if it's not available for some reason (like the remote server is down).

Why do we have to compare to the string "undefined" instead of the keyword? Because typeof() always returns a string.

    console.log(typeof(j));             // "undefined"
console.log(typeof(typeof(j))); // "string"

This seems too simple/short. Will it work in all web browsers? This will work in any modern web browser that supports JavaScript. Just to be thorough, I've personally verified that it works as expected in Internet Explorer 8, Firefox 3.6, Safari 4, and Google Chrome 5.0.

Checking for undefined variables in JavaScript in this way has been around for a long time, and several other sources suggest this method. I wrote this up mainly as an extended explanation for why things are the way they are, and to explicitly address quirks like typeof() always returning a string.

What if it could also be null in some cases? Then you add a check for null. I've limited the check to checking for "undefined" to keep the explanation focused, but yes, in reality you'll want to do an additional check.

    var k;

if (typeof(k) != "undefined" && k)
k.some_method();
else
alert("we can't use k");

The check for null is pretty straightforward; it's "undefined" that trips us up. Note that, as written, this code will only proceed to check for the null condition if k has been defined. This is because of short-circuiting: if the first condition is true, there's no need to evaluate the rest of the other parts of the conditions.

When it comes down to it, the key insight is that JavaScript has "undefined" variables in two senses: first, when they're not even declared, and second, when a variable has been declared but hasn't been assigned a value. This is when it really is undefined in the strict sense.

We also saw that typeof() will always return a string, which means you need to compare its result to a string when you're running a conditional test.