運用Closure Compiler 打造高品質的JavaScript Hedger 王璽
前端工程師 Yahoo! 2004 - 2008 Google 2008 - Present
適用場合 Complex JS Application Object-Oriented JavaScript
面向對象的 JavaScript
困境
缺乏類別 Class
// 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.
缺乏私有 @private
// Crockford's approach. function User(name) { var _name = name; // Getter for the private name. this.getName = function() { return _name; }; }
缺乏包 Package
// Global is evil. var myProjectDemoUserName = 'foo'; // Meeh... my.project.demo.user.Name = 'foo';
缺乏伸縮性 scalability
jQuery MooTool prototype script.aculo.us YUI ExtJS Dojo Closure
Functional OO? or var p1 = new Person('John'); var p2 = new Person('Jane'); var people = new People(); people.add(p1); people.add(p2); document.write(people.getCount());
<script src="http://yui.yahooapis.com/2.8.2r1/build/dom/dom-min.js"></script> // YAHOO.util.DragDropMgr /** * Returns the specified element style property * @method getStyle * @param {HTMLElement} el the element * @param {string} styleProp the style property * @return {string} The value of the style property * @deprecated use YAHOO.util.Dom.getStyle * @static */ getStyle: function(el, styleProp) { return YAHOO.util.Dom.getStyle(el, styleProp); },
人人都是 JavaScript 忍者
if (!('a' in window)) { var a = 1; } alert(a);
Closure Compiler
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.
@constructor
/** * @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.
@private
// File demo1.js // File demo2.js /** // Create a user. * A User. var me = new User(); * @constructor */ // Print out its birth year. function User() { document.write(me. getBirthYear().toString()); /** * The creation date. // Error * @private document.write(me. * @type {Date} _birthDay.toString()); */ this._birthDay = new Date(); } /** * @return {number} The creation year. */ User.prototype.getBirthYear = function() { return this._birthDay.getYear(); };
@extend
/** * The shape * @constructor */ function Shape() { } /** * The Box. * @constructor * @extends {Shape} */ function Box()) { }
@interface @implements
/** * The shape * @constructor */ function Shape() { } /** @return {number} */ Shape.prototype.getSize = function() {}; /** * The Box. * @constructor * @imnplements {Shape} */ function Box()) { } /** @inheritDoc */ Box.prototype.getSize = function() {return 0;};
命名空 package & namespace
// 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); };
STRICT type checking 嚴格的類型檢查
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);
/** * @constructor */ function User() { } /** * @constructor */ function UsersGroup() { /** * @private * @type {Array.<User>} */ this.users_ = []; } /** * @param {User} user */ UsersGroup.prototype.add = function(user) { this.users_.push(user); };
@enum
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());
/** * @constructor * @param {Project.Status} status */ function Project(status) { /** /** * @return {boolean} * @type {Project.Status} */ * @private Project.prototype.isBusy = function() { */ switch (this.status_) { this.status_ = status; case Project.Status.BUSY:; } case Project.Status.SUPER_BUSY: return true; /** default: * @enum {number} return false; */ } Project.Status = { }; BUSY: 0, SUPER_BUSY: 1, var p1 = new Project(Project.Status.BUSY); IDLE: 2 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());
@define
/** * namespace for the Logger. */ var Logger = {}; /** * Whether logging should be * enabled. /** * @define {boolean} * A User. */ * @param {string} name Logger.ENABLED = true; * @constructor */ /** function LoggerUser(name) { * the log API. Logger.log('New User', name); * @param {...*} args } */ Logger.log = function(args) { var me = new LoggerUser('me'); if (!Logger.ENABLED) { // Don't do anything if logger is disabled. return; } var console = window['console']; if (console) { console['log'].apply(console, arguments); } };
@casting
/** /** * The Model definition. * A static method that creates a * @constructor * User from a model. */ * @param {UserModel} model function UserModel() { * @return {User} The user created. /** */ * @type {string} User.createFromUserModel = */ function(model) { this.firstName = ''; return new User( model.firstName, /** model.lastName); * @type {string} }; */ this.lastName = ''; // Cast a simple JSON Object as } // {UserModel}. /** var data = /** @type {UserModel} */({ * The User constructor. firstName : 'foo', * @constructor lastName : 'bar' * @param {string} firstName }); * @param {string} lastName */ // Create a user from the model. function User(firstName, var user = User.createFromUserModel(data); lastName) { document.write(user.fullName); /** * @type {string} */ this.fullName = firstName + ' ' + lastName; }
總結
良好的代碼風格 可讀性 / 可維護性 / 可用性 Developer, IDE, Compiler, JSDocBuilder
詳細合理的註釋 progressive enhancemen
良好的開發週期 開發/測試/發佈
保持嚴謹
展望
JavaScript Everywhere PS3 . iPhone . Android ChromeOS . WebOS
JavaScript Everything Mobile App . Game . RIA
謝謝 大家

運用Closure Compiler 打造高品質的JavaScript

  • 1.
  • 2.
    前端工程師 Yahoo! 2004- 2008 Google 2008 - Present
  • 3.
    適用場合 ComplexJS Application Object-Oriented JavaScript
  • 4.
  • 5.
  • 6.
  • 7.
    // John Resig'sApproach. 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.
  • 8.
  • 9.
    // Crockford's approach. functionUser(name) { var _name = name; // Getter for the private name. this.getName = function() { return _name; }; }
  • 10.
  • 11.
    // Global isevil. var myProjectDemoUserName = 'foo'; // Meeh... my.project.demo.user.Name = 'foo';
  • 12.
  • 13.
    jQuery MooTool prototype script.aculo.us YUI ExtJS Dojo Closure
  • 14.
    Functional OO? or var p1 = new Person('John'); var p2 = new Person('Jane'); var people = new People(); people.add(p1); people.add(p2); document.write(people.getCount());
  • 15.
    <script src="http://yui.yahooapis.com/2.8.2r1/build/dom/dom-min.js"></script> // YAHOO.util.DragDropMgr /** * Returns the specified element style property * @method getStyle * @param {HTMLElement} el the element * @param {string} styleProp the style property * @return {string} The value of the style property * @deprecated use YAHOO.util.Dom.getStyle * @static */ getStyle: function(el, styleProp) { return YAHOO.util.Dom.getStyle(el, styleProp); },
  • 16.
  • 17.
    if (!('a' inwindow)) { var a = 1; } alert(a);
  • 18.
  • 19.
    It is atrue 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.
  • 20.
  • 21.
    /** * @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.
  • 22.
  • 23.
    // File demo1.js // File demo2.js /** // Create a user. * A User. var me = new User(); * @constructor */ // Print out its birth year. function User() { document.write(me. getBirthYear().toString()); /** * The creation date. // Error * @private document.write(me. * @type {Date} _birthDay.toString()); */ this._birthDay = new Date(); } /** * @return {number} The creation year. */ User.prototype.getBirthYear = function() { return this._birthDay.getYear(); };
  • 24.
  • 25.
    /** * Theshape * @constructor */ function Shape() { } /** * The Box. * @constructor * @extends {Shape} */ function Box()) { }
  • 26.
  • 27.
    /** * Theshape * @constructor */ function Shape() { } /** @return {number} */ Shape.prototype.getSize = function() {}; /** * The Box. * @constructor * @imnplements {Shape} */ function Box()) { } /** @inheritDoc */ Box.prototype.getSize = function() {return 0;};
  • 28.
  • 29.
    // Create namespaces. vardemo = {}; 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); };
  • 30.
    STRICT type checking 嚴格的類型檢查
  • 31.
    function User() { } functionUsersGroup() { 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);
  • 32.
    /** * @constructor */ function User() { } /** * @constructor */ function UsersGroup() { /** * @private * @type {Array.<User>} */ this.users_ = []; } /** * @param {User} user */ UsersGroup.prototype.add = function(user) { this.users_.push(user); };
  • 33.
  • 34.
    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());
  • 35.
    /** * @constructor * @param {Project.Status} status */ function Project(status) { /** /** * @return {boolean} * @type {Project.Status} */ * @private Project.prototype.isBusy = function() { */ switch (this.status_) { this.status_ = status; case Project.Status.BUSY:; } case Project.Status.SUPER_BUSY: return true; /** default: * @enum {number} return false; */ } Project.Status = { }; BUSY: 0, SUPER_BUSY: 1, var p1 = new Project(Project.Status.BUSY); IDLE: 2 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());
  • 36.
  • 37.
    /** * namespacefor the Logger. */ var Logger = {}; /** * Whether logging should be * enabled. /** * @define {boolean} * A User. */ * @param {string} name Logger.ENABLED = true; * @constructor */ /** function LoggerUser(name) { * the log API. Logger.log('New User', name); * @param {...*} args } */ Logger.log = function(args) { var me = new LoggerUser('me'); if (!Logger.ENABLED) { // Don't do anything if logger is disabled. return; } var console = window['console']; if (console) { console['log'].apply(console, arguments); } };
  • 38.
  • 39.
    /** /** * The Model definition. * A static method that creates a * @constructor * User from a model. */ * @param {UserModel} model function UserModel() { * @return {User} The user created. /** */ * @type {string} User.createFromUserModel = */ function(model) { this.firstName = ''; return new User( model.firstName, /** model.lastName); * @type {string} }; */ this.lastName = ''; // Cast a simple JSON Object as } // {UserModel}. /** var data = /** @type {UserModel} */({ * The User constructor. firstName : 'foo', * @constructor lastName : 'bar' * @param {string} firstName }); * @param {string} lastName */ // Create a user from the model. function User(firstName, var user = User.createFromUserModel(data); lastName) { document.write(user.fullName); /** * @type {string} */ this.fullName = firstName + ' ' + lastName; }
  • 40.
  • 41.
    良好的代碼風格 可讀性 / 可維護性/ 可用性 Developer, IDE, Compiler, JSDocBuilder
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
    JavaScript Everywhere PS3. iPhone . Android ChromeOS . WebOS
  • 47.
    JavaScript Everything Mobile App . Game . RIA
  • 48.