Checking your JavaScript for variable leaks

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.

JS.Class 2.0, now with even more Ruby-ness

A couple of months ago, I began a complete rewrite of JS.Class, as I’d become dissatisfied at some of its incompatibilities with Ruby, specifically with respect to module support. The 1.x series supported mixins in a reasonable way, but would not allow callSuper() to call methods from mixins. There were also a number of issues with the way extend and include work, which would surprise anyone expecting Ruby-like behaviour. In short, this particular abstraction was a bit too leaky.

The only way to get around this was a complete rewrite with Module right at the core, rather than having it tacked onto a fundamentally Class-based system. (In Ruby, Class inherits from Module, and both are classes.) The end result is that JS.Class 2.0 is much more Ruby-like: its method lookup rules are identical, and its internals are more Ruby-like too – JS.Class is a subclass of JS.Module, objects have eigenclasses, you can inspect method lookup results at runtime, and all sorts of other craziness. All these enhancements are hidden from you most of the time, but the extra metaprogrammability is allowing me to do some cool things – at the moment I’m working on a ‘compiler’ that will generate code for your classes that doesn’t depend on JS.Class, so you can use the library as a build tool rather than as a runtime dependency.

On top of these mostly invisible changes, there are three newcomers to the standard library: Package, Set and StackTrace. Package gives you the equivalent of Ruby’s require statement so you can declare URLs for various objects in your code, and lazy-load those objects (including dependencies) at runtime. Set implements a collection of unique objects, with support for custom object comparison. It has a subclass SortedSet that keeps its members in sort order, making searches faster – I’ve benchmarked it against KevLin’s red black tree library and its performance is roughly the same for insertion and searching. StackTrace is a debugging tool that allows classes to log all their method calls to the Firebug console, along with arguments, return values and stack level. This module was made possible by the new ability to redefine how method definition works in JS.Class 2.0.

Enough waffle from me: go and download it and play around. It is largely backward compatible with 1.x, but there are some small easily fixable cases where it may break old code – see the upgrade notes for more information.