从基础语法到高级类型定义,掌握 JavaScript 文档注释的最佳实践
1.1 基本语法
JSDoc 注释必须以 /** 开头,以 */ 结尾。内部可以使用多行描述和一系列块标签(以 @ 开头)。
📄 基本语法模板
/*** 函数的简短描述(第一行会作为摘要)** 可以添加更详细的说明,支持 **Markdown** 格式。* - 支持列表* - 支持 `代码`** @param {参数类型} 参数名 - 参数描述* @returns {返回值类型} 返回值描述*/
1.2 简单的函数注释
📄 加法函数示例
/*** 计算两个数字的和* @param {number} a - 第一个数字* @param {number} b - 第二个数字* @returns {number} 两数之和*/functionadd(a, b){return a + b;}
2.1 参数相关标签
常用参数标签包括:
@param@param {类型}[可选参数][参数=默认值]...剩余参数参数名.属性名
📄 用户注册函数 - 完整参数演示
/*** 用户注册函数* @param {string} username - 用户名(必填)* @param {string} email - 邮箱地址(必填)* @param {number} [age=18] - 年龄(可选,默认为18)* @param {...number} scores - 可变参数,多个分数* @param {Object} options - 配置选项* @param {boolean} options.verbose - 是否输出详细信息*/functionregister(username, email, age = 18, ...scores){// 实现代码}
2.2 返回值标签
一个函数只能有一个@returns(或 @return),但可以使用内联类型或 @typedef 来描述复杂结构。
❌ 错误示例(多个 @returns)——工具会报错或解析异常
❌ 不推荐写法
// 不推荐!工具会报错或解析异常/*** @returns {Object} 用户信息对象* @returns {string} returns.name - 用户名 // 错误写法*/
✅ 正确写法一:内联对象类型
✅ 内联对象类型
/*** 获取用户信息* @param {string} userId - 用户ID* @returns {{ name: string, age: number }} 包含用户名和年龄的对象*/functiongetUserInfo(userId) {return {name: "张三",age: 25,};}
✅ 正确写法二:结合 @typedef(推荐)
✅ 使用 @typedef
/*** @typedef {Object} UserInfo* @property {string} name - 用户名* @property {number} age - 年龄*//*** 获取用户信息* @param {string} userId - 用户ID* @returns {UserInfo} 用户信息对象*/functiongetUserInfo(userId){return {name: "张三",age: 25,};}
2.3 异常处理标签
使用 @throws 描述可能抛出的错误。
📄 fetchData 异常处理
/*** 从API获取数据* @param {string} url - API地址* @returns {Promise<Object>} 返回的JSON数据* @throws {Error} 当网络请求失败时抛出错误* @throws {TypeError} 当URL格式不正确时抛出类型错误*/async function fetchData(url) {if (!url.startsWith('http')) {throw new TypeError('URL格式不正确');}const response = await fetch(url);if (!response.ok) {throw new Error('网络请求失败');}return response.json();}
2.4 类型标签 ⭐ 重要补充
以下类型标签在大型项目中至关重要:
@type@typedef@property@callback@enum
@type 示例
📄 @type 用法
/** @type {string} */let userName;/** @type {number[]} */const scores = [95, 87, 76];/** @type {{ x: number, y: number }} */const point = { x: 10, y: 20 };
@typedef 与 @property 详细用法
📄 类型定义
/*** @typedef {Object} User* @property {string} id - 用户唯一标识* @property {string} name - 用户名* @property {number} age - 年龄* @property {string[]} hobbies - 爱好列表*//*** @typedef {'admin' | 'editor' | 'viewer'} UserRole - 用户角色枚举*/
@callback 定义回调签名
📄 回调类型
/*** 处理结果回调* @callback processCallback* @param {Error|null} error - 错误对象,成功时为 null* @param {Object} result - 处理结果*//*** 执行异步任务* @param {string} taskId - 任务ID* @param {processCallback} callback - 完成后的回调*/functiondoTask(taskId, callback) {// 完成后调用 callback(null, result) 或 callback(error)}
@enum 定义枚举值
📄 枚举定义
/*** 状态枚举* @readonly* @enum {number}*/const Status = {PENDING: 0,APPROVED: 1,REJECTED: 2};
3.1 类型定义的复用
@typedef 定义的类型可以在整个项目中引用,提高可维护性。
📄 复用 ApiResponse 类型
/*** @typedef {Object} ApiResponse* @property {boolean} success - 请求是否成功* @property {*} data - 响应数据* @property {string} [message] - 可选的消息字段*//*** 获取用户列表* @returns {Promise<ApiResponse>} API 响应结构*/async function getUsers() {// ...}
3.2 联合类型与泛型
联合类型使用 | 分隔,泛型使用 @template 声明。
📄 泛型函数
/*** 身份函数:返回传入的值* @template T* @param {T} value - 任意类型的值* @returns {T} 与输入相同的值*/functionidentity(value) {return value;}/*** 安全获取对象属性* @template T, K extends keyof T* @param {T} obj - 源对象* @param {K} key - 属性键* @returns {T[K]} 属性值*/functiongetProperty(obj, key) {return obj[key];}
3.3 类的文档注释
使用 @class、@constructor、@static 等标签。
📄 User 类
/*** 用户实体类* @class*/class User {/*** 创建用户实例* @param {string} name - 用户名* @param {number} age - 年龄*/constructor(name, age) {/*** 用户名* @type {string}*/this.name = name;/*** 年龄* @type {number}*/this.age = age;}/*** 获取格式化信息* @returns {string} 格式化字符串*/getInfo() {return `${this.name} (${this.age}岁)`;}/*** 静态方法:验证用户名合法性* @static* @param {string} name - 待验证的用户名* @returns {boolean} 是否有效*/static validateName(name) {return name.length >= 2;}}
4.1 模块注释(ES模块)
使用 @module 定义模块,配合 @exports 或直接在 export 语句上注释。
📄 数学工具模块
/*** 数学工具模块* @module mathUtils*//*** 计算阶乘* @param {number} n - 非负整数* @returns {number} n 的阶乘*/export function factorial(n) {if (n <= 1) return 1;return n * factorial(n - 1);}
4.2 命名空间(对象字面量)
使用 @namespace 和 @memberof。
📄 Utils 命名空间
/*** 工具函数集合* @namespace Utils*/const Utils = {/*** 日期格式化* @memberof Utils* @param {Date} date - 日期对象* @returns {string} ISO 格式日期部分*/formatDate(date) {return date.toISOString().split('T')[0];},/*** 深度克隆对象* @memberof Utils* @param {Object} obj - 要克隆的对象* @returns {Object} 克隆后的新对象*/deepClone(obj) {return JSON.parse(JSON.stringify(obj));}};
5.1 描述 this 上下文
📄 @this 标签
/*** 点击处理函数* @this {HTMLElement} 触发事件的元素* @param {MouseEvent} event - 鼠标事件对象*/function handleClick(event) {console.log(this.id);}
5.2 标记废弃与链接
@deprecated@see@since
📄 废弃标记
/*** 旧版登录方法* @deprecated 自 v2.0 起使用 {@link newLogin} 替代* @param {string} user - 用户名* @param {string} pass - 密码* @returns {boolean} 登录结果* @see newLogin*/functionoldLogin(user, pass) {// ...}
5.3 示例代码
使用 @example 可以嵌入示例,支持 Markdown 代码块。
📄 @example 标签
/*** 计算两个数相除的结果* @param {number} dividend - 被除数* @param {number} divisor - 除数* @returns {number} 商* @throws {Error} 除数为零时抛出错误* @example* // 返回 2.5* divide(5, 2);** @example* // 抛出错误* try {* divide(5, 0);* } catch (e) {* console.error(e.message);* }*/function divide(dividend, divisor) {if (divisor === 0) throw new Error('除数不能为零');return dividend / divisor;}
5.4 其他实用标签
@author@version@license
📄 元信息标签
/*** @author 张三 <zhangsan@example.com>* @version 1.2.3* @license MIT*/
6.1 完整示例:用户服务
📄 UserService 完整示例
/*** 用户服务模块,提供用户相关的 CRUD 操作* @module services/UserService*//*** 用户信息对象* @typedef {Object} User* @property {string} id - 唯一ID* @property {string} name - 姓名* @property {string} email - 邮箱* @property {number} [age] - 年龄(可选)*//*** 用户服务类* @class*/class UserService {/*** 创建用户服务实例* @param {string} apiBase - API 基础路径*/constructor(apiBase) {/** @type {string} */this.apiBase = apiBase;}/*** 根据 ID 获取用户* @param {string} userId - 用户 ID* @returns {Promise<User>} 用户对象* @throws {Error} 当用户不存在或网络异常时抛出* @example* const service = new UserService('https://api.example.com');* const user = await service.getUserById('123');* console.log(user.name);*/async getUserById(userId) {const res = await fetch(`${this.apiBase}/users/${userId}`);if (!res.ok) throw new Error(`HTTP ${res.status}`);return res.json();}/*** 创建新用户* @param {Object} userData - 用户数据(不含 id)* @param {string} userData.name - 姓名* @param {string} userData.email - 邮箱* @param {number} [userData.age] - 年龄* @returns {Promise<string>} 新创建用户的 ID*/async createUser(userData) {const res = await fetch(`${this.apiBase}/users`, {method: 'POST',headers: { 'Content-Type': 'application/json' },body: JSON.stringify(userData)});const result = await res.json();return result.id;}}
6.2 配置 JSDoc 生成文档
项目根目录下创建 jsdoc.config.json:
📄 jsdoc.config.json
{"source": {"include": ["src"],"exclude": ["src/test", "node_modules"]},"opts": {"destination": "./docs","recurse": true,"readme": "./README.md","template": "node_modules/docdash"},"plugins": ["plugins/markdown"],"templates": {"cleverLinks": false,"monospaceLinks": false}}
💡 推荐模板:docdash(更现代)、 minami
6.3 生成文档命令
💻 终端命令
# 全局安装npm install -g jsdoc# 生成单个文件文档jsdoc your-file.js -d ./docs# 使用配置文件jsdoc -c jsdoc.config.json
7. 注意事项与技巧
只使用一个 @returns,复杂对象用@typedef或内联结构。- 可选属性
推荐 @property {number} [age]的写法。 - 类型组合
灵活使用联合类型 {string | number}、数组{string[]}、Promise{Promise<User>}。 - 继承文档
使用 @inheritdoc或@override可减少重复注释。 启用 Markdown 插件,让描述更易读。 在 VS Code 等编辑器中,JSDoc 注释能提供智能提示,提升开发体验。
8. 总结
JSDoc 是 JavaScript 社区标准化的文档注释方案,通过系统的类型标注和标签使用,能够:
✅ 自动生成可读性强的 API 文档 ✅ 为 IDE 提供智能提示与类型检查 ✅ 提升团队协作和代码可维护性
掌握从基础标签到高级类型定义(@typedef、@template、@callback)的用法,再结合合理的项目配置与生成工具,将使你的代码文档从"备注"升级为"活文档",贯穿开发始终。
夜雨聆风