你不会写注释? 快学一下!!!

896 阅读11分钟

1.注释的目的和原则

提高可读性和可维护性
如无必要,勿增注释;如有必要,尽量详尽

2.语法和快捷键

单行注释:// 快捷键: ctrl+/
多行注释:/**/ 快捷键: ctrl+shift+/

3.规范

1、注释符与注释内容之间加一个空格
2、注释行与上方代码间加一个空行

4.实例

HTML

/**
  * @description: 中文说明
  * @author: name
  * @update: name(xxxx-xx-xx)
  */

CSS

/* content */
内容
/* end content */

JS 函数

/** 
* @func * @todo 这个函数需要优化 * @desc 一个带参数的函数 
* @param {string} a - 参数a 
* @param {number} b=1 - 参数b默认值为1 
* @param {string} c=1 - 参数c有两种支持的取值</br>1—表示x</br>2—表示xx 
* @param {object} d - 参数d为一个对象 
* @param {string} d.e - 参数d的e属性 
* @param {string} d.f - 参数d的f属性 
* @param {object[]} g - 参数g为一个对象数组 
* @param {string} g.h - 参数g数组中一项的h属性 
* @param {string} g.i - 参数g数组中一项的i属性 
* @param {string} [j] - 参数j是一个可选参数 
* @returns {boolean} 返回值为true 
*/

5.标签

标签含义
@abstract该成员必须由继承者实现(或覆盖)
@access指定此成员的访问级别(私有、包私有、公共或受保护)
@alias将成员视为具有不同的名称。
@async指示函数是异步的。
@augments指示符号从父符号继承并添加到父符号。
@author识别项目的作者。
@borrows这个对象使用了另一个对象的东西。
@callback记录一个回调函数。
@class此函数旨在使用“new”关键字调用。
@classdesc使用以下文字描述整个类
@constant将对象记录为常量
@constructs这个函数成员将是前一个类的构造函数。
@copyright记录一些版权信息。
@default记录默认值。
@deprecated证明这不再是首选方式
@description描述一个符号。
@enum枚举
@event事件
@example提供如何使用已记录项目的示例。
@exports导出
@external标识外部类、命名空间或模块
@file描述一个文件。
@fires描述此方法可能触发的事件。
@function描述一个功能或方法。
@generator指示函数是生成器函数。
@global记录一个全局对象。
@hideconstructor指示不应显示构造函数
@ignore从文档中省略一个符号
@implements这个符号实现了一个接口。
@inheritdoc指示符号应继承其父级的文档。
@inner- 记录一个内部对象。
@instance记录实例成员。
@interface这个符号是其他人可以实现的接口。
@kind这是什么符号?
@lends在对象文字上记录属性,就好像它们属于具有给定名称的符号一样。
@license确定适用于此代码的许可证。
@listens列出符号侦听的事件。
@member记录成员。
@memberof此符号属于父符号。
@mixes该对象混合了来自另一个对象的所有成员。
@mixin记录一个 mixin 对象。
@module记录一个 JavaScript 模块。
@module记录对象的名称。
@namespace记录命名空间对象。
@override指示符号覆盖其父代
@package这个符号是包私有的。
@param将参数记录到函数中
@private这个符号是私有的。
@property记录对象的属性。
@protected该符号应受到保护。
@public这个符号是公开的。
@readonly该符号是只读的
@requires该文件需要一个 JavaScript 模块。
@returns记录函数的返回值。
@see有关详细信息,请参阅其他一些文档。
@since这个功能是什么时候添加的?
@static记录一个静态成员。
@summary完整描述的较短版本。
@this“this”关键字在这里指的是什么?
@throws描述可能引发的错误。
@todo记录要完成的任务。
@tutorial插入指向包含的教程文件的链接。
@type记录对象的类型。
@typedef记录自定义类型。
@variation区分具有相同名称的不同对象。
@version记录项目的版本号。
@yields记录生成器函数产生的值
类型定义语法示例解释
String{string}--
Number{number}--
Boolean{boolean}--
Object{Object}--
Function{Function}--
RegExp{RegExp}--
Array{Array}--
Date{Date}--
单一类型集合{Array.}string 类型的数组
多类型{(number|boolean)}可能是 number 类型, 也可能是 boolean 类型
允许为null{?number}可能是 number, 也可能是 null
不允许为null{!Object}Object 类型, 但不是 null
Function类型{function(number, boolean)}函数, 形参类型
Function带返回值{function(number, boolean):string}函数, 形参, 返回值类型
PromisePromise.<resolveType, rejectType>Promise,成功返回的数据类型,失败返回的错误类型
参数可选@param {string=} name可选参数, =为类型后缀
可变参数@param {...number} args变长参数, ...为类型前缀
任意类型{*}任意类型
可选任意类型@param {*=} name可选参数,类型不限
可变任意类型@param {...*} args变长参数,类型不限

2.4.5 文件注释

[强制] 文件顶部必须包含文件注释,用 @file 标识文件说明。

示例: /**\

  • @file Describe the file
    */
[建议] 文件注释中可以用 @author 标识开发者信息。

解释:

开发者信息能够体现开发人员对文件的贡献,并且能够让遇到问题或希望了解相关信息的人找到维护人。通常情况文件在被创建时标识的是创建者。随着项目的进展,越来越多的人加入,参与这个文件的开发,新的作者应该被加入 @author 标识。

@author 标识具有多人时,原则是按照 责任 进行排序。通常的说就是如果有问题,就是找第一个人应该比找第二个人有效。比如文件的创建者由于各种原因,模块移交给了其他人或其他团队,后来因为新增需求,其他人在新增代码时,添加 @author 标识应该把自己的名字添加在创建人的前面。

@author 中的名字不允许被删除。任何劳动成果都应该被尊重。

业务项目中,一个文件可能被多人频繁修改,并且每个人的维护时间都可能不会很长,不建议为文件增加 @author 标识。通过版本控制系统追踪变更,按业务逻辑单元确定模块的维护责任人,通过文档与wiki跟踪和查询,是更好的责任管理方式。

对于业务逻辑无关的技术型基础项目,特别是开源的公共项目,应使用 @author 标识。

示例:

/**
 * @file Describe the file
 * @author author-name(mail-name@domain.com)
 *         author-name2(mail-name2@domain.com)
 */

2.4.6 命名空间注释

[建议] 命名空间使用 @namespace 标识。

示例:

/**
 * @namespace
 */
var util = {};

2.4.7 类注释

[建议] 使用 @class 标记类或构造函数。

解释:

对于使用对象 constructor 属性来定义的构造函数,可以使用 @constructor 来标记。

示例:

/**
 * 描述
 *
 * @class
 */
function Developer() {
 // constructor body
}
[建议] 使用 @extends 标记类的继承信息。

示例:

/**
 * 描述
 *
 * @class
 * @extends Developer
 */
function Fronteer() {
 Developer.call(this);
 // constructor body
}
util.inherits(Fronteer, Developer);
[强制] 使用包装方式扩展类成员时, 必须通过 @lends 进行重新指向。

解释:

没有 @lends 标记将无法为该类生成包含扩展类成员的文档。

示例:

/**
 * 类描述
 *
 * @class
 * @extends Developer
 */
function Fronteer() {
 Developer.call(this);
 // constructor body
}

util.extend(
 Fronteer.prototype,
 /** @lends Fronteer.prototype */{
 getLevel: function () {
 // TODO
        }
    }
);
强制] 类的属性或方法等成员信息不是 public 的,应使用 @protected 或 @private 标识可访问性。

解释:

生成的文档中将有可访问性的标记,避免用户直接使用非 public 的属性或方法。

示例:

/**
 * 类描述
 *
 * @class
 * @extends Developer
 */
var Fronteer = function () {
 Developer.call(this);

 /**
     * 属性描述
     *
     * @type {string}
     * @private
     */
 this.level = 'T12';

 // constructor body
};
util.inherits(Fronteer, Developer);

/**
 * 方法描述
 *
 * @private
 * @return {string} 返回值描述
 */
Fronteer.prototype.getLevel = function () {
};

2.4.8 函数/方法注释

[强制] 函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标识。

解释:

当 return 关键字仅作退出函数/方法使用时,无须对返回值作注释标识。

[强制] 参数和返回值注释必须包含类型信息,且不允许省略参数的说明。
[建议] 当函数是内部函数,外部不可访问时,可以使用 @inner 标识。

示例:

/**
 * 函数描述
 *
 * @param {string} p1 参数1的说明
 * @param {string} p2 参数2的说明,比较长
 *     那就换行了.
 * @param {number=} p3 参数3的说明(可选)
 * @return {Object} 返回值描述
 */
function foo(p1, p2, p3) {
 var p3 = p3 || 10;
 return {
 p1: p1,
 p2: p2,
 p3: p3
    };
}
[强制] 对 Object 中各项的描述, 必须使用 @param 标识。

示例:

/**
 * 函数描述
 *
 * @param {Object} option 参数描述
 * @param {string} option.url option项描述
 * @param {string=} option.method option项描述,可选参数
 */
function foo(option) {
 // TODO
}
[建议] 重写父类方法时, 应当添加 @override 标识。如果重写的形参个数、类型、顺序和返回值类型均未发生变化,可省略 @param@return,仅用 @override 标识,否则仍应作完整注释。

解释:

简而言之,当子类重写的方法能直接套用父类的方法注释时可省略对参数与返回值的注释。

2.4.9 事件注释

[强制] 必须使用 @event 标识事件,事件参数的标识与方法描述的参数标识相同。

示例:

/**
 * 值变更时触发
 *
 * @event Select#change
 * @param {Object} e e描述
 * @param {string} e.before before描述
 * @param {string} e.after after描述
 */
this.fire(
 'change',
    {
 before: 'foo',
 after: 'bar'
    }
);
[强制] 在会广播事件的函数前使用 @fires 标识广播的事件,在广播事件代码前使用 @event 标识事件。
[建议] 对于事件对象的注释,使用 @param 标识,生成文档时可读性更好。

示例:

/**
 * 点击处理
 *
 * @fires Select#change
 * @private
 */
Select.prototype.clickHandler = function () {

 /**
     * 值变更时触发
     *
     * @event Select#change
     * @param {Object} e e描述
     * @param {string} e.before before描述
     * @param {string} e.after after描述
     */
 this.fire(
 'change',
        {
 before: 'foo',
 after: 'bar'
        }
    );
};

2.4.10 常量注释

[强制] 常量必须使用 @const 标记,并包含说明和类型信息。

示例:

/**
 * 常量说明
 *
 * @const
 * @type {string}
 */
var REQUEST_URL = 'myurl.do';

2.4.11 复杂类型注释

[建议] 对于类型未定义的复杂结构的注释,可以使用 @typedef 标识来定义。

示例:

// `namespaceA~` 可以换成其它 namepaths 前缀,目的是为了生成文档中能显示 `@typedef` 定义的类型和链接。
/**
 * 服务器
 *
 * @typedef {Object} namespaceA~Server
 * @property {string} host 主机
 * @property {number} port 端口
 */

/**
 * 服务器列表
 *
 * @type {Array.<namespaceA~Server>}
 */
var servers = [
    {
 host: '1.2.3.4',
 port: 8080
    },
    {
 host: '1.2.3.5',
 port: 8081
    }
];

2.4.12 AMD 模块注释

[强制] AMD 模块使用 @module 或 @exports 标识。

解释:

@exports 与 @module 都可以用来标识模块,区别在于 @module 可以省略模块名称。而只使用 @exports 时在 namepaths 中可以省略 module: 前缀。

示例:

define(
 function (require) {

 /**
         * foo description
         *
         * @exports Foo
         */
 var foo = {
 // TODO
        };

 /**
         * baz description
         *
         * @return {boolean} return description
         */
        foo.baz = function () {
 // TODO
        };

 return foo;

    }
);

也可以在 exports 变量前使用 @module 标识:

define(
 function (require) {

 /**
         * module description.
         *
         * @module foo
         */
 var exports = {};


 /**
         * bar description
         *
         */
 exports.bar = function () {
 // TODO
        };

 return exports;
    }
);

如果直接使用 factory 的 exports 参数,还可以:

/**
 * module description.
 *
 * @module
 */
define(
 function (require, exports) {

 /**
         * bar description
         *
         */
 exports.bar = function () {
 // TODO
        };
 return exports;
    }
);
[强制] 对于已使用 @module 标识为 AMD模块 的引用,在 namepaths 中必须增加 module: 作前缀。

解释:

namepaths 没有 module: 前缀时,生成的文档中将无法正确生成链接。

示例:

/**
 * 点击处理
 *
 * @fires module:Select#change
 * @private
 */
Select.prototype.clickHandler = function () {
 /**
     * 值变更时触发
     *
     * @event module:Select#change
     * @param {Object} e e描述
     * @param {string} e.before before描述
     * @param {string} e.after after描述
     */
 this.fire(
 'change',
        {
 before: 'foo',
 after: 'bar'
        }
    );
};
[建议] 对于类定义的模块,可以使用 @alias 标识构建函数。

示例:

/**
 * A module representing a jacket.
 * @module jacket
 */
define(
 function () {

 /**
         * @class
         * @alias module:jacket
         */
 var Jacket = function () {
        };

 return Jacket;
    }
);
[建议] 多模块定义时,可以使用 @exports 标识各个模块。

示例:

// one module
define('html/utils',
 /**
     * Utility functions to ease working with DOM elements.
     * @exports html/utils
     */
 function () {
 var exports = {
        };

 return exports;
    }
);

// another module
define('tag',
 /** @exports tag */
 function () {
 var exports = {
        };

 return exports;
    }
);
[建议] 对于 exports 为 Object 的模块,可以使用@namespace标识。

解释:

使用 @namespace 而不是 @module 或 @exports 时,对模块的引用可以省略 module: 前缀。

[建议] 对于 exports 为类名的模块,使用 @class 和 @exports 标识。

示例:


// 只使用 @class Bar 时,类方法和属性都必须增加 @name Bar#methodName 来标识,与 @exports 配合可以免除这一麻烦,并且在引用时可以省去 module: 前缀。
// 另外需要注意类名需要使用 var 定义的方式。

/**
 * Bar description
 *
 * @see foo
 * @exports Bar
 * @class
 */
var Bar = function () {
 // TODO
};

/**
 * baz description
 *
 * @return {(string|Array)} return description
 */
Bar.prototype.baz = function () {
 // TODO
};

2.4.13 细节注释

对于内部实现、不容易理解的逻辑说明、摘要信息等,我们可能需要编写细节注释。

[建议] 细节注释遵循单行注释的格式。说明必须换行时,每行是一个单行注释的起始。

示例:

function foo(p1, p2, opt_p3) {
 // 这里对具体内部逻辑进行说明
 // 说明太长需要换行
 for (...) {
        ....
    }
}
[强制] 有时我们会使用一些特殊标记进行说明。特殊标记必须使用单行注释的形式。下面列举了一些常用标记:

解释:

  1. TODO: 有功能待实现。此时需要对将要实现的功能进行简单说明。
  2. FIXME: 该处代码运行没问题,但可能由于时间赶或者其他原因,需要修正。此时需要对如何修正进行简单说明。
  3. HACK: 为修正某些问题而写的不太好或者使用了某些诡异手段的代码。此时需要对思路或诡异手段进行描述。
  4. XXX: 该处存在陷阱。此时需要对陷阱进行描述。

参考

jsDoc