JSDoc简单应用

前端开发
2022年10月14日
0

JSDoc 是一个根据 JavaScript 文件中注释信息,生成 JavaScript 应用程序、库或模块的 API 文档的工具。

它的本质是代码注释,但它也有一定的格式和规则。

JSDoc 注释

JSDoc 注释一般应该放置在方法或函数声明之前,它必须以 /** 开头,以便由 JSDoc 解析器识别。其他任何以 /*/*** 或者超过 3 个星号的注释,都会被 JSDoc 解析器忽略。如以下代码所示:

js
/** * 加法运算 */ function plus (a, b) {} /** * 减法运算 */ function minus (a, b) {}

JSDoc 注释标签

在 JSDoc 注释中有一套标准的注释标签,一般以 @ 开头。如下代码所示:

js
/** * 加法运算 * @param { number } a - 加数 * @param { number } b - 被加数 * @returns { number } 两数之和 */ function plus (a, b) {} /** * 减法运算 * @param { number } a - 减数 * @param { number } b - 被减数 * @returns { number } 两数之差 */ function minus (a, b) {}

因为 JSDoc 经历过好几个版本,为了向后兼容,所以也存在别名,比如 @param 就有以下两个别名:

  • @arg
  • @argument

在某些场景下,别名可能更加语义化。

案例说明

本文只会对个别常用案例中使用到的注释标签进行说明,并不会涉及到所有的 JSDoc 注释标签。

变量 / 常量

在 JSDoc 中,并没有变量的注释标签,它只有一个常量的注释标签 @constant,别名为 @const

js
@constant [<type> <name>]

typename 都是可选的:

js
/** @constant */ const A = 1 /** @const { string } B - 字符串 */ const B = '' /** * @constant 一个布尔标识 * @type { boolean } * @default */ const C = false

@type 标签中提供了一个类型,这是可选的。另外,@default 标签在这里也是一样,这里将自动添加指定的值(如:false)给文档。

虽然,它是一个常量的注释标签,但也可以有效地用在 varlet 声明上:

js
/** @constant { string } */ let a = 'string' /** @const { number } */ var b = 1

enum

@enum 标签描述一个静态属性值的全部相同属性集合。枚举类似一个属性的集合,除了枚举自己描述注释之外,其它属性都记录在容器内部的注释中。

@enum 通常与 @readonly 结合使用,作为一个枚举通常表示常量的集合:

js
/** * @readonly * @enum { number } */ const state = { TRUE: 1, FALSE: -1, /** @type { boolean } */ MAYBE: true }

函数

通过 @function 标签将一个对象标记为函数/方法:

js
const arithmetic = { plus (a, b) { return a + b } } /** @function */ const plus = arithmetic.plus

另外,我们可以通过 @name 标签来指定该函数的名称:

js
/** * @function * @name plus */ const plus = arithmetic.plus

当然,这些只是针对生成文档时的应用,对于编辑器提示并没有什么卵用。

下面我们来了解一下如果对函数参数以及返回值进行注释:

js
const arithmetic = { /** * 获取两数之和 * @arg { number } a - 加数 * @arg { number } b - 减数 * @returns { number } 两数之和 */ plus (a, b) { return a + b } }

我们可以通过 @params 或别名 @arg@argument 等标签对函数的参数进行注释:

js
@param [<type>] name [<comment>] @arg [<type>] name [<comment>] @argument [<type>] name [<comment>]

通过 @returns 标签对函数的返回值进行注释:

js
@return [<type> <comment>]

在编辑器里面,当鼠标移动到对应的方法,我们应该可以看到以下的结果:

image-20221014111340079.png

在函数中,还存在着可选参数,如以下函数所示,性别是可选的(0 表示保密,1 表示男,2 表示女):

js
function test (name, age, gender = 0) {}

我们可以将给参数名加上 [] 来表示该参数是可选的:

js
/** * Test * @param { string } name - 名字 * @param { number } age - 年龄 * @param { number } [gender] - 性别(0 表示保密,1 表示男,2 表示女) */ function test (name, age, gender = 0) {}

另外,也可以给可选参数加上默认值:

js
/** * Test * @param { string } name - 名字 * @param { number } age - 年龄 * @param { number } [gender=0] - 性别(0 表示保密,1 表示男,2 表示女) */ function test (name, age, gender = 0) {}

多类型参数和可重复使用的参数

使用 | 可以表示一个参数允许不同的类型:

typescript
/** * SayHello * @param { string | string[] } somebody - 名称,或名称组成的数组 */ function sayHello (somebody = 'zhangsan') { if (Array.isArray(somebody)) { console.log(`Hello ${somebody.join(',')}`) } else { console.log(`Hello ${somebody}`) } }

使用 * 表示一个参数允许任意类型:

js
/** * @param { * } any - 任意类型参数 */ function testAny (any) {}

使用 ... 表示参数个数不确定,但类型一样,也就是可重复使用的参数:

js
/** * 求和 * @param { ...number } num - 正数或负数 */ function sum () { let result = 0 for (i = 0; i < arguments.length; i++) { result += arguments[i] } return result }

回调函数

如果函数接受一个回调函数作为参数,那么我们可以使用 @callback 标签来定义一个回调函数,回调函数的参数类型包含到 @param 标签中:

js
/** * 这是一个名为 reqCallback 的回调函数,它接收一个字符串类型的参数:res * @callback reqCallback * @param { string } res - 返回结果 */ /** * 一个异步函数,接收一个 cb 作为参数 * @param { string } name - 用户名 * @param { reqCallback } cb - 回调函数 */ function asyncFunc (name, cb) { setTimeout(() => { cb && cb(`Hello ${name}`) }, 200) }

需要注意的是,回调函数的类型声明是在另一个注释块里面。

对象参数

如果对象作为参数,我们可以把类型设置成 Object,通过 @param 可以对该对象中其它参数进行注释:

js
/** * 对象作为参数 * @param { Object } person - 一个人 * @param { string } person.name - TA 的名字 * @param { number } person.age - TA 的年龄 * @param { number } [person.gender] - TA 的性别 */ function objFunc (person) { console.log(person.name) console.log(person.age) console.log(person.gender) }

image-20221014143007350.png

同理,如果参数是一个对象数组,那我们可以这样描述:

js
/** * 对象作为参数 * @param { Object[] } persons - 人的集合 * @param { string } persons[].name - TA 的名字 * @param { number } persons[].age - TA 的年龄 * @param { number } [persons[].gender] - TA 的性别 */ function objFunc (persons) { if (Array.isArray(persons)) { persons.forEach(person => { console.log(person.name) console.log(person.age) console.log(person.gender) }) } }

函数使用示例

有些时候,我们需应该给函数添加一些使用示例:

js
/** * 获取参数之和 * @param { ...number } num - 正数或负数 * @return { number } 参数之和 * * @example * sum(1, 2) * * @example * sum(-1, 2) */ function sum () { let result = 0 for (i = 0; i < arguments.length; i++) { result += arguments[i] } return result }

image-20221014143721175.png

@class 标签指明函数是一个构造函数,意味着该函数需要使用 new 关键字来实例化,它的别名是 @constructor

js
@class [<type> <name>] @constructor [<type> <name>]

如下所示:

js
/** * Create a new person * @class */ function Person () {} const p = new Person()

修饰符

在面向对象中,类的修饰符有三种:privateprotectedpublic。同样在 JSDoc 也有相应的注释标签:@private@protected@public

js
/** * @class */ class Person { /** * 名字 * @public * @type { string } */ name /** * 年龄 * @protected * @type { number } */ age /** * 性别 * @private * @type { number } * @default */ gender = 0 /** * @param { string } name * @param { number } age * @param { number } [gender] */ constructor (name, age, gender = 0) { this.name = name this.age = age this.gender = gender } }

当然,少不了 @static

js
/** * @class */ class Person { // ... /** * 工具函数,将字符串转成数字 * @static * @param { string | number } str * @returns { number } */ static str2number (str) { return Number(str) } }

this

@this 标签指明 this 关键字的指向:

js
/** * @constructor * @param { string } name */ function Greeter (name) { setName.apply(this, name) } /** * @this Greeter * @param { string } name */ function setName (name) { this.name = name }

extends

使用 @extends 标签来扩展类:

js
/** * @class * @this Person * @param { string } name */ function Person (name) { this.name = name } /** * @class * @extends Person */ function Male () {} Male.prototype = Person.prototype

自定义一个类型

通过 @typedef 可以自定义一个类型。

js
/** * @typedef { Object } Person * @property { string } name - 名字 * @property { number } age - 年龄 * @property { number } [gender] - 性别 */ /** * @param { Person } person */ function test (person) { console.log(person.name, person.age, person.gender) }

image-20221014161558696.png

接口的返回值

利用自定义类型来定义一个接口的返回值类型:

js
/** * @template T * @typedef ResponseData * @property { code } number * @property { message } string * @property { T } data */ /** * @typedef { Object } UserInfo * @property { string } name * @property { number } age * @property { number } [gender] * @property { Skill[] } [skills] * @property { UserInfo[] } [friends] */ /** * @typedef Skill * @type { '唱' | '跳' | 'Rap' | '篮球' } */ /** * 获取用户信息 * @param { string } url * @returns { Promise<ResponseData<UserInfo>> } */ function getUserInfo (url) { return axios.get(url) } const zhangsan = getUserInfo('https://www.baidu.com').then(res => { console.log(res.data.skills) })

image-20221014170846215.png

这里需要注意的是,在 JSDoc 中,需要使用 @template 来标识一个泛型类型。

其他

@since

@since 表示一个功能是哪个版本被加入的:

js
/** * 获取随机数 * @since 1.0.0 * @return { number } */ function getRandom () { return Math.random() }

@deprecated

@deprecated 表示该功能已经被废弃:

js
/** * @deprecated since version 1.2.3 */ function oldFunc () {}

@see

@see 表示可以参考另一个标识符的文档,或一个外部资源:

js
/** * @see {@link bar} * @see bar */ function foo () {} /** * @see {@link foo} * @see {@link https://www.baidu.com} */ function bar () {}

@todo

@todo 记录一个需要完成的任务:

js
/** * @todo Implement this function. * @todo Write the documentation. */ function test (a) { }