这是一个常见的开发问题:您已经编写了供他人使用的 JavaScript 代码,并且需要一份美观的 HTML API 文档。在 JavaScript 世界中,生成 API 文档的事实上的标准工具是JSDoc。[23] 它仿照其 Java 对应物 JavaDoc 建模。
JSDoc 接收带有 /** */
注释(以星号开头的普通块注释)的 JavaScript 代码,并为其生成 HTML 文档。例如,给定以下代码:
/** @namespace */
var
util
=
{
/**
* 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
;
}
return
new
Array
(
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:[email protected]">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}
*/
function
getPerson
(
name
)
{
}
其次,您可以内联类型信息
function
getPerson
(
/**String*/
name
)
/**Object*/
{
}
以下标签用于记录变量、参数和实例属性:
@type {typeName}
文档化的变量是什么类型?例如:
/** @type {number} */
var
carCounter
=
0
;
此标签也可用于记录函数的返回类型,但在这种情况下,@returns
更可取。
@constant
一个标志,指示文档化的变量具有常量值。
/** @constant */
var
FORD
=
'Ford'
;
@property {propType} propKey 描述
在构造函数注释中记录实例属性。例如:
/**
* @constructor
* @property {string} name The name of the person.
*/
function
Person
(
name
)
{
this
.
name
=
name
;
}
或者,实例属性可以记录如下
/**
* @class
*/
function
Person
(
name
)
{
/**
* The name of the person.
* @type {string}
*/
this
.
name
=
name
;
}
使用哪种样式是个人喜好问题。
@default defaultValue
参数或实例属性的默认值是什么?例如:
/** @constructor */
function
Page
(
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
*/
function
Person
(
name
)
{
}
要通过对象字面量定义类,您需要两个标记。首先,您需要告诉 JSDoc 给定变量保存了一个类。其次,您需要将对象字面量标记为定义一个类。您可以通过 @lends
标签来完成后者:
/**
* A class for managing persons.
* @class
*/
var
Person
=
makeClass
(
/** @lends Person# */
{
say
:
function
(
message
)
{
return
'This person says: '
+
message
;
}
}
);
如果对象字面量有一个 @constructs
方法,您需要告诉 JSDoc,以便它可以找到实例属性的文档。类的文档移动到该方法:
var
Person
=
makeClass
(
/** @lends Person# */
{
/**
* A class for managing persons.
* @constructs
*/
initialize
:
function
(
name
)
{
this
.
name
=
name
;
},
say
:
function
(
message
)
{
return
this
.
name
+
' says: '
+
message
;
}
}
);
如果省略 @lends
,则必须指定方法所属的类
var
Person
=
makeClass
({
/**
* A class for managing persons.
* @constructs Person
*/
initialize
:
function
(
name
)
{
this
.
name
=
name
;
},
/** @memberof Person# */
say
:
function
(
message
)
{
return
this
.
name
+
' says: '
+
message
;
}
}
);
JavaScript 本身不支持子类化。当您在代码中进行子类化时(无论是手动还是通过库),您都必须告诉 JSDoc 发生了什么:
@extends namePath
表示文档化的类是另一个类的子类。例如
/**
* @constructor
* @extends Person
*/
function
Programmer
(
name
)
{
Person
.
call
(
this
,
name
);
...
}
// Remaining code for subclassing omitted