Keep Javascript Libraries From Colliding

Namespacing

Summary (TL;DR)

Javascript libraries can often be used in ways the authors did not foresee. Global variables present many opportunities for conflict among Javascript libraries. Authors should use namespacing techniques and JSLint to avoid polluting the global namespace and thus avoid collisions with other libraries.

1. The problem

People who run Zendesk help desks like to integrate with other services. We have custom-built solutions for integration with BasecampPivotal TrackerHarvest, and a variety of others. Still, we can’t keep up with the constant stream of new applications out there. For those that we haven’t anticipated, we offer a “generic HTML widget” into which the help desk administrator can put a custom piece of HTML. The administrator goes to the other site, finds the little page that says “put this snippet in your page,” and sticks it into the generic HTML widget. Usually the snippet is something like:

<script src="http://some.webapp.com/our_snippet.js?uid=12345"></script>

The result is that this code is running on a page that has not only all of the Javascript for a Zendesk help desk, but also the Javascript for a half-dozen other unrelated widgets. That’s a problem when the code includes some global functions and variables with very common names.

A Semi-Fictional Example:

Two different libraries define an each function for iterating over an Array. The first does so as follows:

function each(array, fn) {
  for (var i = 0; i < array.length; ++i) {
    fn.call(array[i], i);
  }
}

And is called like so:

each(['a', 'b', 'c'], function(letter, index) {
  alert(index + ': ' + letter);
});

The other library also defines each to take an Array and a Function, but passes the arguments to the function in the opposite order:

function each(array, fn) {
  for (var i = 0; i < array.length; ++i) {
    fn.call(i, array[i]);
  }
}

And is called like so:

each(['a', 'b', 'c'], function(index, letter) {
  alert(index + ': ' + letter);
});

If a help desk administrator includes both of these libraries on a help desk, whichever was included second will win and break the first. Not only does the library break, the breakage is very difficult to diagnose.

2. Solutions

There are two common solutions. The first is to use an object literal as a namespace:

var MyIncludedLibrary = {
  each: function(array, fn) { ... }
};
...
MyIncludedLibrary.each(...);

This moves each out of the global namespace and into the named MyIncludedLibrary object. It certainly accomplishes the goal of preventing collision, but it doesn’t fully encapsulate the functionality: ?other code could purposefully rewrite MyIncludedLibrary.each. To truly protect the implementation of each, instead use a self-executing closure self-invoking function:

(function() {
  function each(array, fn) { ... };
  ...
  each(...);
}());

Angus Croll has a good article about namespacing in Javascript that goes into more depth on these two options and offers some others. A quick aside: there are perfectly good reasons to expose a function as a public member of a namespace likeMyIncludedLibrary.each. One is to offer a default implementation that can be improved in specific environments without making assumpations about the environment.

3. One more thing…

Even if you’re careful to namespace your code, you can still introduce incompatibilities by accidentally assigning variables to the global namespace. In the following example, the variable iis intended to be local to the function, but since there is no vardeclaration, it gets assigned in the global namespace:

var MyNamespace = {
  each: function(array, fn) {
    for (i = 0; i < array.length; ++i) {
      fn.call(array[i], i);
    }
  }
};
...
MyNamespace.each(['a', 'b', 'c'], function(letter, i) {
  alert(i + ': ' + letter);
});
i; // => 3

To avoid this problem, run your code through JSLint. It will tell you if you have an “implied global” that might conflict with other libraries.