A Subtle Difference Between Window Properties and Global Variables

Consider this piece code which initializes the _gaq object prior to loading the Google Analytics script. It is part of the Google Analytics page code which they ask you to put on your web page:

<script>
var _gaq = _gaq || [];
// ...
</script>

This code looks innocent, but it was this very piece of code that sent me on a wild goose chase thanks to a very subtle difference in how it works on IE <= 8 vs other browsers.

First, what is the intent of this code? The right-hand-side of the assignment is _gaq || [] which intends to use the existing value of _gaq if it exists, but falls back to the empty array.

If _gaq was already a variable in the global scope - maybe you set it as a global variable by not using the var statement:

<script>
_gaq = ['the', 'existing', 'array'];
</script>

then, our original code snippet would successfully use the existing array, because Javascript ignores the var statement in that case and works as if it was

<script>
_gaq = _gaq || [];
</script>

In the case that _gaq is a property of the window object - maybe you initialized it like this:

window._gaq = ['the', 'existing', 'array'];

most browsers will treat _gaq equivalent to a global variable, and for those browsers, it would work exactly the same, except for the case of IE8 and below.

For IE8, given that _gaq is a window property and not a global variable, and also given that the var _gaq = _gaq || []; GA snippet is pasted inside its own <script> tag, then the following applies.

Imagine that the code was instead this:

;(function(){
  var _gaq = _gaq || [];
  // ...
}());

This is somewhat what IE8 behaves like. This code creates a new scope for the _gaq variable, within which it would be initialized with the value of undefined, and then be fallen back to the empty array. If you are confused, think of it like this

;(function(){
  var _gaq;
  _gaq = _gaq || [];
  // ...
}());

Thus, _gaq in this scope will always be assigned the empty array, losing sight of what it was in the global scope.

It behaves like this code except that at the end, _gaq ends up as a global variable. The end result is that, _gaq, which had a value and used to be a property of the window object, has now been erased, and replaced with the new value of _gaq (empty array).

To test the code and see exactly what I mean, here is a jsbin - run it in IE8 vs another browser and see the difference in output.

blog comments powered by Disqus