Proper testing for defined JavaScript variables

I often see even seasoned programmers failing to test for JavaScript variables and methods before attempting to operate on them. When they don't exist bad things happen. In IE this results in the infamous "Done, but with errors" whisper in the status bar (or if you are a proper developer, the interrupting dialog displaying slightly more detail1). In FF the error console will show the error, but the casual user often has no idea the script broke--just for some reason the page doesn't work. Such as in the case of poorly implemented client-side form validation...a classic and common error.

If you work in the industry, you'll find these cases when a manager comes to you angry about a broken page or irate customers. Too bad QA didn't catch the case since they didn't have (or follow) OCD-style test plans, which nobody has time to write or maintain in this crazy world of deliver-this-last-week. (Ugh, I guess so much time in this line of work has taken it's toll on my nerves...sorry folks.)

So, unlike Java which explodes vocally for any number of OCD reasons, JavaScript is the silent angry type--barely letting the casual user know that something went wrong. Even when that something is critical to the proper functioning of your web page or web application. This means you as the developer need to be vigilant and test your cases. Then when browsers change and new updates emerge...um, you probably ought to re-test your cases--just to make sure some developer didn't accidentally "do you a favor".

What do I mean specifically? I've included some examples below in an attempt to reveal common aspects of the problem, and some approaches to solutions.

One approach--and probably the preferred one from the procedure oriented programming crowd is to use the typeof() method to check that a...something, isn't undefined. There is an example of this approach below.

The approach I take most often is to script properties and methods as a property of a common object--such as "window", or "document". From an OOP purity standpoint, attaching to a known, but not necessarily directly related object seems a little tainted--okay, I can empathize with the thought, but in the end, it's an effective approach that's served me well for some time. There are some scoping issues that can come into play, for example attaching something to the window object essentially gives it a global scope rather than isolating it inside of a function. So really, a huge part of solving this problem is to properly understand how scoping works--which is er, out of scope for my quick-and-dirty purposes here. (Though I highly recommend taking the time to understand scope more fully as it's a critical way point in your pursuit of JavaScript--and indeed programming, mastery.)

Looking for a good JavaScript reference? Here are a few javascript resources.

I've tried to code the examples below in such a way as to have your JavaScript engine demonstrate the error. If it doesn't work, then I've fallen victim to my own fears--how embarrassing. Try the examples below by clicking the "Prove It" button beside the example you want to run.

The JavaScript engine throws an error differently in each browser. Here are two I use regularly under Windows.

COMMON--BUT ERROR PRONE VARIABLE TEST CASES

This will work.

<script type="text/javascript"><!-- var a=1; // variable is declared/defined if(a){ alert('a='+a); } else{ alert('"a" what?!'); } //--></script>



This will fail--and throw an error.

(Meaning, it WILL NOT run the else case.) <script type="text/javascript"><!-- /* var a=1; */ //commented out the variable declaration/definition if(a){ alert('a='+a); } else{ alert('"a" what?!'); } //--></script>



SOLUTIONS TO THE COMMON ERROR PRONE CASES

This will work.

<script type="text/javascript"><!-- window.a = 1 if(window.a){ alert('window.a='+a); } else{ alert('that\'s crazy talk'); } //--></script>



This will fail BUT WILL NOT THROW AN ERROR.


(It will fail in a controlled way. Meaning, it WILL run the else case.) <script type="text/javascript"><!-- /* window.a = 1 */ //commented out the variable declaration/definition if(window.a){ alert('window.a='+a); } else{ alert('that\'s crazy talk'); } //--></script>



Another approach that will fail BUT WILL NOT THROW AN ERROR.


Notice in this case, the variable is not explicitly attached to an object--it's a more common case variable. However in this case it's being tested for before being eval'd. <script type="text/javascript"><!-- /* var a = 1 */ //commented out the variable declaration/definition if(typeof(a) != "undefined"){ alert('a='+a); } else{ alert('that\'s crazy talk'); } //--></script>

It's worth pointing out that the cases above are very simplified in that the variable declaration/definition happens very close to the use case. Where this error crops up more often is when a variable is supposed to be defined in a layer not closely associated with the use case--for example in a professional setting where the programming team and the HTML teams are separate. Team 1 is supposed to create a library or server side include that will create a variable or method which Team 2 then uses blindly.

My favorite related observed interaction:
Team 1: "It's in the design documentation! "
Team 2: "...yes, well too bad it wasn't in the instantiation when the bosses browser experienced the error ...maybe you could forward a copy of the design doc to their browser."


There are other methods for error handling, including the more traditional try and catch mechanism (see also, try, catch, and throw)

1 The error dialog is not enabled in IE by default. You can enable the dialog in the options page by checking the box to "notify about every error". If you are serious about your craft, I highly recommend checking this box--at least on your development machine. (Your spouse, roommate, parents, or significant others would probably be annoyed by this option so don't select it on a community machine...unless you are the anti-social type.)

Related Keywords and misspellings: javascript, help, testing for defined javascript variables, undefined, variables, undef