運用Closure Compiler 打造高品質的JavaScript

Post on 25-Dec-2014

3.224 views 2 download

description

運用Closure Compiler 打造高品質的JavaScript Hedger 王璽

Transcript of 運用Closure Compiler 打造高品質的JavaScript

運用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

jQueryMooToolprototype

script.aculo.usYUI

ExtJSDojo

Closure

var p1 = new Person('John');var p2 = new Person('Jane');var people = new People();people.add(p1);people.add(p2);document.write(people.getCount());

Functional or OO?

<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

/** * 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());

// Errordocument.write(me. _birthDay.toString());

@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) { /** * @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());

@define

/** * 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 LoggerUser(name) { Logger.log('New User', name);}

var me = new LoggerUser('me');

@casting

/** * 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);

總結

良好的代碼風格可讀性 / 可維護性 / 可用性Developer, IDE, Compiler, JSDocBuilder

詳細合理的註釋progressive enhancemen

良好的開發週期開發/測試/發佈

保持嚴謹

展望

JavaScriptEverywhere

PS3 . iPhone . Android ChromeOS . WebOS

謝謝大家