这是一个常见的开发问题:您已经编写了供他人使用的 JavaScript 代码,并且需要一份美观的 HTML API 文档。在 JavaScript 世界中,生成 API 文档的事实上的标准工具是JSDoc。[23] 它仿照其 Java 对应物 JavaDoc 建模。
JSDoc 接收带有 /** */ 注释(以星号开头的普通块注释)的 JavaScript 代码,并为其生成 HTML 文档。例如,给定以下代码:
/** @namespace */varutil={/*** Repeat <tt>str</tt> several times.* @param {string} str The string to repeat.* @param {number} [times=1] How many times to repeat the string.* @returns {string}*/repeat:function(str,times){if(times===undefined||times<1){times=1;}returnnewArray(times+1).join(str);}};
生成的 HTML 在 Web 浏览器中显示,如图29-1所示。
JSDoc 网站上的自述文件介绍了如何安装和调用此工具。
JSDoc 的全部内容都是关于记录实体(函数、方法、构造函数等)。这是通过实体之前的注释实现的,这些注释以 /** 开头。
让我们回顾一下开头显示的注释:
/*** Repeat <tt>str</tt> several times.* @param {string} str The string to repeat.* @param {number} [times=1] How many times to repeat the string.* @returns {string}*/
这演示了一些 JSDoc 语法,它由以下几部分组成
/** 启动了这样的注释。@param 是前面代码中的一个示例。<tt> 以等宽字体显示单词。@param {string} name@param {string|number} idCode@param {string[]} names在 JSDoc 注释中,所谓的名称路径用于引用实体。此类路径的语法如下
myFunction MyClass MyClass.staticMember MyClass#instanceMember
类通常(由)构造函数实现。例如,静态成员是构造函数的属性。JSDoc 对实例成员有广泛的定义。它表示可以通过实例访问的所有内容。因此,实例成员包括实例属性和原型属性。
以下是基本元数据标签:
@fileOverview 描述
标记描述整个文件的 JSDoc 注释。例如
/*** @fileOverview Various tool functions.* @author <a href="mailto:jd@example.com">John Doe</a>* @version 3.1.2*/
@author
@deprecated
@example
包含一个代码示例,说明如何使用给定的实体
/*** @example* var str = 'abc';* console.log(repeat(str, 3)); // abcabcabc*/
用于链接的基本标签如下
@see
指向相关资源
/*** @see MyConstructor#myMethod* @see The <a href="http://example.com">Example Project</a>.*/
{@link ...}
@see,但可以在其他标签中使用。@requires resourceDescription
版本控制标签包括以下内容
@version versionNumber
指示文档化实体的版本。例如
@version 10.3.1
@since versionNumber
指示文档化实体自哪个版本开始可用。例如
@since 10.2.0
对于函数和方法,您可以记录参数、返回值和它们可能抛出的异常:
@param {paramType} paramName 描述
描述名称为 paramName 的参数。类型和描述是可选的。以下是一些示例:
@param str The string to repeat.
@param {string} str
@param {string} str The string to repeat.高级功能
可选参数
@param {number} [times] The number of times is optional.具有默认值的可选参数
@param {number} [times=1] The number of times is optional.@returns {returnType} 描述
@throws {exceptionType} 描述
有两种方法可以为参数和返回值提供类型信息。首先,您可以将类型注释添加到 @param 和 @returns:
/*** @param {String} name* @returns {Object}*/functiongetPerson(name){}
其次,您可以内联类型信息
functiongetPerson(/**String*/name)/**Object*/{}
以下标签用于记录变量、参数和实例属性:
@type {typeName}
文档化的变量是什么类型?例如:
/** @type {number} */varcarCounter=0;
此标签也可用于记录函数的返回类型,但在这种情况下,@returns 更可取。
@constant
一个标志,指示文档化的变量具有常量值。
/** @constant */varFORD='Ford';
@property {propType} propKey 描述
在构造函数注释中记录实例属性。例如:
/*** @constructor* @property {string} name The name of the person.*/functionPerson(name){this.name=name;}
或者,实例属性可以记录如下
/*** @class*/functionPerson(name){/*** The name of the person.* @type {string}*/this.name=name;}
使用哪种样式是个人喜好问题。
@default defaultValue
参数或实例属性的默认值是什么?例如:
/** @constructor */functionPage(title){/*** @default 'Untitled'*/this.title=title||'Untitled';}
JSDoc 区分类和构造函数。前一个概念更像是一种类型,而构造函数是实现类的一种方式。JavaScript 用于定义类的内置方法是有限的,这就是为什么有许多 API 可以帮助完成这项任务的原因。这些 API 各不相同,通常是根本性的不同,因此您必须帮助 JSDoc 弄清楚发生了什么。以下标签可以让您做到这一点:
@constructor
@class
@class 是 @constructor 的同义词。@constructs
@lends namePath
指定以下对象字面量贡献给哪个类。有两种贡献方式。
@lends Person#:对象字面量将实例成员贡献给 Person。@lends Person:对象字面量将静态成员贡献给 Person。@memberof parentNamePath
@lends MyClass# 与使用 @memberof MyClass# 标记该字面量的每个属性具有相同的效果。定义类的最常见方法是:通过构造函数、通过对象字面量以及通过具有 @constructs 方法的对象字面量。
要通过构造函数定义类,您必须标记构造函数;否则,它不会被记录为类。仅大写并不能将函数标记为构造函数:
/*** A class for managing persons.* @constructor*/functionPerson(name){}
要通过对象字面量定义类,您需要两个标记。首先,您需要告诉 JSDoc 给定变量保存了一个类。其次,您需要将对象字面量标记为定义一个类。您可以通过 @lends 标签来完成后者:
/*** A class for managing persons.* @class*/varPerson=makeClass(/** @lends Person# */{say:function(message){return'This person says: '+message;}});
如果对象字面量有一个 @constructs 方法,您需要告诉 JSDoc,以便它可以找到实例属性的文档。类的文档移动到该方法:
varPerson=makeClass(/** @lends Person# */{/*** A class for managing persons.* @constructs*/initialize:function(name){this.name=name;},say:function(message){returnthis.name+' says: '+message;}});
如果省略 @lends,则必须指定方法所属的类
varPerson=makeClass({/*** A class for managing persons.* @constructs Person*/initialize:function(name){this.name=name;},/** @memberof Person# */say:function(message){returnthis.name+' says: '+message;}});
JavaScript 本身不支持子类化。当您在代码中进行子类化时(无论是手动还是通过库),您都必须告诉 JSDoc 发生了什么:
@extends namePath
表示文档化的类是另一个类的子类。例如
/*** @constructor* @extends Person*/functionProgrammer(name){Person.call(this,name);...}// Remaining code for subclassing omitted