我们使用 JSDoc 中的注释风格. 行内注释使用 // 变量 的形式. 另外, 我们也遵循 C++ 代码注释风格 . 这也就是说你需要:
- 版权和著作权的信息,
- 文件注释中应该写明该文件的基本信息(如, 这段代码的功能摘要, 如何使用, 与哪些东西相关), 来告诉那些不熟悉代码的读者.
- 类, 函数, 变量和必要的注释,
- 期望在哪些浏览器中执行,
- 正确的大小写, 标点和拼写.
为了避免出现句子片段, 请以合适的大/小写单词开头, 并以合适的标点符号结束这个句子.
现在假设维护这段代码的是一位初学者. 这可能正好是这样的!
目前很多编译器可从 JSDoc 中提取类型信息, 来对代码进行验证, 删除和压缩. 因此, 你很有必要去熟悉正确完整的 JSDoc .
顶层/文件注释
顶层注释用于告诉不熟悉这段代码的读者这个文件中包含哪些东西. 应该提供文件的大体内容, 它的作者, 依赖关系和兼容性信息. 如下:
// Copyright 2009 Google Inc. All Rights Reserved. /** * @fileoverview Description of file, its uses and information * about its dependencies. * @author user@google.com (Firstname Lastname) */
类注释
每个类的定义都要附带一份注释, 描述类的功能和用法. 也需要说明构造器参数. 如果该类继承自其它类, 应该使用 @extends
标记. 如果该类是对接口的实现, 应该使用 @implements
标记.
/** * Class making something fun and easy. * @param {string} arg1 An argument that makes this more interesting. * @param {Array.<number>} arg2 List of numbers to be processed. * @constructor * @extends {goog.Disposable} */ project.MyClass = function(arg1, arg2) { // ... }; goog.inherits(project.MyClass, goog.Disposable);
方法与函数的注释
提供参数的说明. 使用完整的句子, 并用第三人称来书写方法说明.
/** * Converts text to some completely different text. * @param {string} arg1 An argument that makes this more interesting. * @return {string} Some return value. */ project.MyClass.prototype.someMethod = function(arg1) { // ... }; /** * Operates on an instance of MyClass and returns something. * @param {project.MyClass} obj Instance of MyClass which leads to a long * comment that needs to be wrapped to two lines. * @return {boolean} Whether something occured. */ function PR_someMethod(obj) { // ... }
对于一些简单的, 不带参数的 getters, 说明可以忽略.
/** * @return {Element} The element for the component. */ goog.ui.Component.prototype.getElement = function() { return this.element_; };
属性注释
也需要对属性进行注释.
/** * Maximum number of things per pane. * @type {number} */ project.MyClass.prototype.someProperty = 4;
类型转换的注释
有时, 类型检查不能很准确地推断出表达式的类型, 所以应该给它添加类型标记注释来明确之, 并且必须在表达式和类型标签外面包裹括号.
/** @type {number} */ (x) (/** @type {number} */ x)
JSDoc 缩进
如果你在 @param
, @return
, @supported
, @this
或 @deprecated
中断行, 需要像在代码中一样, 使用4个空格作为一个缩进层次.
/** * Illustrates line wrapping for long param/return descriptions. * @param {string} foo This is a param with a description too long to fit in * one line. * @return {number} This returns something that has a description too long to * fit in one line. */ project.MyClass.prototype.method = function(foo) { return 5; };
不要在 @fileoverview
标记中进行缩进.
虽然不建议, 但也可对说明文字进行适当的排版对齐. 不过, 这样带来一些负面影响, 就是当你每次修改变量名时, 都得重新排版说明文字以保持和变量名对齐.
/** * This is NOT the preferred indentation method. * @param {string} foo This is a param with a description too long to fit in * one line. * @return {number} This returns something that has a description too long to * fit in one line. */ project.MyClass.prototype.method = function(foo) { return 5; };
枚举
/** * Enum for tri-state values. * @enum {number} */ project.TriState = { TRUE: 1, FALSE: -1, MAYBE: 0 };
注意一下, 枚举也具有有效类型, 所以可以当成参数类型来用.
/** * Sets project state. * @param {project.TriState} state New project state. */ project.setState = function(state) { // ... };
Typedefs
有时类型会很复杂. 比如下面的函数, 接收 Element 参数:
/** * @param {string} tagName * @param {(string|Element|Text|Array.<Element>|Array.<Text>)} contents * @return {Element} */ goog.createElement = function(tagName, contents) { ... };
你可以使用 @typedef
标记来定义个常用的类型表达式.
/** @typedef {(string|Element|Text|Array.<Element>|Array.<Text>)} */ goog.ElementContent; /** * @param {string} tagName * @param {goog.ElementContent} contents * @return {Element} */ goog.createElement = function(tagName, contents) { ... };
JSDoc 标记表
标记 | 模板 & 例子 | 描述 | 类型检测支持 |
---|
@param | @param {Type} 变量名 描述 如: /** * Queries a Baz for items. * @param {number} groupNum Subgroup id to query. * @param {string|number|null} term An itemName, * or itemId, or null to search everything. */ goog.Baz.prototype.query = function(groupNum, term) { // ... }; | 给方法, 函数, 构造器中的参数添加说明. | 完全支持. |
@return | @return {Type} 描述 如: /** * @return {string} The hex ID of the last item. */ goog.Baz.prototype.getLastId = function() { // ... return id; }; | 给方法, 函数的返回值添加说明. 在描述布尔型参数时, 用 "Whether the component is visible" 这种描述优于 "True if the component is visible, false otherwise". 如果函数没有返回值, 就不需要添加 @return 标记. | 完全支持. |
@author | @author username@google.com (first last) 如: /** * @fileoverview Utilities for handling textareas. * @author kuth@google.com (Uthur Pendragon) */ | 表明文件的作者, 通常仅会在 @fileoverview 注释中使用到它. | 不需要. |
@see | @see Link 如: /** * Adds a single item, recklessly. * @see #addSafely * @see goog.Collect * @see goog.RecklessAdder#add ... | 给出引用链接, 用于进一步查看函数/方法的相关细节. | 不需要. |
@fileoverview | @fileoverview 描述 如: /** * @fileoverview Utilities for doing things that require this very long * but not indented comment. * @author kuth@google.com (Uthur Pendragon) */ | 文件通览. | 不需要. |
@constructor | @constructor 如: /** * A rectangle. * @constructor */ function GM_Rect() { ... } | 指明类中的构造器. | 会检查. 如果省略了, 编译器将禁止实例化. |
@interface | @interface 如: /** * A shape. * @interface */ function Shape() {}; Shape.prototype.draw = function() {}; /** * A polygon. * @interface * @extends {Shape} */ function Polygon() {}; Polygon.prototype.getSides = function() {}; | 指明这个函数是一个接口. | 会检查. 如果实例化一个接口, 编译器会警告. |
@type | @type Type @type {Type}如: /** * The message hex ID. * @type {string} */ var hexId = hexId; | 标识变量, 属性或表达式的类型. 大多数类型是不需要加大括号的, 但为了保持一致, 建议统一加大括号. | 会检查 |
@extends | @extends Type @extends {Type}如: /** * Immutable empty node list. * @constructor * @extends goog.ds.BasicNodeList */ goog.ds.EmptyNodeList = function() { ... }; | 与 @constructor 一起使用, 用来表明该类是扩展自其它类的. 类型外的大括号可写可不写. | 会检查 |
@implements | @implements Type @implements {Type}如: /** * A shape. * @interface */ function Shape() {}; Shape.prototype.draw = function() {}; /** * @constructor * @implements {Shape} */ function Square() {}; Square.prototype.draw = function() { ... }; | 与 @constructor 一起使用, 用来表明该类实现自一个接口. 类型外的大括号可写可不写. | 会检查. 如果接口不完整, 编译器会警告. |
@lends | @lends objectName @lends {objectName}如: goog.object.extend( Button.prototype, /** @lends {Button.prototype} */ { isButton: function() { return true; } }); | 表示把对象的键看成是其他对象的属性. 该标记只能出现在对象语法中. 注意, 括号中的名称和其他标记中的类型名称不一样, 它是一个对象名, 以"借过来"的属性名命名. 如, @type {Foo} 表示 "Foo 的一个实例", but @lends {Foo} 表示 "Foo 构造器". 更多有关此标记的内容见 JSDoc Toolkit docs. | 会检查 |
@private | @private 如: /** * Handlers that are listening to this logger. * @type Array.<Function> * @private */ this.handlers_ = []; | 指明那些以下划线结尾的方法和属性是 私有的. 不推荐使用后缀下划线, 而应改用 @private. | 需要指定标志来开启. |
@protected | @protected 如: /** * Sets the component's root element to the given element. Considered * protected and final. * @param {Element} element Root element for the component. * @protected */ goog.ui.Component.prototype.setElementInternal = function(element) { // ... }; | 指明接下来的方法和属性是 被保护的. 被保护的方法和属性的命名不需要以下划线结尾, 和普通变量名没区别. | 需要指定标志来开启. |
@this | @this Type @this {Type}如: pinto.chat.RosterWidget.extern('getRosterElement', /** * Returns the roster widget element. * @this pinto.chat.RosterWidget * @return {Element} */ function() { return this.getWrappedComponent_().getElement(); }); | 指明调用这个方法时, 需要在哪个上下文中. 当 this 指向的不是原型方法的函数时必须使用这个标记. | 会检查 |
@supported | @supported 描述 如: /** * @fileoverview Event Manager * Provides an abstracted interface to the * browsers' event systems. * @supported So far tested in IE6 and FF1.5 */ | 在文件概述中用到, 表明支持哪些浏览器. | 不需要. |
@enum | @enum {Type} 如: /** * Enum for tri-state values. * @enum {number} */ project.TriState = { TRUE: 1, FALSE: -1, MAYBE: 0 }; | 用于枚举类型. | 完全支持. 如果省略, 会认为是整型. |
@deprecated | @deprecated 描述 如: /** * Determines whether a node is a field. * @return {boolean} True if the contents of * the element are editable, but the element * itself is not. * @deprecated Use isField(). */ BN_EditUtil.isTopEditableField = function(node) { // ... }; | 告诉其他开发人员, 此方法, 函数已经过时, 不要再使用. 同时也会给出替代方法或函数. | 不需要 |
@override | @override 如: /** * @return {string} Human-readable representation of project.SubClass. * @override */ project.SubClass.prototype.toString() { // ... }; | 指明子类的方法和属性是故意隐藏了父类的方法和属性. 如果子类的方法和属性没有自己的文档, 就会继承父类的. | 会检查 |
@inheritDoc | @inheritDoc 如: /** @inheritDoc */ project.SubClass.prototype.toString() { // ... }; | 指明子类的方法和属性是故意隐藏了父类的方法和属性, 它们具有相同的文档. 注意: 使用 @inheritDoc 意味着也同时使用了 @override. | 会检查 |
@code | {@code ...} 如: /** * Moves to the next position in the selection. * Throws {@code goog.iter.StopIteration} when it * passes the end of the range. * @return {Node} The node at the next position. */ goog.dom.RangeIterator.prototype.next = function() { // ... }; | 说明这是一段代码, 让它能在生成的文档中正确的格式化. | 不适用. |
@license or@preserve | @license 描述 如: /** * @preserve Copyright 2009 SomeThirdParty. * Here is the full license text and copyright * notice for this file. Note that the notice can span several * lines and is only terminated by the closing star and slash: */ | 所有被标记为 @license 或 @preserve 的, 会被编译器保留不做任何修改而直接输出到最终文挡中. 这个标记让一些重要的信息(如法律许可或版权信息)原样保留, 同样, 文本中的换行也会被保留. | 不需要. |
@noalias | @noalias 如: /** @noalias */ function Range() {} | 在外部文件中使用, 告诉编译器不要为这个变量或函数重命名. | 不需要. |
@define | @define {Type} 描述 如: /** @define {boolean} */ var TR_FLAGS_ENABLE_DEBUG = true; /** @define {boolean} */ goog.userAgent.ASSUME_IE = false; | 表示该变量可在编译时被编译器重新赋值. 在上面例子中, BUILD 文件中指定了 --define='goog.userAgent.ASSUME_IE=true' 这个编译之后, 常量 goog.userAgent.ASSUME_IE 将被全部直接替换为 true. | 不需要. |
@export | @export 如: /** @export */ foo.MyPublicClass.prototype.myPublicMethod = function() { // ... }; | 上面的例子代码, 当编译器运行时指定 --generate_exports 标志, 会生成下面的代码: goog.exportSymbol('foo.MyPublicClass.prototype.myPublicMethod', foo.MyPublicClass.prototype.myPublicMethod); 编译后, 将源代码中的名字原样导出. 使用 @export 标记时, 应该 - 包含 //javascript/closure/base.js, 或者
- 在代码库中自定义 goog.exportSymbol 和 goog.exportProperty 两个方法, 并保证有相同的调用方式.
| 不需要. |
@const | @const 如: /** @const */ var MY_BEER = 'stout'; /** * My namespace's favorite kind of beer. * @const * @type {string} */ mynamespace.MY_BEER = 'stout'; /** @const */ MyClass.MY_BEER = 'stout'; | 声明变量为只读, 直接写在一行上. 如果其他代码中重写该变量值, 编译器会警告. 常量应全部用大写字符, 不过使用这个标记, 可以帮你消除命名上依赖. 虽然 jsdoc.org 上列出的 @final 标记作用等价于 @const , 但不建议使用. @const 与 JS1.5 中的 const 关键字一致. 注意, 编译器不禁止修改常量对象的属性(这与 C++ 中的常量定义不一样). 如果可以准确推测出常量类型的话, 那么类型申明可以忽略. 如果指定了类型, 应该也写在同一行上. 变量的额外注释可写可不写. | 支持. |
@nosideeffects | @nosideeffects 如: /** @nosideeffects */ function noSideEffectsFn1() { // ... }; /** @nosideeffects */ var noSideEffectsFn2 = function() { // ... }; /** @nosideeffects */ a.prototype.noSideEffectsFn3 = function() { // ... }; | 用于对函数或构造器声明, 说明调用此函数不会有副作用. 编译器遇到此标记时, 如果调用函数的返回值没有其他地方使用到, 则会将这个函数整个删除. | 不需要检查. |
@typedef | @typedef 如: /** @typedef {(string|number)} */ goog.NumberLike; /** @param {goog.NumberLike} x A number or a string. */ goog.readNumber = function(x) { ... } | 这个标记用于给一个复杂的类型取一个别名. | 会检查 |
@externs | @externs 如: /** * @fileoverview This is an externs file. * @externs */ var document; | 指明一个外部文件. | 不会检查 |
在第三方代码中, 你还会见到其他一些 JSDoc 标记. 这些标记在 JSDoc Toolkit Tag Reference 都有介绍到, 但在 Google 的代码中, 目前不推荐使用. 你可以认为这些是将来会用到的 "保留" 名. 它们包含:
- @augments
- @argument
- @borrows
- @class
- @constant
- @constructs
- @default
- @event
- @example
- @field
- @function
- @ignore
- @inner
- @link
- @memberOf
- @name
- @namespace
- @property
- @public
- @requires
- @returns
- @since
- @static
- @version
JSDoc 中的 HTML
类似于 JavaDoc, JSDoc 支持许多 HTML 标签, 如 <code>, <pre>, <tt>, <strong>, <ul>, <ol>, <li>, <a>, 等等.
这就是说 JSDoc 不会完全依照纯文本中书写的格式. 所以, 不要在 JSDoc 中, 使用空白字符来做格式化:
/** * Computes weight based on three factors: * items sent * items received * last timestamp */
上面的注释, 出来的结果是:
Computes weight based on three factors: items sent items received items received
应该这样写:
/** * Computes weight based on three factors: * <ul> * <li>items sent * <li>items received * <li>last timestamp * </ul> */
另外, 也不要包含任何 HTML 或类 HTML 标签, 除非你就想让它们解析成 HTML 标签.
/** * Changes <b> tags to <span> tags. */
出来的结果是:
Changes tags to tags.
另外, 也应该在源代码文件中让其他人更可读, 所以不要过于使用 HTML 标签:
/** * Changes <b> tags to <span> tags. */
上面的代码中, 其他人就很难知道你想干嘛, 直接改成下面的样子就清楚多了:
/** * Changes 'b' tags to 'span' tags. */