I’ve recently been working on an update to JavaScript Debug, which has me doing a lot of cross-browser testing, and I noticed a few “interesting quirks” with try…catch in Internet Explorer 6-8 that I couldn’t find documented anywhere.
The Backstory
Since the primary goal of JavaScript Debug is to normalize the console cross-browser, I’ve been doing a lot of in-depth logging, passing large quantities of arguments to the console.log
method (among others). And because I’m dealing with totally fabricated data, I’m using totally fabricated variable names too, to keep things simple. Well, I thought it would be simple.
Normally, I try to avoid using try...catch
statements, but in this particular instance, I was troubleshooting some issues with console.assert
in Opera 11, and the try...catch
statement was the only thing keeping me from going insane. Either way, the code inside the try
block wasn’t throwing an exception in Internet Explorer.
The “Interesting Quirks”
Anyways, after I completed my tests in Opera, I came back to Internet Explorer to work on a few other things, and noticed that in all the logs, the e
object was undefined instead of the object I expected. Since my code had evolved organically, and was quite a bit more complex than this example, and I couldn’t remember if it had ever fully worked (I thought it had, but I wasn’t sure), I embarked on a 30-minute long debugging excursion where I systematically removed and tweaked code from the project until I had pretty much nothing left, at which I finally decided to take another look at the example code to see if anything was out-of-place there.
That’s when I finally noticed that the catch
block I had just recently added had an argument e
that just happened to coincide with the object variable e
that was all-of-a-sudden logging as undefined
. Lovely.
The Comedic Interlude
So, last night, I tweeted a little joke, “I may have just discovered another way to test for IE… var isMSIE = (function(e,x){try{x()}catch(e){}return!!e})();
#javascript #leakage”. But this morning, I realized that what I had tweeted, while amusing, doesn’t exactly explain what’s going on here.. So I decided to take another look.
The Quirks, Investigated
In the interests of being thorough, I tested this quirky behavior in Firefox 2-4, Chrome 8-10, Safari 3-5, Opera 9.6-11 and IE 6-9, and have only seen this behavior in Internet Explorer 6-8. I haven’t tested in any mobile browsers. You can load my simple test page (which is a combination of the two following examples) in any browser, and if you see 1,1,2,1,1,2
, things are working as-expected. If you see something like 1,,1,[object Error],[object Error],[object Error]
, you’re probably in Internet Explorer 6-8.
In the following test, the try...catch
statements have simply been defined, and since there’s no code inside of them, no exceptions will be thrown. Notice how, in Internet Explorer 6-8, the mere act of specifying a catch
clause ( with a named argument, e
) appears to implicitly declare that argument in the current scope, as if var e
was specified.
In the following test, the try...catch
statements contain code that will throw an exception of type ReferenceError
, because an attempt is made to access an undeclared variable. Notice that in Internet Explorer 6-8, not only does the catch
clause argument get implicitly declared (like in the previous example), but its value is set to the exception object, overriding any previously-set value.
It’s like a whole new kind of hoisting, made extra-special just for Internet Explorer 6-8. Except that you had no idea it was happening.
The Solution
So, what’s the solution? Use a unique variable name in your catch
blocks. Tell people to “pretty please, use a better browser.” Maybe pay more attention when debugging, or just try to get more sleep, in general. That’s all I’ve got. And I’m not sure what else I can say about this specific “catch
argument declaration/leakage” issue, other than that I haven’t yet seen it documented anywhere, so I hope this article helps somebody, somewhere.
Also, FWIW, if you try to do what I’ve done in my examples, JSLint will yell at you, saying “Do not assign to the exception parameter.” It won’t explain why, nor will it tell you that the current scope has become tainted with a brand new named variable in Internet Explorer 6-8, or that even if you’re not assigning it, its value may have changed, but at least it gives you some kind of warning, which may be helpful.
You know, while I may, at one point in my career, have thought coding JavaScript would be fairly simple, straightforward, and possibly mundane, I can tell you that there’s still always room for surprises. And that the next version of JavaScript Debug is going to be awesome.