Which Compilation Level is Right for Me?

Wednesday, September 26, 2012 | 7:28 AM

Cross-posted from the Missouri State Web and New Media Blog

When first starting with Closure Compiler, it is easy to see the names for the compilation levels and assume that advanced is better than simple. While it is true that advanced optimization generally produces a smaller file, that does not mean it is the best fit for all projects.

What is really the difference?

There are quite a few differences, but the most significant is dead-code elimination. With advanced optimizations the compiler removes any code that it knows you are not using. Perfect! Who would not want that? Turns out a lot of people because the compiler can only correctly eliminate code when you specifically tell it about ALL of the other code used in your project AND ALL of the ways that your code is used by other scripts. Everything should be compiled together at the same time. That is a pretty big gotcha.

Here is a classic example:

<html>
<head>
  <title>Advanced Optimization Gotchas</title>
  <!-- an external library -->
  <script src="jquery-1.7.2.js"></script>
  <script>
    //This section is compiled
    function ChangeBackground() {
      $('body').css('background-color', 'pink');
    }
    //Export for external use
    window['ChangeBackground'] = ChangeBackground;
  </script>
</head>
<body>
  <!-- external use of compiled code -->
  <a onclick="ChangeBackground()">Pinkify</a>
</body>
</html>

In this case we have to explicitly tell the compiler about jQuery during compilation with an extern file and we have to tell it that our ChangeBackground function is called from external code. While this is a contrived example, it illustrates a case where it probably was not worth the time to ensure compatibility with the advanced optimization level.

General decision factors

So how do you actually decide which optimization level is right for your project? Below are some of the most common factors in that decision:

Simple Optimizations Advanced Optimizations
Looking for a replacement JavaScript compressor Looking for every last byte of savings in delivered code size or in execution time
Compiling a library where the vast majority of functions are part of the publicly exposed API Authoring a very large application with multiple modules
Unwilling to make substantial changes to code style Starting a new project or are willing to make substantial changes to coding style and patterns
Using external libraries that do not have existing extern files and are not compatible with advanced optimizations Wanting the best possible obfuscation of your code to protect intellectual property
On a tight timeline that does not allow for troubleshooting obscure errors after compilation Authoring a large library but want to support users who only use a small subset of the code

Coding style factors

Most of us are proud of our JavaScript. In fact we may have some slick coding patterns that make our code elegant to read and maintain, however, not all JavaScript coding patterns compile equally with Closure Compiler advanced optimizations. If your code contains any of the following (and you are unwilling to change this) then simple optimizations would probably be the best choice for you:

  • Mixed property access methods
    Closure Compiler treats properties accessed with dotted notation (obj.prop) differently than when accessed via brackets or quoted notation (obj[‘prop’]). In fact it sees them as completely different properties. This item is first on the list for a reason: it is almost always the biggest hurdle. Because of this, the following patterns are all places which can cause problems with advanced optimizations:

    1. Building method names with strings
      var name = 'replace';
      obj[name] = function() {};
      obj[name + 'With'] = function() {};
      Obj.replaceWith(); //Mixed access problem
    2. Testing for property existence with strings
      obj.prop = 'exists';
      if ('prop' in obj) … //Mixed access problem
    3. Using a property name in a loop
      obj.prop = function() {};
      for (var propName in obj) {
        if(propName == 'prop') { //Mixed access problem
        }
      }
  • Using the “this” keyword outside of constructors or prototype methods
    var obj = {};
    //Static method using “this”
    obj.prop = function() { this.newprop = 'exists' };
    obj.prop();
    alert(obj.newprop);
    ...

    In advanced optimizations, Closure Compiler can move and refactor code in unexpected ways. Some of them include functions which are inlined and properties which are collapsed to variables. In many of these cases, it changes which object the “this” keyword references. These cases have workarounds, but without special attention your code will likely not execute as intended. To illustrate, under advanced optimizations the compiler might change the above code to:

    var obj = {};
    var a = function() { this.newprop = 'exists' };
    a();
    //Property does not exist - it is defined on the window object
    alert(obj.newprop);

Choose wisely

Regardless of the choice between simple or advanced optimizations, you can still use the many compile time code checks and warnings for your code. From a missing semicolon to a misspelled property, the compiler can assist in identifying problems with your code before your users do it for you.

So to recap, advanced is not always better than simple. Modifying an existing code base to be compatible with Closure Compiler advanced optimizations can be a daunting challenge, and it definitely is not the best choice for every project.

0 comments: