Skip to content

Instantly share code, notes, and snippets.

@david-mark
Last active October 22, 2023 08:04
Show Gist options
  • Save david-mark/7cf93afccf3696a4f6754b36cd6ab34e to your computer and use it in GitHub Desktop.
Save david-mark/7cf93afccf3696a4f6754b36cd6ab34e to your computer and use it in GitHub Desktop.
Always Declare Global Variables

Often see library code similar to this:

// NOTE: Do not use this or similar code

(function(global) {
  'use strict';

  global.$ = {};
})(window);

Aside from the strange property name, what's wrong with this picture? The answer is that it brings a browser-provided host object into the mix for no reason. As we don't know anything about host objects, other than they are implementation-dependent, it makes no sense to use them when the language provides all we need. For example:

(function(global) {
  'use strict';

  global.$ = {};
})(this);

Still, why create a property of the global object, when we can simply declare a global variable?

var $;

(function(global) {
  'use strict';

  $ = {};
})();

Note that adding a property to the global object is not the same as declaring a global variable. There are no benefits to adding a property instead of declaring the variable, but there are certainly drawbacks. For example, older versions of IE created properties of the global object for each element in the document with an ID; attempting to overwrite these properties threw exceptions. Then there are other cases where the subtle differences can lead to mass confusion, particularly when mashing up separate modules; can get one behavior before concatenation and a different one after.

As I've always declared global variables, have never experienced anything like the latter example in any browser. Not sure whether this is strictly a case where the window host object differs from the global object or if it is a JScript bug in IE 8 (and under I presume). A quick Google search turns up numerous others with the same problem; unfortunately, all of the documented code uses window instead of this.

http://stackoverflow.com/questions/6581566/have-you-ever-seen-this-weird-ie-javascript-behaviour-bug

http://stackoverflow.com/questions/2635018/redeclared-javascript-global-variable-overrides-old-value-in-ie

segmentio/analytics.js#80

requirejs/requirejs#125

http://stackoverflow.com/questions/10633187/javascript-global-variable-behaviour-in-ie8

BorisMoore/jsrender#235

paulfalgout/backbone-moment#2

Ironically, the conflation of window and the global object makes it impossible to tell whether this is indeed a bug in the older IE browsers or simply a case of bad assumptions about the implementation-dependent host object. This has to stop and the "popular" libraries need to set an example as their code will invariably be copied.

https://github.com/jquery/jquery/blob/2d4f53416e5f74fa98e0c1d66b6f3c285a12f0ce/src/exports/global.js

Here's a case where window was used instead of declaring a global to seemingly "fix" an issue (while creating new ones of course):

expressjs/express-expose#13

Clearly they had problems elsewhere (possibly related to concatenating code for eval, which is another can of worms).

Anyway, the most popular answer to this StackOverflow question:

http://stackoverflow.com/questions/4862193/difference-between-variable-declaration-syntaxes-in-javascript-including-global

...contains more detail on differences between the global object and window host object in IE 8 and under. Unfortunately, it also contains this falsehood:

This won't work in strict mode, though, because in strict mode global code, this doesn't have a reference to the global object (it has > the value undefined instead).

In the global context, the this object always references the global object, strict mode or not.

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this

As a side note, always put function and variable declarations at the top. Won't change the results, but will help to avoid additional confusion related to hoisting.

But I digress, the only conceivable reason I can imagine for avoiding declaring the $ global variable is a concern that a builder or "compiler" (e.g. Dojo, RequireJS) may wrap every module in a one-off function call, thus removing the variable from the global scope. Make sure your tools don't do this; or better yet, concatenate your scripts before development and testing. This is another example of the sort of trouble we can get into by developing and testing code and then releasing different code (whether to production or QA).

In summary, adding properties to the global object instead of simply declaring global variables is a bad idea that can lead to all sorts of issues and confusion. Adding them to the window host object is an even worse idea and adds additional issues and more confusion. Furthermore, the behavior before and after module concatenation (or "compilation") can differ as well. Declare your global variables and avoid all of these problems. ;)

If for some reason we actually need a reference to the global object outside of the global context, we can simply declare one:

var global = this;

...and if NodeJS support is needed:

var global = global || this;

Should come as no surprise that adding support for NodeJS does not involve references to browser-provided host objects. :)

Note that the above will work, even in strict mode. Should also note that strict mode in the global context is a horrible idea as it adds the potential for additional complications when concatenating modules (will find most confine strict mode to functions for this reason).

@aminomancer
Copy link

thanks, glad i stumbled on this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment