Ah, global variables, the bain of any JavaScript developer’s working life.
Especially if you’re in the business of writing a library, you want to minimise
the number of global vars your code produces so as to minimise the potential for
conflicts with other scripts running in the same environment. It’s all too easy
to miss a var
statement or to declare something in the wrong scope, so I use
this little tool to check that my libraries don’t create any unexpected
variables:
var Globals = {
originals: [],
userDefined: [],
warned: [],
root: this,
initialize: function() {
if (this.originals.length > 0) return;
for (var key in this.root) this.originals.push(key);
},
register: function() {
for (var i = 0, n = arguments.length; i < n; i++)
this.userDefined.push(arguments[i]);
},
check: function() {
for (var key in this.root) {
if (this.originals.indexOf(key) == -1
&& this.userDefined.indexOf(key) == -1
&& this.warned.indexOf(key) == -1) {
console.warn('Global variable: ' + key);
this.warned.push(key);
}
}
},
run: function() {
var self = this;
setInterval(function() { self.check() }, 1000);
}
};
// Example -- variables from JS.Class
Globals.register('JS', 'it', 'its', 'require', 'undefined');
Globals.initialize();
Put this in an external script file and load it before loading your library’s
code. Then, you can call Globals.run()
in Firebug and get a nice list of
warnings if you’ve accidentally created a global variable. Let’s take a quick
walk through how it works.
originals
stores an array of all pre-existing global variables. Most
JavaScript environments have a lot of built-in global variables and you want to
make sure you ignore them, since they’re not a problem. userDefined
stores a
list of names you’ve explicitly told Globals
about, so that they can also be
safely ignored. warned
stores names that Globals
has already warned you
about, as we don’t want to generate multiple warnings, and root
stores a
reference to the global object, equivalent to window
in web browser
environments.
We call Globals.register()
to tell the scanner about global variables we are
knowingly creating: we don’t want to be warned about them since we created them
deliberately. Put any global variable names from your library in here. Then, we
call Globals.initialize()
, which populates the originals
array with the
names of any global variables currently in existence. Make sure you call this
before loading your library code, otherwise your code will be ignored by the
scanner.
Finally, you can call Globals.run()
whenever you like, and this kicks off the
scanner. The function Globals.check
is run every second, and scans all the
names in the global object. If it finds a name that is not in originals
or
userDefined
or warned
, it prints a warning to the Firebug console and pushes
the name onto the warned
array. I always run this before I release code to the
public as part of my test process.