[前端项目规范]提高前端代码可读性的JSDoc

2,601 阅读6分钟

1.JSDoc的介绍

JSDoc是一种针对Javascript的注释。利用这种注释,我们可以提高文档的可读性。JSDoc主要有三个作用:

  1. 通过注释说明让维护者更快更全面地了解函数
  2. 约束Javascript的变量类型,部分IDE会做类型校验
  3. 关联代码生成HTML说明文档

2.JSDoc的例子

如何生成说明文档请看下文。

1.普通函数

/**
 * @author Ocean 
 * @since 1.0.1
 * @name fn
 * @description 该函数的描述...
 * @function
 * @param {String} str 类型为String,名为str的变量...
 * @param {Number} num 类型为Number,名为num的变量...
 * @return {Array} 返回类型为Array的变量
 * @example <caption>Example usage of fn.</caption>
 * // returns ['5', 10]
 * fn('5', 10);
 */
function fn(str,num){
  return [str,num]
}

补充说明:

  • @author:此代码的作者
  • @function:表明这段代码定义的是一个函数,同类的标注有@module(模块),@class(类),@generator(生成器)等。
  • @since:说明该类或者函数或者变量是在哪个版本添加的,以上面为例子则是在1.0.1添加的。此处也可以替换成@version 1.0.1,所表示的意义相同。
  • @example:提供一个如何使用该函数的例子

生成HTML文档效果:

2.私有异步函数

/**
 * @private
 * @async
 * @function
 * @param {String} str 
 * @return {Symbol}
  */
async function _fn(str){
  return Symbol(str)
}

补充说明:

  • @private:说明此函数为私有。标注私有后,对应的说明文档不会显示该函数的说明。同类的标注有@public@protected(仅可在当前module中调用),@package(仅可在当前目录中调用)
  • @async:用于被async声明的函数中,表示该函数为异步函数

因为被标注@private,所以说明文档没有该函数的说明。

3.常数变量

/**
 *  @constant
 *  @default
 *  @deprecated
 */
const RED = 0xff0000

补充说明:

  • @constant:说明该变量是常量
  • @default:在生成的说明文档中显示该常量值
  • @deprecated:说明该变量已被弃用,可能在下一个大版本中去除

生成HTML文档效果:

4.全局变量对象

/**
 * @global
 * @namespace Config
 * @property {object}  config.defaults               - The default values for parties.
 * @property {number}  config.defaults.players       - The default number of players.
 * @property {string}  config.defaults.level         - The default level for the party.
 * @property {object}  config.defaults.treasure      - The default treasure.
 * @property {number}  config.defaults.treasure.gold - How much gold the party starts with.
 */
var config = {
  defaults: {
      players: 1,
      level:   'beginner',
      treasure: {
          gold: 0
      }
  }
}

补充说明:

  • @global:说明该变量为全局变量
  • @namespace:标记指示对象为其成员创建名称空间。命名空间的名称默认为对象名,也可以自定义如例子所示定义成Config。
  • @property:用于标注对象或类中的属性,经过嵌套标注后生成的文档有更可观的层次结构。如下图:

生成HTML文档效果:

5.构造函数

/**
 * @constructor
 * @classdesc 类描述...
 * @param {number} num1
 * 
 */
function Example(num1,num2){
  /**
   * 关于num1变量的描述....
   * @instance
   */
  this.num1=num1
  /**
   * 关于num3变量的描述....
   * @instance
   */
  this.num2=2
  /**
   * 关于fn1函数的描述....
   * @instance
   */
  this.fn1=function(){

  }
}
/**
 * @static
 * @readonly
 */
Example.prototype.num3=3

**补充说明:
**

  • @constructor:说明此对象为构造器,也可以用@class替代。
  • @instance:被标记的变量会当作其父对象的实例成员。
  • @static:表示被标记的变量为静态变量,所谓静态变量即可以不通过实例化父类的情况下访问该变量,如Math.ramdom。同样的,上述代码也可以通过Example.num3访问。
  • @readonly:表示被标记的变量只读,常用于常量和类中的get函数。

生成HTML文档效果:

6.类

/**
 * @extends Shape
 */
class Rectangle extends Shape{
  /**
   * 
   * @param {number} height - 矩形的高
   * @param {numer} width - 矩形的宽
   */
  constructor(height,width){
    this.height = height
    this.width = width
  }

  /**
   * @readonly
   * @returns {number} - 返回体积
   */
  getArea(){
    return this.height*this.width
  }
}

补充说明:

引用自官网关于ES 2015 Classes:

JSDoc 3 makes it easy to document classes that follow the ECMAScript 2015 specification. You don't need to use tags such as @class and @constructor with ES 2015 classes—JSDoc automatically identifies classes and their constructors simply by parsing your code. ES 2015 classes are supported in JSDoc 3.4.0 and later.

JSDoc3.4.0后会自动识别被其他tag注解的class,所以不需要额外加上@class@constructor

  • @extends:表示所继承的类

生成HTML文档效果:

7.modules

这里就直接贴官网,不详细说了:

ES 2015 Modules

CommonJS Modules

8.更多@tag

更多注解请直接看@block-tags

2.JSDoc带来的类型检查

1.VSCode自动检查类型

在VSCode下使用JSDoc,VSCode内置的type-checking插件会自动推导出类型出来。

那上面第一个例子,也就是普通函数的代码来检测,可得到以下效果:

甚至当你通过变量自身函数得到另一个变量时,这个变量的类型也可以被检测到:

2.泛型约束

首先要解释什么是泛型,用上面例子修改一下,成为下面的代码:

/**
 * 省略部分tag
 * @param {String} temp1 
 * @param {String} temp2 
 * @return {Array<String>} 
 */
function fn(temp1,temp2){
  return [temp1,temp2]
}

此时我们通过fn('1','2')调用后会得出子元素为String类型的列表。

可是如果该函数面对的业务不局限于只传入String类型的参数,还可能传入Number或者Object类型的参数时,那JSDoc的已经写死的类型约束就是一个鸡肋。

此时JSDoc中的泛型就是解决这种场景而诞生的。

所谓泛型就是,我们把泛型定义在类或者函数上时,当用户在调用这个类或者函数,才把类型确定下来。

我们把上面的代码修改一下:

/**
 * 省略部分tag
 * @template T
 * @param {T} temp1 
 * @param {T} temp2 
 * @return {Array<T>} 
 */
function fn(temp1,temp2){
  return [temp1,temp2]
}

此处用@template标记T时说明T是泛型类,之后在调用时,无论是传入Number类型还是String类型的参数,VSCode都会推出最后返回的是Array还是Array的变量,如下所示:

3.如何生成说明文档

1.安装

npm i jsdoc -D

2.配置

(1)jsdoc.json

在根目录添加jsdoc.json配置文件,配置如下:

{
  "plugins":[],
  "source": {
    "include": [ "src/" ],
    "exclude": [ "src/libs" ],
    "includePattern": ".+\\.js(doc|x)?$",
    "excludePattern": "(^|\\/|\\\\)_"
  },
  "opts": {
    "template": "templates/default",
    "encoding": "utf8",
    "destination": "./docs/",
    "recurse": true,
    "verbose": false
  }
}

说明:

  • plugins:表示用到的插件。JSDoc有蛮多内置的插件,例如"plugins/markdown":可以在@author@classdesc等注解中用markdown写文字说明,更多关于"plugins/markdown"的说明请看官方文档plugins-markdown。例子中的配置不用任何插件。
  • source.include:指定应该生成说明文档的文件的路径
  • source.exclude:指定不生成说明文档的文件的路径
  • source.includePattern:用正则表达式指定要生成说明文档的文件路径。例子中的配置是指定以.js,.jsdoc和.jsx结尾的文件会生成说明文档
  • source.excludePattern:用正则表达式指定不生成说明文档的文件路径。例子中的配置是指定以下划线_开头的目录和下划线_开头的文件名不会生成说明文档
  • opts.template:说明文档的生成模板。例子中用的是JSDoc自带的默认模板。除了默认的也可以使用第三方模板,如docdashminami
  • opts.encoding: 说明文档的编码
  • opts.destination:说明文档的生成输出路径。例子中是生成在名为docs的目录里
  • opts.recurse:是否递归扫描子目录文件
  • opts.verbose:生成过程中是否输出详细信息

跟详细配置请点击about-configuring-jsdoc

(2)package.json

在script中添加指令,如下所示:

"scripts": {
    
  "docs": "jsdoc -c jsdoc.json"
}

3.运行

直接cmd运行npm run docs即可在docs目录中生成说明文档

4.何时使用JSDoc

其实不同项目有不同的代码规则。我偏向于Angular Team Specification中写到的:

All public API methods must be documented with ngdoc, an extended version of jsdoc (we added support for markdown and templating via @ngdoc tag).

即,必须对每个公共函数用JSDoc注释清楚,其他的不作要求。