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:- Building method names with strings
var name = 'replace'; obj[name] = function() {}; obj[name + 'With'] = function() {}; Obj.replaceWith(); //Mixed access problem
- Testing for property existence with strings
obj.prop = 'exists'; if ('prop' in obj) … //Mixed access problem
- Using a property name in a loop
obj.prop = function() {}; for (var propName in obj) { if(propName == 'prop') { //Mixed access problem } }
- Building method names with strings
- 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:
Post a Comment