Annotation
Closure Compiler 的檢查、編譯皆基於 JSDoc Annotation,因此撰寫清楚的 Annotation 才能達到最有效的應用。
- Closure Compiler 並不完全符合 JSDoc 的規範,請參考 Google 的 Type 說明 及 Google 的 Annotation 說明
Data Type
Feature
- 變數, 屬性, 參數, 回傳須以註記以下 Annotation 協助 Closure Compiler 進行檢查; DATA_TYPE 為其類型
@type {DATA_TYPE}: 變數, 屬性類型為 DATA_TYPE@const {DATA_TYPE}: 變數, 屬性為 const; 類型為 DATA_TYPE@param {DATA_TYPE}: 參數類型為 DATA_TYPE@returns {DATA_TYPE}: 回傳類型為 DATA_TYPE@enum {DATA_TYPE}: 列舉的值為 DATA_TYPE
Setup
- Primitive Type: 為小寫單字 - null, undefined, boolean, number, string
- Instance Type: 為字首大寫單字 - Object, Array, Function, Type
Object: 內含任意屬性的 Object
Object.<DATA_TYPE1, DATA_TYPE2>: key 值類型為 DATA_TYPE1, value 值類型為 DATA_TYPE2 的 Object{key1:DATA_TYPE1}: 內含屬性 key1 的 Object, key1 值為 DATA_TYPE1 類型Array: 內含任意類型的 ArrayArray.<DATA_TYPE>: 指定類型的 ArrayFunction: 任意參數, 回傳的 Functionfunction(DATA_TYPE1):DATA_TYPE2: 需要 1 個 DATA_TYPE1類型的參數, 且回傳 DATA_TYPE2 的 Instancefunction(...DATA_TYPE): 能夠傳入任意個 DATA_TYPE 類型的參數
- 若可接受複數種類型, 則以
|分隔兩個 DATA_TYPE@type {DATA_TYPE=}: 等同於@type {DATA_TYPE|undefined}@typedef及@record不支援此種縮寫
@type {?DATA_TYPE}: 等同於@type {DATA_TYPE|null}- Primitive Type 不須另外註記 null 類型
@typedef及@record不支援此種縮寫
- 若 DATA_TYPE 為較為複雜的 Object, 可以
@typedef或@record定義@typedef: 以 Annotation 及變數宣告定義內含特定屬性的 Object, 可將變數作為 DATA_TYPE 填入 Annotation 使用 -@typedef無法跨檔案@record: 以 function 及 prototype 定義內含特定屬性的 Object, 可將 function 作為 DATA_TYPE 填入 Annotation 使用
- function 中可不填寫的參數需註記為包含 undefined 的複數類型
範例
/** @enum {number} */
const Gender = {
Female: 0,
Male: 1,
None: 3
}
/**
* @typedef {{firstName:string, lastName:(string|undefined)}}
*/
let Name;
/**
* @record
*/
function Persona () {}
/** @type {Name} */
Persona.prototype.name;
/** @type {Gender|undefined} */
Persona.prototype.gender;
/** @type {function():string} */
Persona.prototype.hello;
/**
* @param {Persona} persona
*/
function sayHello (persona) {
console.log(persona.name.firstName + ": " + persona.hello());
}
let john = {
name: {
firstName: "John"
},
gender: Gender.Male, //此處不能直接填寫數字 1
hello: function () {return "您好!";}
}
sayHello(john)
Function
Setup
- 須以
@param {DATA_TYPE} NAME註明參數的類型及名稱, 若無參數怎可省略- 若為選填, 則在 DATA_TYPE 需註記可為 undefined:
@param {DATA_TYPE|undefined} NAME,@param {DATA_TYPE?} NAME
- 若為選填, 則在 DATA_TYPE 需註記可為 undefined:
- 須以
@returns {TYPE}註明回傳的類型, 若無回傳怎可省略 - 若為內部使用的 function 需註記
@protected或@private
Type (Class)
Setup
- 作為 Type 使用的 function 需要註記
@constructor- ES6 class 寫法不需要添加此註記
- 需要註記
@returns {TYPE}, TYPE 為自身的名稱- ES6 class 寫法不需要添加此註記
- 若要繼承某 Type, 則需註記
@extends {TYPE}, TYPE 為繼承對象的名稱- ES6 class 寫法不需要添加此註記
- Type 僅能繼承 Type
- 若覆寫 Parent Type 的 function, 需註記
@override - constructor 不需註記
@protected或@private
範例:
//以下為 ES5 的範例
/**
* @constructor
* @param {string} id
* @param {string=} styleClass
* @returns {PHComponent}
*/
function PHComponent (id, styleClass) {
this.id = id;
this.styleClass = styleClass;
this.htmlElement = null;
}
PHComponent.prototype.init = function () {
this.htmlElement = this.doInit();
};
/**
* @protected
* @returns {Element}
*/
PHComponent.prototype.doInit = function () {};
/**
* @constructor
* @extends {PHComponent}
* @param {string} id
* @param {string=} styleClass
* @returns {PHButton}
*/
function PHButton (id, styleClass) {
PHComponent.call(this, id, styleClass);
}
/** @override */
PHButton.prototype.doInit = function () {
return document.createElement('button');
};
Protocol (Interface)
Feature
- 應用同個 Protocol, 編譯是會被視為同個 Type
- Protocol 所具備的屬性, function API, 皆須被定義在 prototype
- prototype 上的屬性皆需註記
@type {DATA_TYPE}, 且屬性不能被實作(賦值) - Protocol.prototype 上的每個屬性皆需被套用的 Type 於 constructor 中賦值
- constructor 中可以先賦值為 null, 此時屬性的 DATA_TYPE 尚不會被 Closure Compiler 檢查
Setup
- 作為 Protocol 使用的 function 需註記
@interface - 套用 Protocol 的 Type 需註記
@implements {PROTOCOL}, PROTOCOL 為 Protocol 的名稱 - Protocol 僅能繼承 Protocol, 需註記
@extends {PROTOCOL}
範例:
//以下為 ES5 的範例
/**
* @interface
*/
function PHComponent () {}
/** @type {string} */
PHComponent.prototype.id;
/** @type {string|undefined} */
PHComponent.prototype.styleClass;
/** @type {Element} */
PHComponent.prototype.htmlElement;
/** @type {Function} */
PHComponent.prototype.init;
/**
* @constructor
* @implements {PHComponent}
* @param {string} id
* @param {string=} styleClass
* @returns {PHButton}
*/
function PHButton (id, styleClass) {
this.id = id;
this.styleClass = styleClass;
this.htmlElement = null;
}
/** @override */
PHButton.prototype.init = function () {
this.htmlElement = document.createElement('button');
};
輸出 Symbols, 避免 Closure Compiler 更動變量/屬性名稱
- 註記
@export, 並將 base.js 列為--js的第一個檔案 - 使用指令
—-generate_exports true及--export_local_property_definitions true
Annotation 順序
/**
* @override
* @private ----------------> @protected / @private
* @constructor ------------> @type / @constructor / @interface
* @extends {TYPE} ---------> @extends / @implements
* @param {DATA_TYPE} NAME
* @returns {TYPE}
* @export
*/