The Closure Compiler you know is very different than the Closure Compiler that’s familiar to Google engineers. The defaults at Google are much stricter. It forbids duplicate global variable declarations, performs type-checking, and restricts a number of other patterns that are common in non-Closure-style JavaScript. By default, the “Google build” of Closure Compiler is like running the "open source build" of Closure Compiler with the --warning_level VERBOSE flag.
Why? Almost every contributor to Closure Tools works on web applications. We want to be able to share code. Part of that means we should be able to fix the shared code, and integrate it into all our apps quickly so that, for example, fixes for IE9 will make it out to production within a reasonable time. To integrate those changes safely, we need to be confident that they won’t break anything. Compiler restrictions help us do that. Sometimes those restrictions are obvious...sometimes less so.
Suppose Joey writes some library code:
Joey.prototype.eatHotDogs = function(hotDogs) {
for (i = 0; i < hotDogs.length; i++) {
this.eat(hotDogs[i]);
}
};Next summer, Nathan writes an application to feed Joey HotDogs by the boxful:
Nathan.prototype.runHotDogContest = function(player, boxes) {
for (i = 0; i < boxes.length; i++) {
player.eatHotDogs(this.unpackHotDogs(boxes[i]));
}
};Surprisingly, when Nathan runs his code in a browser, it tries to feed Joey an infinite number of hot dogs and crashes the browser.
The problem, of course, is that JavaScript "helpfully" declares variables for you if you forgot. No one declared
i, so a variable
i got magicked into the global scope. When Joey originally wrote his code, he tested it and it worked fine. But when Nathan tried to use it, he found that
eatHotDogs() was "resetting" the
i in his loop every time he called it.
In this example, Nathan was directly calling the problematic method, and the bug was relatively easy to spot. In the general case, these bugs are "non-localized" —missing
var in one file may trigger a bug in an unrelated file that hasn’t been touched in years. Or it may cause a bug years later in unrelated new code. For a library shared across many codebases, non-localized bugs can stop progress quickly because it’s so hard to know where to start looking.
In
VERBOSE mode, Closure Compiler enforces that all variables must be declared with the
var or
function or
catch keywords. Global variables may only be declared once. The Google JS style guide has
naming conventions to make these collisions even less likely.
This rule has some corollaries that have a big impact on how we write JavaScript. For one, there must be a way to declare variables that are "owned" by the surrounding page, and not by the code under compilation. So Closure Compiler has
externs files, which allow you to declare external variables. Conversely, external variables may not conflict with your code. If you use the default externs, you will not be able to name a variable
Node because it will conflict with
DOM Nodes.
Try running Closure Compiler with
--warning_level=VERBOSE mode and see what you find in your code!
Posted by Nick Santos, Software Engineer