This Var is My Var
Monday, August 30, 2010 | 1:34 PM
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!
4 comments:
trooper said...
That'll be really helpful, I always try to enforce a strict coding discipline when dealing with JavaScript. But keep in mind that even Closure Library itself produces a lot of warnings. For example I get this with most recent version of the library:
goog/ui/controlrenderer.js:298: WARNING - Access to protected property setStateInternal of goog.ui.Control not allowed here.
[exec] control.setStateInternal(state);
:)
August 30, 2010 at 2:14 PM
George Moschovitis said...
JavaScript sure has some pitfalls :(
September 1, 2010 at 12:38 AM
Unknown said...
Pitfalls, I agree. I believe Google maintains their own Closure library that has no warnings (from reading this post). The one that is open sourced is different, they keep pushing stuff from internal to external is my bet, that is why we see some missing stuff and warnings.
September 1, 2010 at 2:48 PM
Jethro Larson said...
It'd be nice if there was just a simple comment or something for declaring externals.
"globals: jQuery, util"
It feels really awkward to define the function again.
September 15, 2010 at 1:53 PM
Post a Comment