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.

// John Resig's Approach.
function User(name){
  if ( !(this instanceof User) )
    return new User(name);
  this.name = name;
}

var userA = new User('John'); // An instance.
var userB = User('Jane'); // Also an instance.

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.

// Crockford's approach.
function User(name) {
  var _name = name;

  // Getter for the private 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.

// Global is evil.
var myProjectDemoUserName = 'foo';
// Meeh...
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.

/**
 * @constructor
 */
function MyClass() {
}

// Pass.
var obj1 = new MyClass();

// ERROR: Constructor function (this:MyClass):
// class should be called with the "new" keyword.
var obj2 = MyClass(); // Error.

Use @private for access control.

// File demo1.js //

/**
 * A User.
 * @constructor
 */
function User() {

  /**
   * The creation date.
   * @private
   * @type {Date}
   */
  this._birthDay = new Date();
}

/**
 * @return {number} The creation year.
 */
User.prototype.getBirthYear = function() {
  return this._birthDay.getYear();
};

// File demo2.js //

// Create a user.
var me = new User();

// Print out its birth year.
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

/**
 * Helper function that implements (pseudo)Classical inheritance inheritance.
 * @see http://www.yuiblog.com/blog/2010/01/06/inheritance-patterns-in-yui-3/
 * @param {Function} childClass
 * @param {Function} parentClass
 */
function inherits(childClass, parentClass) {
  /** @constructor */
  var tempClass = function() {
  };
  tempClass.prototype = parentClass.prototype;
  childClass.prototype = new tempClass();
  childClass.prototype.constructor = childClass;
}

//////////////////////////////////////////////////////////////////////////////

/**
 * The shape
 * @constructor
 */
function Shape() {
  // No implementation.
}

/**
 * Get the size
 * @return {number} The size.
 */
Shape.prototype.getSize = function() {
  // No implementation.
};

//////////////////////////////////////////////////////////////////////////////

/**
 * The Box.
 * @param {number} width The width.
 * @param {number} height The height.
 * @constructor
 * @extends {Shape}
 */
function Box(width, height) {
  Shape.call(this);

  /**
   * @private
   * @type {number}
   */
  this.width_ = width;

  /**
   * @private
   * @type {number}
   */
  this.height_ = height;
}
inherits(Box, Shape);

/**
 * @return {number} The width.
 */
Box.prototype.getWidth = function() {
  return this.width_;
};

/**
 * @return {number} The height.
 */
Box.prototype.getHeight = function() {
  return this.height_;
};

/** @inheritDoc */
Box.prototype.getSize = function() {
  return this.height_ * this.width_;
};

////////////////////////////////////////////////////////////////////////////

/**
 * The Box.
 * @param {number} width The width.
 * @param {number} height The height.
 * @param {number} depth The depth.
 * @constructor
 * @extends {Box}
 */
function Cube(width, height, depth) {
  Box.call(this, width, height);

  /**
   * @private
   * @type {number}
   */
  this.depth_ = depth;
}
inherits(Cube, Box);

/**
 * @return {number} The width.
 */
Cube.prototype.getDepth = function() {
  return this.depth_;
};

/** @inheritDoc */
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.

// skip example code.

////////////////////////////////////////////////////////////////////////////

/**
 * The shape
 * @interface
 */
function Shape() {
}

/**
 * Get the size
 * @return {number} The size.
 */
Shape.prototype.getSize = function() {};

////////////////////////////////////////////////////////////////////////////

/**
 * The Box.
 * @param {number} width The width.
 * @param {number} height The height.
 * @constructor
 * @implements {Shape}
 */
function Box(width, height) {
  Shape.call(this);

  /**
   * @private
   * @type {number}
   */
  this.width_ = width;

  /**
   * @private
   * @type {number}
   */
  this.height_ = height;
}

/**
 * @return {number} The width.
 */
Box.prototype.getWidth = function() {
  return this.width_;
};

// skip example code.

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.

// Create namespaces.
var demo = {};
demo.example = {};
demo.example.exercise = {};

/**
 * @constructor
 */
demo.example.exercise.Foo = function() {
  demo.example.exercise.Foo.print(this.value1);
  demo.example.exercise.Foo.print(this.value2);
};

/**
 * Static method
 * @param {string} str String to print.
 */
demo.example.exercise.Foo.print = function(str) {
  document.write(str);
};

/**
 * @type {string}
 */
demo.example.exercise.Foo.prototype.value1 = 'abc';

/**
 * @type {string}
 */
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) {
  // Make sure that only user can be added.
  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.

/**
 * @constructor
 */
function User() {
}

/**
 * @constructor
 */
function UsersGroup() {
  /**
   * @private
   * @type {Array.<User>}
   */
  this.users_ = [];
}

/**
* @param {User} user
*/
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:

/**
 * @constructor
 * @param {Project.Status} status
 */
function Project(status) {
  /**
   * @type {Project.Status}
   * @private
   */
  this.status_ = status;
}

/**
 * @enum {number}
 */
Project.Status = {
  BUSY: 0,
  SUPER_BUSY: 1,
  IDLE: 2
};

/**
 * @return {boolean}
 */
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.

/**
 * namespace for the Logger.
 */
var Logger = {};

/**
 * Whether logging should be enabled.
 * @define {boolean}
 */
Logger.ENABLED = true;

/**
 * the log API.
 * @param {...*} args
 */
Logger.log = function(args) {
  if (!Logger.ENABLED) {
    // Don't do anything if logger is disabled.
    return;
  }
  var console = window['console'];
  if (console) {
    console['log'].apply(console, arguments);
  }
};

/**
 * A User.
 * @param {string} name
 * @constructor
 */
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:

/**
 * The Model definition.
 * @constructor
 */
function UserModel() {
  /**
   * @type {string}
   */
  this.firstName = '';

  /**
   * @type {string}
   */
  this.lastName = '';
}

/////////////////////////////////////////////////////////////////////////////

/**
 * The User constructor.
 * @constructor
 * @param {string} firstName
 * @param {string} lastName
 */
function User(firstName, lastName) {
  /**
   * @type {string}
   */
  this.fullName = firstName + ' ' + lastName;
}

/**
 * A static method that creates a User from a model.
 * @param {UserModel} model
 * @return {User} The user created.
 */
User.createFromUserModel = function(model) {
  return new User(model.firstName, model.lastName);
};

/////////////////////////////////////////////////////////////////////////////

// Cast a simple JSON Object as {UserModel}.
var data = /** @type {UserModel} */({
  firstName : 'foo',
  lastName : 'bar'
});

// Create a user from the model.
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.

  1. 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.

  2. Write documentations that are both descriptive and informative. Sometimes you just need to write more JSDocs than the code itself.

  3. 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.

  4. Write code fast and frequently, and build it when you’re ready to ship it.

  5. 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.

  6. 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.

ABOUT THE AUTHOR
Hedger Wang photo

Hedger Wang
HTML/CSS/JS Fanboy.

Hedger Wang used to work on Yahoo! as Frontend Tech Lead and now he works for Google and continue working on several big projects as UI Developer.

36 Responses to “Coding Better Object-Oriented JavaScript with Closure Compiler”

  1. Tweets that mention Performance Calendar » Coding Better Object-Oriented JavaScript with Closure Compiler -- Topsy.com

    [...] This post was mentioned on Twitter by Stoyan Stefanov and 马士华, François Dussert. François Dussert said: RT @stoyanstefanov: advent perf day 10: Hedger Wang on Closure Compiler http://perfplanet.com/201010 /cc @mimiz33 look at that :) [...]

  2. Peter van der Zee

    Nice overview. I liked the way you displayed common pain points in js and the advice/examples given for GCL is great. Good job :)

  3. Iouri Goussev

    Hi, very interesting article. Looks like an elegant approach to add type enforcement to a dynamic language. I’m a little worried about debugging the code compiled by closure. I wonder if it would be possible to attach debug symbols to make debugger show original function and variable names.

  4. jpvincent

    hi
    great article, I’ll take a 2nd look à Closure now
    however one thing really bother me : you are basically suggesting that we should adapt the coding style to the Closure Compiler tool
    What if tomorrow a better tool is released ?
    I can accept to adapt my OO knowledge to JS since it’s the language itself, but to a tool ?

  5. JavaScript Magazine Blog for JSMag » Blog Archive » News roundup: Crankshaft, WebSockets disabled, 3d Christmas tree

    [...] using sprites in an efficient way capuchin: A JavaScript implementation running on the Rubinius VM Coding Better Object-Oriented JavaScript with Closure Compiler Animating Isosurfaces with WebGL and Workers Share and [...]

  6. Hedger

    RE: Iouri Goussev

    You may use Closure Inspector to help to debug the compiled code.
    http://code.google.com/closure/compiler/docs/inspector.html

    I usually don’t use that this because I prefer using unit tests to catch any potential run-time errors.

  7. Hedger

    RE: jpvincent

    It’s actually JSDoc style (see http://jsdoc.sourceforge.net/)
    and Closure Compiler happens to be the tool that can understand this style.

    There are also many other tools that also understand this style and you can do many cool things with those tools.

  8. Joel Webber

    @jpvincent:

    I would submit that the “style” in question could be more accurately described as a derivative language of Javascript. The added syntax is buried in comments in order to make it compatible with direct execution in the browser (before the compiler processes it), but it’s a different language nonetheless.

    When the advanced optimizations are turned on, it also alters the semantics of the language somewhat, in that certain things that would be legal in raw Javascript will be disallowed by the compiler (which is why I would refer to it as a derivative language, rather than a superset).

    It’s a bit verbose, but a lot of the work you do to add these comments arguably makes your code readable in the long run. And many of the optimizations that the compiler performs are provably impossible for raw Javascript without these constraints in place. For large-scale application development, I believe the tradeoff’s worth it.

  9. Hedger

    @jpvincent

    Once more good thing that I did not mention is that some IDEs (Injellij, PyCharm, …etc) are pretty good with understanding JSDoc.

    If you document your code carefully, it really helps a lot for your productivity since your IDE and help you to a lot of type-based code references and fixes.

  10. Amit Agarwal

    Great post! It made me know lots of things about Google closure compiler. One thing which raises concern is that, doesn’t it make my code heavily dependent on Closure Compiler? There are no optimization things writting in code, its all in comments which are used by closure compiler. What if my team-mates don’t understand some of the Closure compiler specific comments and they delete them? What if some time I need to use my code(developer mode) without passing that through closure compiler, I gain zero optimization. Because all the magic is there in comments only.

  11. John Lenz

    @Louri Goussev Generally, people debug using the uncompiled code but for problems that old appear in the compiled code the the compiler’s “debug” and “pretty print” options get you a long way. The Closure Inspector is also handy but takes a little more effort to get setup.

  12. Jay Young

    @Amit

    “doesn’t it make my code heavily dependent on the Closure Compiler?”

    Not really, no. It is an explicit design goal that code written for the Closure Compiler (and Library) be runnable in un-compiled form. You can do this without much hassle. In fact, you should be developing with un-compiled code, and then compiling as part of a build process.

    For projects of any significant size, if you care about your user experience, you will be dependent on a build tool, whether it’s the Closure Compiler or something else. If you’re writing a complex front-end application, you’re going to be splitting code into multiple files and serving code like that in production is a recipe for awful performance.

    “There are no optimization things written in code”

    There’s nothing stopping you from optimizing your code. The compiler will only make it better by performing optimizations that are, for all practical purposes, impossible to do yourself.

    “What if my team-mates don’t understand some of the Closure compiler comments and they delete them?”

    The JSDoc style comments are very distinct. It’s obvious that they are their own language and have specific meaning, even if you don’t know what that meaning is. I would hope your teammates are curious enough to ask what those meanings are, and how they should be using them in their own code.

    “What if some time I need to use my code(developer mode) without passing that through closure compiler, I gain zero optimization.”

    That’s going to be the case no matter what tool you don’t run your code through, isn’t it?

    The whole Closure Tool suite is fantastic. As Michael Bolin says in Closure: The Definitive Guide: “Indeed, learning Closure will change the way you develop Javascript applications.” The Closure tools force good coding habits and performance best practices.

  13. Kartik Sehgal

    This is a great article. Although we do not use the google closure compiler (as yet), we will definitely study it for use in future. We do actually practice most of the things you have mentioned regarding coding style. One thing that you did not mention, or it probably skipped my eyes, is handling of DOM elements, binding events with functions inside your classes. Does google closure compiler unbind such events and prevent memory leaks automatically for such situations? I am talking about creating a function inside a javascript class and then assigning it to the event of a DOM object. The object will remain in memory till the DOM object is destroyed. I asked because you seemed to have a lot of experience on Google Closure Compiler and JavaScript in general.

  14. John Lenz

    @Kartik
    The Closure Compiler does not attempt this.

  15. Adam

    Compiler is a good tool for optimizing the javascript code and size, but I think if I rely on it

  16. Adam

    I feel it will be a little not-convenient for developing.

  17. Jorge

    Wtf, console['log'].apply(console, arguments); ?
    Wtf, if (!this instanceof Constructor) return new Constructor() ?

    What about prototype.isPrototypeOf(this) ?

    Breaking news: in this non-classical, prototypal language, objects have prototypes, not classes.

    Neither new nor instanceof are “good parts”… they’re not failsafe (not in JS), new Constructor() / Constructor.prototype / instanceof are brittle mechanisms, a bad part of JS, a last-minute add-on to make it look classical, to make it look like Java (back then, it seemed a good idea), but they’re -plainly- a big mistake, prototypically speaking.

    Anyway, the whole thing is amazing, imho. You feed cute JS source code through an end, and get incomprehensible garbage shit on the other. Amazing. Truly amazing. A truly amazing *nightmare*.

    You could very well rename it the google-code-JS-SINK+TRITURATOR.

    No sane, proficient, proud JS coder would ever pass a single LOC through it.

    [ in-my-humble-opinion ]

    Cheers,

    Jorge.

  18. Dmitry Baranovskiy

    Nice advertisement. I would rather hear opinion from somebody not related to Google.

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

    Thank you for linking to my blog, but this wasn’t an example of how to write your code. I am sure you got it, but still corrupt the meaning. Anyway. I hear your sarcasm about JS “ninjas”, but it is not about becoming “unnaturally cool” about JS — it’s about becoming simply competent about it.

    Google Closure Compiler isn’t a salvation if you are incompetent.

    JavaScript is much simpler than ninjitsu, so learn it or GTFO!

  19. Hedger

    @Jorge

    Agree.

    There are too many insane JS coders that I know are in Google are still writing JS codes for crazy stuffs :-)

  20. Stephen Woods

    I think a lot of this is good practice (clean, machine readable comments). However, I disagree with this:

    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.

    JavaScript is a functional/prototypal language, and, at least for me, this is a real advantage. There is a reason node.js is not written in java…functional programming is great for writing concurrent-ish code without threads. Which is why this approach was chosen for the browser, where non-linear code paths make closures and first class functions essential. Its totally possible to right maintainable code without using a classical model.

    Now, I don’t object to the fundamental idea here: using a build tool to enforce coding conventions like “_private” so you don’t need to write a lot of defensive type checking code. However I think there is a real risk here of premature optimization. JavaScript is eventually compiled, by the interpreter. That is the best place for runtime optimization, rather than adding a layer in the form of a preprocessor that rewrites your code making certain assumptions about what will be the best way to do things.

    I guess my objection to the article is that rather than just write javascript, you have decided to use a derived language that is kind of like javascript, then use a tool to turn that in to javascript. Ultimately this is pretty similar to GWT without the framework. Not an evil idea, but not a panacea, imho.

  21. Jorge

    @Hedger

    Sure, there must be many excellent JS programmers @ google, no doubt about it.

    And I can understand why Google wants to serve incomprehensible, obfuscated, “optimized” JS, as it comes out of this google-closure-compiler thing.

    For Google such a thing may represent an “optimization”, but perhaps for most of the rest of us not, much rather the opposite.

    None of which has anything to do with “coding better object-oriented JavaScript”, it seems to me.

  22. » 第五次D2-关注在前端基础架构 Kejun’s Blog

    [...] Hedger分享的“打造高品质的Javascript-运用Closure Compiler”,完全扭转了我对这个工具的理解。Closure Compiler从名字上看就知道是Compiler,Compressor仅仅是它的功能之一。所以YUI Compressor和Closure Compiler没有可比性。目前JSLint和Closure Linter都是不错的代码风格校验工具。Closure Compiler则弥补了它们在检查语法规则上的不足。比如对私有变量访问的控制,泛型检查>太酷了。Javascript本身是一个弱类型,松散的语言,Closure Compiler把它变成一个强类型,严谨的语言。真是神器。 更多请见这里: http://calendar.perfplanet.com/2010/coding-better-object-oriented-javascript-with-closure-compiler/ [...]

  23. Shawn

    hi,请假您一个问题。google的jsdoc规范里把jstag的@example这个tag去掉的考虑是什么?

  24. Armagan Amcalar

    Thank you for this cool article, it’s been a “must read” for my team of JS developers.

    Guys, are you afraid that you will lose your jobs as JS ninjas? I bet you never used Closure Compiler, you were never part of a project that used a “build environment” and are just talking about it.

    It really does a wonderful job in a big project where there is actually a “build process”. Why do you worry about the output being obfuscated, or can’t you steal code?

    Once your unit tests are OK and your code is working both compiled and uncompiled, of course, every proud web site owner would want to see less bytes being served to the user. Why does it bother you that the production code on a site is obfuscated and unreadable, but very efficient and lightweight? Why do you want to serve MB’s of JS code to the user? And in the end, what is the production code to you, as a developer, if it works?

    If it’s one tenth the size, secure and saves you a lot of worries about code quality, then let it be obfuscated.

  25. dexbol » Blog Archive » Closure Compiler 注释

    [...] 参考:http://calendar.perfplanet.com/2010/coding-better-object-oriented-javascript-with-closure-compiler/ http://code.google.com/intl/zh-CN/closure/compiler/docs/js-for-compiler.html#tag-implements 2011 01 11 Uncategorized Comments(0) « 触发div的focus/blur事件 点击div的滚动条不能触发它的click事件 [...]

  26. Erik

    Do you know of an IDE that can be configured to auto-format code in compliance with the Google JavaScript Style Guide? Some of the requirements require very granular control that I haven’t been able to achieve in Eclipse, IntelliJ, etc. For instance, within block comments the @fileoverview description should wrap but not indent, while @param description should wrap but indent 4 spaces! Just curious if you’ve found any IDE’s that let you run a format command to conform with Google’s standards.

  27. Dave

    Thanks for a great article.

    Re: A derivative language of Javascript.

    Fantastic! I want choice! I want progress! I want to have my say!

    Whatever the benefits / pitfalls of JS, at the end of the day there is one and only one reason why I use it – it is built-in to every browser (or should I say, some approximation of the official specification is built-in to up-to-date mainstream browsers). If there is something I don’t like about the language, there is not much I can do but choke it down. I’m certainly not going to complain because there is a miniscule chance that my suggestion will be accepted, and that can only lead to even more years of bugs and IEncompatibility.

    I have absolutely no problem with having to compile code if it is reasonably quick, and I couldn’t care less whether the result is human-readable or not. If this offends you who are smarter than me – well, how about you teach me how to code as elegantly as you do, right after I get back from the bank.

    I would like to see Google go one step further and drop the requirement that my source code is runnable before compilation, and I would like to see others follow Google’s lead and take it further. Invent a completely new language, recycle some old ones, or add psuedo-key words to JS – as long as the compiled end result works on all the mainstream browsers, then I will check it out, submit my feedback, vote with my feet, and never bother the ninjas with my stupidity ever again.

  28. reelfernandes

    I’m sure you’ve seen the latest developments with ECMAScript5 which introduced the Access Control API. While not as clean as your traditional compiler access control private/public/override/internal/etc in terms of inheritance, you can create private properties & methods with Object.defineProperty().

    ECMAScript5 Getter/Setter

  29. 我为什么向后端工程师推荐NodeJS - node.js专业中文技术社区 - CNodeJS.ORG

    [...] 我之前谈到过JS静态编译器:“如果给JS代码发布正式使用前增加一个编译步骤,我们能做些什么”,动态语言的实时编译系统只完成了静态语言编译中的将代码转化为字节码的过程,而静态语言编译器的额外工作,如接口校验,全局性能优化等待.所以JS也需要一个静态的编译器来完成这些功能,Google利用ClouserComplier提供了系列编译指令,让JS更好的实现OO编程,我来利用静态编译器解决一些JS做细粒度模块化引入的性能方面的问题.而老赵最近的项目JSCEX,则也是利用JS发布前的编译环节重点解决异步编程的代码复杂度问题. 我们习惯于阻塞式编程的写法,切换到异步模式编程,往往对于太多多层次的callback嵌套弄得不知所措.所以老赵开发的JS静态编译器,借鉴F#的Computation Expressions,让大家遵守一些小的约定后,能够仍然保持同步编程的写法,写完的代码通过JSCEX编译为异步回调式的代码再交给JS引擎执行. 如果这个项目足够好用,那就也解决了一个使用NodeJS这种新技术,却加大编程复杂度这个额外引入的困扰.甚至可以沿着这个思路,在静态编译阶段优化内存使用. [...]

  30. Dave

    Have to revise my earlier comment a bit. Still like the idea, but after using Closure Compiler for a while, I am less enthusiastic.
    I had a type-casting bug in my JavaScript code. It took longer than it should have to track down because I assumed Closure Compiler had ruled out the possibility. Wrong!
    I had a mistake in a JSdoc comment. Closure Compiler silently ignored the bug in the comment, and therefore gave the bug in the code its stamp of approval.
    So, now I need a way to check for bugs in JSdoc comments. I think I might write something myself – in C++.

  31. Getting Started with Springboard - Rainy Day Media

    [...] Compiler which I tend to use both for it’s advanced code optimisations and for coding better Object-Oriented JavaScript (not to mention generating JavaScript documentation from the very same comments I write for the [...]

  32. 我为什么向后端工程师推荐NodeJS | 岭南六少 - 一朵在LAMP架构下挣扎的云

    [...] 我之前谈到过JS静态编译器:“如果给JS代码发布正式使用前增加一个编译步骤,我们能做些什么”,动态语言的实时编译系统只完成了静态语言编译中的将代码转化为字节码的过程,而静态语言编译器的额外工作,如接口校验,全局性能优化等待.所以JS也需要一个静态的编译器来完成这些功能,Google利用ClouserComplier提供了系列编译指令,让JS更好的实现OO编程,我来利用静态编译器解决一些JS做细粒度模块化引入的性能方面的问题.而老赵最近的项目JSCEX,则也是利用JS发布前的编译环节重点解决异步编程的代码复杂度问题. 我们习惯于阻塞式编程的写法,切换到异步模式编程,往往对于太多多层次的callback嵌套弄得不知所措.所以老赵开发的JS静态编译器,借鉴F#的Computation Expressions,让大家遵守一些小的约定后,能够仍然保持同步编程的写法,写完的代码通过JSCEX编译为异步回调式的代码再交给JS引擎执行. 如果这个项目足够好用,那就也解决了一个使用NodeJS这种新技术,却加大编程复杂度这个额外引入的困扰.甚至可以沿着这个思路,在静态编译阶段优化内存使用. [...]

  33. {fotografia ślubna słupca|fotograf słupca|zdjęcia ślubne słupca}

    Pogląd nawet ciekawy, wpisuję stronę do zakładek i będę zaglądał częściej

  34. music education

    I have to express appreciation to this writer just for bailing me out of this type of issue. After browsing throughout the search engines and obtaining proposals that were not productive, I was thinking my life was gone. Living devoid of the strategies to the difficulties you’ve fixed by way of the guideline is a crucial case, and ones which could have badly affected my entire career if I had not discovered the blog. Your own personal natural talent and kindness in handling a lot of stuff was excellent. I’m not sure what I would have done if I had not come across such a solution like this. I am able to at this time relish my future. Thanks very much for the impressive and amazing help. I won’t think twice to propose your web site to anybody who should have assistance about this issue.

  35. Peter StJ

    I can see that this post is dated, however it dismays me that the contra argument your exposé starts with is never contradicted:

    “…and inheritance comes with an additional penalty.”

    Yet you miss to communicate how exactly does the compiler fights the run-time penalty. The prototype chain is kept exactly as deep as it was when the source was written. If I have 10 levels of inheritance in the source, the resulting code will still have 10 levels of inheritance, even if large portions of the inherited methods are stripped away. The run-time penalty for walking the prototype chain remains.

    The only real benefit the compiler provides for a seasoned javascript ninja is the ‘free’ type checking at compile time, everything else it does we already know and practice. This ‘free’ type checking comes at the cost of the excessive time spend writing type casting comments all over your code, even when it is clear from miles what the type is, you still need to tell it to the compiler.

    Dead code removal is only useful with extra large code bases, even that is only possible if the code is written only with the patterns the compiler can understands and there is no run-time penalty for code that is not executed, only the download and parse times are shortened which again is only important for really large code bases.

    Name space squashing is only there just because someone at google is more comfortable working with namespaces (as in java) tan with modules (as in amd for example) It has no real benefit for code that does not use name spaces and pretty much any other large code base does not use it.

    So basically you are telling the reader that closure compiler can help with OOP. Well, actually no, it cannot. It uses just one OOP pattern, only ONE of at least 4 possible. And it even cannot optimize for that one! For example if I Inherit method X from constructor A into Constructor B, the compiler does not move the X method in the prototype of B, even if it is never used in another inherited constructor or in the constructor prototype itself. Well that would have been cool if existed. But it does not. And thus the pattern is not the fastest or the most appropriate. It is just the one they decided they can operate with and support.

Leave a Reply

You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>
And here's a tool to convert HTML entities