Intro

While many programmers do think that Object Oriented JavaScript is a good approach to take, it’s also known that it’s hard to write robust OO-style JavaScript simply due the nature of the language itself and the environment which it’s running in (mostly are browsers).

Using Google Closure Compiler can not only help you to compress the the code, but it also compiles it just like any compiler does.

When the compiler’s flag ADVANCED_OPTIMIZATIONS is enabled, you can get a lot more optimization than most JavaScript compressors such as YUI Compressor, Dojo Compressor.

This article will talk about several common OO-style patterns and how they are implemented now or how they should be implemented.

In the end, you shall see how you can write formal OO-Style code and keep the performance boosted with the help of Closure Compiler.

Have fun!

OO-style JavaScript is considered expensive

For a long time, for JavaScript, due to its nature and its host environment, the browser, it’s usually considered hard to write pure Object-Oriented javascript application. Nicholas Zakas, one of the most well-known Black Belt Karate JavaScript programmers, once wrote a BLOG post about the pain of writing OO-style JS code.

In traditional object-oriented languages, classes and inheritance come for free. There’s no performance penalty for increasing the number of classes or having a deep inheritance tree – the compilers know their job good enough to save the details from you. In JavaScript, reference types do come with a penalty, and inheritance comes with an additional penalty.

JavaScript simply has no Class

Though OO-style JS has its drawback such as runtime performance penalty, people still tries varies of different way to achieve such a goal of using more OO-style JS to replace functional JS.

For example, John Resig has an interesting way to ensure that a “Class” function is always used as a constructor no matter the whether new keyword is used.

function User ( name ) { if ( ! ( this instanceof User ) ) return new User ( name ) ; this . name = name ; } var userA = new User ( ' John ' ) ; var userB = User ( ' Jane ' ) ;

JavaScript has no access control

Moreover, Douglas Crockford also proposed a module pattern that demonstrates how to protect private members from being read or written by external Object.

function User ( name ) { var _name = name ; this . getName = function ( ) { return _name ; } ; }

Global variables are bad, but deeply name-spaced variables aren’t anything better, too.

While global variables are considered evil, using deeply name-spaced variables are also considered slow.

var myProjectDemoUserName = ' foo ' ; my . project . demo . user . Name = ' foo ' ;

Developers can’t afford redundancy

How about loading the entire YUI DOM library to the document when all you really need is just one static method YAHOO.util.Dom.getStyle(document.body, ‘backgroundColor’) to get your job done?

Simplicity isn’t that simple

Or, you plug in your jQuery library then quickly realize that there’s nothing built in to handle the DOM Range and Selection Model. Also there’s no Data Collection, no Component framework, no Class Inheritance or anything that you might need to build an complex web application.

Only JavaScript Ninjas survive?

So this is why everyone is trying so hard to hire a JavaScript ninja or become one

However, is it really necessary to write JavaScript like this?

if ( ! ( " a " in window ) ) { var a = 1 ; } alert ( a ) ;

In fact,Code like this should never be written, it’s the bad part of JavaScript. No C++, Java programmer would ever or be allowed to write a code like this.Programmer should focus on algorithm and data structure, not this kind of confusing tricks.

Can we have all the Good Parts?

It appears it’s almost unlikely to write JavaScript that is light-weight, generic, robust and can be used in many places while being maintainable, capable of providing rich set of functionalities.

Does it sound really sad to you? Would this make JavaScript nothing more than a toy language?

No worries, Closure Compiler comes to the rescue.

Let Closure Compiler make you a JavaScript Samurai!

The Closure Compiler is a tool for making JavaScript download and run faster. It is a true compiler for JavaScript. Instead of compiling from a source language to machine code, it compiles from JavaScript to better JavaScript. It parses your JavaScript, analyzes it, removes dead code and rewrites and minimizes what’s left. It also checks syntax, variable references, and types, and warns about common JavaScript pitfalls.

Since the official web site already has a lot of great resources about how to use the compiler. Instead of writing a article about how to use the compiler, which actually requires writing a chapter of a book, let me focus on solving the issues that I mentioned earlier.

User @constructor to annotate a function as a Class.

Instead of doing run-time check inside the constructor, you should let the compiler do the job for you. Note you should avoid using run-time check whenever this job can be done by the compiler, which is the key idea that you should remember.

function MyClass ( ) { } var obj1 = new MyClass ( ) ; var obj2 = MyClass ( ) ;

Use @private for access control.

function User ( ) { this . _birthDay = new Date ( ) ; } User . prototype . getBirthYear = function ( ) { return this . _birthDay . getYear ( ) ; } ; var me = new User ( ) ; document . write ( me . getBirthYear ( ) . toString ( ) ) ;

The compiler will ensure that the private member _birthDay isn’t read or write through out the whole application. Only code in the same JS can access objects that are marked @private . Similarly, you can also use @protected to annotate your code.

Use @extend to manage class inheritance.

Say that we have three classes Shape , Box and Cube .

Class Shape defines a common method getSize()

Class Box extends Shape .

Class Cube extends Box

http://www.yuiblog.com/blog/2010/01/06/inheritance-patterns-in-yui-3/ function inherits ( childClass , parentClass ) { var tempClass = function ( ) { } ; tempClass . prototype = parentClass . prototype ; childClass . prototype = new tempClass ( ) ; childClass . prototype . constructor = childClass ; } function Shape ( ) { } Shape . prototype . getSize = function ( ) { } ; function Box ( width , height ) { Shape . call ( this ) ; this . width_ = width ; this . height_ = height ; } inherits ( Box , Shape ) ; Box . prototype . getWidth = function ( ) { return this . width_ ; } ; Box . prototype . getHeight = function ( ) { return this . height_ ; } ; Box . prototype . getSize = function ( ) { return this . height_ * this . width_ ; } ; function Cube ( width , height , depth ) { Box . call ( this , width , height ) ; this . depth_ = depth ; } inherits ( Cube , Box ) ; Cube . prototype . getDepth = function ( ) { return this . depth_ ; } ; Cube . prototype . getSize = function ( ) { return this . depth_ * this . getHeight ( ) * this . getWidth ( ) ; } ; var cube = new Cube ( 3 , 6 , 9 ) ; document . write ( cube . getSize ( ) . toString ( ) ) ;

The JavaScript code above is quite long. However, you should not worry about the source code size by simply looking at the number of characters entered.

Note that you should always write descriptive code for your documents, variable names, method names then let compiler rename or remove them later.

Similarly, don’t worry about having a three levels Class inheritance tree for such a simple function, the compiler will optimize that part for you.

Let’s see the compiled code below.

function d ( a , b ) { function c ( ) { } c . prototype = b . prototype ; a . prototype = new c } function e ( ) { } e . prototype . a = function ( ) { } ; function f ( a , b ) { this . c = a ; this . b = b } d ( f , e ) ; f . prototype . a = function ( ) { return this . b * this . c } ; function g ( a , b , c ) { f . call ( this , a , b ) ; this . d = c } d ( g , f ) ; g . prototype . a = function ( ) { return this . d * this . b * this . c } ; document . write ( ( new g ( 3 , 6 , 9 ) ) . a ( ) . toString ( ) ) ;

Though all the variables and method are renamed, you might notice that some methods are removed and some methods are in-lined. For example:

Cube . prototype . getSize = function ( ) { return this . depth_ * this . getHeight ( ) * this . getWidth ( ) ; } ;

Becomes:

g . prototype . a = function ( ) { return this . d * this . b * this . c } ;

Apparently, the getter getWidth() and getHeight() can be replaced by this._width and this._height safely, so those getters become useless and can be removed by the compiler.

Note that using both @private and a getter implies that the private property _width is read-only for developers and there’s no penalty for adding a getter for it.

Use @interface and @implements

Since we’re interested in writing OO-style JavaScript, the previous example can be changed to code below.

function Shape ( ) { } Shape . prototype . getSize = function ( ) { } ; function Box ( width , height ) { Shape . call ( this ) ; this . width_ = width ; this . height_ = height ; } Box . prototype . getWidth = function ( ) { return this . width_ ; } ;

The compiled code gets even smaller since @interface is only used in compiled time, and the output never includes the interface code.

function d ( a , b ) { this . c = a ; this . b = b } d . prototype . a = function ( ) { return this . b * this . c } ; function e ( a , b , c ) { d . call ( this , a , b ) ; this . d = c } ( function ( a , b ) { function c ( ) { } c . prototype = b . prototype ; a . prototype = new c } ) ( e , d ) ; e . prototype . a = function ( ) { return this . d * this . b * this . c } ; document . write ( ( new e ( 3 , 6 , 9 ) ) . a ( ) . toString ( ) ) ;

Use package (name-spaced JS Object.)

Namespace your JS Objects as long as you need, don’t worry about the level of the namespaces that you should use regarding runtime performance. The compiler takes care of that part for you.

var demo = { } ; demo . example = { } ; demo . example . exercise = { } ; demo . example . exercise . Foo = function ( ) { demo . example . exercise . Foo . print ( this . value1 ) ; demo . example . exercise . Foo . print ( this . value2 ) ; } ; demo . example . exercise . Foo . print = function ( str ) { document . write ( str ) ; } ; demo . example . exercise . Foo . prototype . value1 = ' abc ' ; demo . example . exercise . Foo . prototype . value2 = ' def ' ; var foo = new demo . example . exercise . Foo ( ) ;

The compiled code looks like this:

function a ( ) { document . write ( this . a ) ; document . write ( this . b ) } a . prototype . a = " abc " ; a . prototype . b = " def " ; new a ;

Also, you can keep your JS code as private by using the flag –output_wrapper so it won’t collide with other scripts on the page and none of your JS object is global (unless you export them explicitly)

The compiled code looks like this:

( function ( ) { function a ( ) { document . write ( this . a ) ; document . write ( this . b ) } a . prototype . a = " abc " ; a . prototype . b = " def " ; new a ; } ) ( )

The compiler will do its best job to ensure that long namespaces, properties, methods are renamed to a much shorted name as possible as it can.

Do type checking at build-time, not run time.

Type-checking helps to reduce the unnecessary run-time type check. For example:

function User ( ) { } function UsersGroup ( ) { this . users_ = [ ] ; } UsersGroup . prototype . add = function ( user ) { if ( ! ( user instanceof User ) ) { throw new Error ( ' Only user can be added. ' ) ; } this . users_ . push ( user ) ; } ; var me = new User ( ) ; var myGroup = new UsersGroup ( ) ; myGroup . add ( me ) ;

should be done in this way.

function User ( ) { } function UsersGroup ( ) { this . users_ = [ ] ; } UsersGroup . prototype . add = function ( user ) { this . users_ . push ( user ) ; } ;

Note that the data type of this.users_ is @type {Array.<User>} which means a list of User .

You should use meaningful data structures rather than treat everything as raw Object or primitives, which is very error-prone.

Use @enum

Sometimes you to want handle multiple states:

function Project ( status ) { this . status_ = status ; } Project . prototype . isBusy = function ( ) { switch ( this . status_ ) { case ' busy ' :; case ' super_busy ' : return true ; default : return false ; } } ; var p1 = new Project ( ' busy ' ) ; var p2 = new Project ( ' super_busy ' ) ; var p3 = new Project ( ' idle ' ) ; document . write ( p1 . isBusy ( ) . toString ( ) ) ; document . write ( p2 . isBusy ( ) . toString ( ) ) ; document . write ( p3 . isBusy ( ) . toString ( ) ) ;

and you really should consider using @enum :

function Project ( status ) { this . status_ = status ; } Project . Status = { BUSY : 0 , SUPER_BUSY : 1 , IDLE : 2 } ; Project . prototype . isBusy = function ( ) { switch ( this . status_ ) { case Project . Status . BUSY :; case Project . Status . SUPER_BUSY : return true ; default : return false ; } } ; var p1 = new Project ( Project . Status . BUSY ) ; var p2 = new Project ( Project . Status . SUPER_BUSY ) ; var p3 = new Project ( Project . Status . IDLE ) ; document . write ( p1 . isBusy ( ) . toString ( ) ) ; document . write ( p2 . isBusy ( ) . toString ( ) ) ; document . write ( p3 . isBusy ( ) . toString ( ) ) ;

and it’s compiled as:

function a ( b ) { this . a = b } function c ( b ) { switch ( b . a ) { case 0 : ; case 1 : return true ; default : return false } } var d = new a ( 1 ) , e = new a ( 2 ) ; document . write ( c ( new a ( 0 ) ) . toString ( ) ) ; document . write ( c ( d ) . toString ( ) ) ; document . write ( c ( e ) . toString ( ) ) ;

Note that the enum variables are replaced by primitive numbers. You pay zero cost of using enums and gain the benefits of having more maintainable code.

Use @define to enable/disable message logging.

Say that you want to log your some important messages for your Class, just like any careful programmer would do.

var Logger = { } ; Logger . ENABLED = true ; Logger . log = function ( args ) { if ( ! Logger . ENABLED ) { return ; } var console = window [ ' console ' ] ; if ( console ) { console [ ' log ' ] . apply ( console , arguments ) ; } } ; function User ( name ) { Logger . log ( ' New User ' , name ) ; } var me = new User ( ' me ' ) ;

The code gets compiled as

function b ( ) { var a = window . console ; a && a . log . apply ( a , arguments ) } new function ( a ) { b ( " New User " , a ) } ( " me " ) ;

Note that you can disable the logger by adding the flag –define Logger.ENABLED=false . Also it’s always safer to add the –jscomp_error unknownDefines flag to capture unknown @define variables, too.

java -jar compiler.jar \ --js src/demo.js \ --js_output_file compiled/demo.js \ --warning_level VERBOSE \ --formatting PRETTY_PRINT \ --jscomp_error accessControls \ --jscomp_error checkTypes \ --jscomp_error unknownDefines --define Logger.ENABLED=false --compilation_level ADVANCED_OPTIMIZATIONS;

By doing so, you’re allowed to build your code either with logger enable (for development) or completely let compiler strip all logger calls for production deployment.

Use Casting

The more typed the code is, the more code gets compressed. Sometimes you can do type-casting to convert a simple JSON Object into a known reference type. For example:

function UserModel ( ) { this . firstName = ' ' ; this . lastName = ' ' ; } function User ( firstName , lastName ) { this . fullName = firstName + ' ' + lastName ; } User . createFromUserModel = function ( model ) { return new User ( model . firstName , model . lastName ) ; } ; var data = ( { firstName : ' foo ' , lastName : ' bar ' } ) ; var user = User . createFromUserModel ( data ) ; document . write ( user . fullName ) ;

As expected, the model definition will be removed, the property firstName , lastName are renamed, too.

var a = { a : " foo " , c : " bar " } ; document . write ( ( new function ( b , c ) { this . b = b + " " + c } ( a . a , a . c ) ) . b ) ;

In the last example, casting a plain Object into a known type reference can provide more specific detail about the Object that you care about.

Similarly, jQuery 1.4 added a new API isPlainObject which actually tries to do type-checking at runtime, which I won’t recommend doing so if you have a compiler in hand since it appears to be a hard problem to solve in JS actually.

And more…

There are still many other things that you want to use, such as using @const for constant.

I strongly recommend you visit the official Closure Tools site to learn more about it.

Moreover, there is already a great book (and it’s the only one) ” Closure: The Definitive Guide ” that explains all the details of everything about the closure tools, you should definitively consider owning this book if you want to gain the full power of these tools.

Conclusion

I’ve been using Google Closure Compiler for more than two years, and it has deeply changed the way I think about JavaScript development.

In summary, here are several things that I’d like to share with you.

Enforce good and consistent code, style such as indentation, 80 or 120 characters width limits, …etc. If you are not sure about what styles you should use, please follow Google JavaScript Style Guide. Note you should ensure the readability and maintainability of your code. Write documentations that are both descriptive and informative. Sometimes you just need to write more JSDocs than the code itself. Focus on good object-oriented design, algorithm, and data-structure. Don’t waster your time on micro optimizing your code or using any ninja technique that is not easy to read or confusing. Write code fast and frequently, and build it when you’re ready to ship it. There’s nothing wrong to use a large JavaScript library as long as you can build your code with the compiler. You may be surprised that you actually get less code than using a much smaller library without compilation. Ensure that your code is compilable by Closure Compiler in ADVANCED_OPTIMIZATIONS mode. Doing so ensures much higher quality of your code and also make it easier to integrate your code with other existing JS code which is also compiler-compatible.

Thanks

Last but not least important. The names of the people that I mentioned in this post represents those of the best known JavaScript developers and evangelists that I and most people recognize. Kudos to all of them and you, and cheers for JavaScript developers.