JSDoc 最佳实践 #

文档编写原则 #

清晰简洁 #

javascript
// ✅ 好的做法:清晰简洁
/**
 * 计算两个数字的和
 * @param {number} a - 第一个数字
 * @param {number} b - 第二个数字
 * @returns {number} 两数之和
 */
function add(a, b) {
  return a + b;
}

// ❌ 不好的做法:过于冗长
/**
 * 这个函数用于计算两个数字的和。
 * 它接受两个数字作为参数,然后返回这两个数字相加的结果。
 * 第一个参数是一个数字,第二个参数也是一个数字。
 * 返回值是一个数字,表示两个输入数字的和。
 * 
 * @param {number} a - 这是一个数字类型的参数,表示第一个要相加的数字
 * @param {number} b - 这是一个数字类型的参数,表示第二个要相加的数字
 * @returns {number} 返回一个数字,表示两个输入数字相加的结果
 */
function add(a, b) {
  return a + b;
}

保持同步 #

javascript
// ✅ 好的做法:文档与代码同步
/**
 * 创建用户
 * @param {string} name - 用户名
 * @param {string} email - 邮箱地址
 * @param {number} [age] - 年龄(可选)
 * @returns {User} 用户实例
 */
function createUser(name, email, age) {
  return new User(name, email, age);
}

// ❌ 不好的做法:文档与代码不同步
/**
 * 创建用户
 * @param {string} name - 用户名
 * @param {string} email - 邮箱地址
 */
function createUser(name, email, age, phone) {
  return new User(name, email, age, phone);
}

使用具体类型 #

javascript
// ✅ 好的做法:使用具体类型
/**
 * @param {string[]} names - 名称数组
 * @param {Object<string, number>} scores - 分数映射
 * @returns {Promise<User[]>} 用户列表 Promise
 */

// ❌ 不好的做法:使用模糊类型
/**
 * @param {Array} names - 数组
 * @param {Object} scores - 对象
 * @returns {Promise} Promise
 */

函数文档规范 #

基本函数 #

javascript
/**
 * 格式化日期为指定格式的字符串
 * 
 * @param {Date} date - 要格式化的日期对象
 * @param {string} [format='YYYY-MM-DD'] - 格式字符串
 * @returns {string} 格式化后的日期字符串
 * 
 * @example
 * formatDate(new Date())
 * // 返回 '2024-01-15'
 * 
 * @example
 * formatDate(new Date(), 'YYYY/MM/DD')
 * // 返回 '2024/01/15'
 */
function formatDate(date, format = 'YYYY-MM-DD') {
  // ...
}

复杂参数 #

javascript
/**
 * 发送 HTTP 请求
 * 
 * @param {string} url - 请求地址
 * @param {Object} [options] - 请求选项
 * @param {string} [options.method='GET'] - 请求方法
 * @param {Object} [options.headers] - 请求头
 * @param {Object|string} [options.body] - 请求体
 * @param {number} [options.timeout=5000] - 超时时间(毫秒)
 * @param {boolean} [options.withCredentials=false] - 是否携带凭证
 * @returns {Promise<Response>} 响应 Promise
 * 
 * @throws {NetworkError} 网络错误时抛出
 * @throws {TimeoutError} 请求超时时抛出
 * 
 * @example
 * // GET 请求
 * const data = await request('/api/users')
 * 
 * @example
 * // POST 请求
 * const result = await request('/api/users', {
 *   method: 'POST',
 *   body: { name: 'John' }
 * })
 */
async function request(url, options = {}) {
  // ...
}

回调函数 #

javascript
/**
 * 异步处理数据
 * 
 * @param {Array} items - 数据项数组
 * @param {Function} iterator - 处理函数
 * @param {*} iterator.item - 当前项
 * @param {number} iterator.index - 当前索引
 * @param {Function} iterator.callback - 完成回调
 * @param {Function} [callback] - 完成回调
 * 
 * @example
 * processItems(items, (item, index, cb) => {
 *   // 处理 item
 *   cb(null, result)
 * }, (err, results) => {
 *   // 所有项处理完成
 * })
 */
function processItems(items, iterator, callback) {
  // ...
}

类文档规范 #

类定义 #

javascript
/**
 * 用户管理类
 * 
 * 提供用户的增删改查功能。
 * 
 * @class
 * @example
 * const manager = new UserManager()
 * const user = await manager.create({ name: 'John' })
 */
class UserManager {
  /**
   * 创建 UserManager 实例
   * 
   * @param {Object} [config] - 配置选项
   * @param {string} [config.apiBase='/api'] - API 基础地址
   * @param {number} [config.timeout=5000] - 请求超时时间
   */
  constructor(config = {}) {
    /**
     * API 基础地址
     * @type {string}
     * @readonly
     */
    this.apiBase = config.apiBase || '/api';
    
    /**
     * 请求超时时间
     * @type {number}
     */
    this.timeout = config.timeout || 5000;
    
    /**
     * 用户缓存
     * @private
     * @type {Map<number, User>}
     */
    this._cache = new Map();
  }

  /**
   * 创建用户
   * 
   * @param {Object} data - 用户数据
   * @param {string} data.name - 用户名
   * @param {string} data.email - 邮箱地址
   * @returns {Promise<User>} 用户实例
   * 
   * @fires UserManager#create
   * @throws {ValidationError} 数据验证失败时抛出
   * 
   * @example
   * const user = await manager.create({
   *   name: 'John',
   *   email: 'john@example.com'
   * })
   */
  async create(data) {
    // ...
  }

  /**
   * 获取默认配置
   * 
   * @static
   * @returns {Object} 默认配置对象
   */
  static getDefaultConfig() {
    return {
      apiBase: '/api',
      timeout: 5000
    };
  }
}

继承类 #

javascript
/**
 * 管理员用户类
 * 
 * @class Admin
 * @extends User
 * @example
 * const admin = new Admin('John', ['read', 'write'])
 */
class Admin extends User {
  /**
   * 创建管理员实例
   * 
   * @param {string} name - 用户名
   * @param {string[]} permissions - 权限列表
   */
  constructor(name, permissions) {
    super(name);
    
    /**
     * 权限列表
     * @type {string[]}
     */
    this.permissions = permissions;
  }

  /**
   * 检查是否有指定权限
   * 
   * @param {string} permission - 权限名称
   * @returns {boolean} 是否有权限
   * 
   * @example
   * admin.hasPermission('read') // true
   */
  hasPermission(permission) {
    return this.permissions.includes(permission);
  }
}

模块文档规范 #

模块定义 #

javascript
/**
 * HTTP 请求工具模块
 * 
 * 提供统一的 HTTP 请求处理功能。
 * 
 * @module utils/http
 * @example
 * import { get, post } from 'utils/http'
 * 
 * const data = await get('/api/users')
 * const result = await post('/api/users', { name: 'John' })
 */

/**
 * 发送 GET 请求
 * 
 * @param {string} url - 请求地址
 * @param {Object} [options] - 请求选项
 * @returns {Promise<*>} 响应数据 Promise
 */
export async function get(url, options) {
  // ...
}

/**
 * 发送 POST 请求
 * 
 * @param {string} url - 请求地址
 * @param {Object} data - 请求数据
 * @param {Object} [options] - 请求选项
 * @returns {Promise<*>} 响应数据 Promise
 */
export async function post(url, data, options) {
  // ...
}

/**
 * 默认请求配置
 * 
 * @constant {Object}
 */
export const defaultConfig = {
  timeout: 5000,
  headers: {
    'Content-Type': 'application/json'
  }
};

类型导出 #

javascript
/**
 * 类型定义模块
 * 
 * @module types
 */

/**
 * 用户类型
 * @typedef {Object} User
 * @property {number} id - 用户 ID
 * @property {string} name - 用户名
 * @property {string} email - 邮箱地址
 */

/**
 * API 响应类型
 * @template T
 * @typedef {Object} ApiResponse
 * @property {number} code - 状态码
 * @property {string} message - 消息
 * @property {T} [data] - 数据
 */

export { User, ApiResponse };

类型定义规范 #

使用 typedef #

javascript
/**
 * 用户类型
 * 
 * @typedef {Object} User
 * @property {number} id - 用户 ID
 * @property {string} name - 用户名
 * @property {string} email - 邮箱地址
 * @property {string[]} [roles] - 角色列表
 * @property {Date} [createdAt] - 创建时间
 */

/**
 * 创建用户
 * 
 * @param {User} user - 用户对象
 * @returns {Promise<User>}
 */
async function createUser(user) {
  // ...
}

复用类型定义 #

javascript
// types.js
/**
 * @typedef {Object} BaseEntity
 * @property {number} id - 实体 ID
 * @property {Date} createdAt - 创建时间
 * @property {Date} updatedAt - 更新时间
 */

/**
 * @typedef {BaseEntity & {
 *   name: string,
 *   email: string
 * }} User
 */

// user.js
/**
 * @typedef {import('./types').User} User
 */

/**
 * @param {User} user
 */
function processUser(user) {
  // ...
}

泛型类型 #

javascript
/**
 * 分页结果类型
 * 
 * @template T
 * @typedef {Object} PaginatedResult
 * @property {T[]} items - 数据项
 * @property {number} total - 总数
 * @property {number} page - 当前页
 * @property {number} pageSize - 每页数量
 */

/**
 * 获取用户列表
 * 
 * @param {number} [page=1] - 页码
 * @param {number} [pageSize=10] - 每页数量
 * @returns {Promise<PaginatedResult<User>>}
 */
async function getUsers(page = 1, pageSize = 10) {
  // ...
}

示例编写规范 #

基本示例 #

javascript
/**
 * 计算两数之和
 * 
 * @param {number} a - 第一个数字
 * @param {number} b - 第二个数字
 * @returns {number} 两数之和
 * 
 * @example
 * add(1, 2) // 返回 3
 */
function add(a, b) {
  return a + b;
}

多场景示例 #

javascript
/**
 * 格式化字符串
 * 
 * @param {string} template - 模板字符串
 * @param {Object} data - 数据对象
 * @returns {string} 格式化后的字符串
 * 
 * @example <caption>基本用法</caption>
 * format('Hello, {name}!', { name: 'John' })
 * // 返回 'Hello, John!'
 * 
 * @example <caption>多个占位符</caption>
 * format('{greeting}, {name}!', { greeting: 'Hi', name: 'Jane' })
 * // 返回 'Hi, Jane!'
 * 
 * @example <caption>嵌套对象</caption>
 * format('City: {address.city}', { address: { city: 'Beijing' } })
 * // 返回 'City: Beijing'
 */
function format(template, data) {
  // ...
}

异步示例 #

javascript
/**
 * 获取用户信息
 * 
 * @param {number} id - 用户 ID
 * @returns {Promise<User>} 用户 Promise
 * 
 * @example
 * // 使用 async/await
 * async function main() {
 *   const user = await getUser(1)
 *   console.log(user.name)
 * }
 * 
 * @example
 * // 使用 Promise.then
 * getUser(1).then(user => {
 *   console.log(user.name)
 * })
 */
async function getUser(id) {
  // ...
}

团队协作规范 #

文档风格指南 #

javascript
// docs/STYLE_GUIDE.md

## JSDoc 文档风格指南

### 1. 注释格式

- 使用 `/** */` 格式
- 每行以 `*` 开头
- 标签前后各空一行

### 2. 描述规范

- 第一行是简短描述(不超过一行)
- 详细描述另起一段
- 使用中文描述

### 3. 类型规范

- 使用具体类型,避免 `*` 或 `any`
- 数组使用 `Type[]` 格式
- 对象使用 `@typedef` 定义

### 4. 参数规范

- 所有参数必须有描述
- 可选参数使用 `[param]` 格式
- 默认值使用 `=value` 格式

### 5. 示例规范

- 每个公共 API 至少有一个示例
- 示例要简洁明了
- 包含预期输出

代码审查检查清单 #

markdown
## JSDoc 代码审查检查清单

- [ ] 所有公共 API 都有文档
- [ ] 参数类型正确
- [ ] 返回值类型正确
- [ ] 描述清晰准确
- [ ] 示例可以运行
- [ ] 文档与代码同步
- [ ] 使用了正确的标签
- [ ] 类型定义完整

ESLint 集成 #

javascript
// .eslintrc.js
module.exports = {
  rules: {
    // 要求 JSDoc 注释
    'require-jsdoc': ['error', {
      require: {
        FunctionDeclaration: true,
        MethodDefinition: true,
        ClassDeclaration: true,
        ArrowFunctionExpression: false
      }
    }],
    
    // 验证 JSDoc 格式
    'valid-jsdoc': ['error', {
      requireReturn: true,
      requireParamDescription: true,
      requireReturnDescription: true
    }],
    
    // 检查类型注释
    'jsdoc/check-types': 'error',
    'jsdoc/check-tag-names': 'error'
  }
};

CI/CD 集成 #

GitHub Actions #

yaml
# .github/workflows/docs.yml
name: Generate Docs

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  docs:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Generate docs
        run: npm run docs
      
      - name: Deploy to GitHub Pages
        if: github.event_name == 'push'
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./docs

GitLab CI #

yaml
# .gitlab-ci.yml
stages:
  - docs

generate_docs:
  stage: docs
  image: node:18
  script:
    - npm ci
    - npm run docs
  artifacts:
    paths:
      - docs/
    expire_in: 1 week
  only:
    - main

文档验证 #

yaml
# .github/workflows/validate-docs.yml
name: Validate Docs

on: [push, pull_request]

jobs:
  validate:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Check JSDoc syntax
        run: npm run docs:check
      
      - name: Validate types
        run: npm run type-check

常见问题解决 #

问题 1:类型推断不准确 #

javascript
// 问题
/**
 * @param data - 数据
 */
function process(data) {
  return data.name; // 类型推断失败
}

// 解决
/**
 * @param {Object} data - 数据对象
 * @param {string} data.name - 名称
 * @returns {string} 名称
 */
function process(data) {
  return data.name;
}

问题 2:可选参数处理 #

javascript
// 问题
/**
 * @param {string} name - 用户名
 * @param {number} age - 年龄
 */
function create(name, age) {
  // age 可能是 undefined
}

// 解决
/**
 * @param {string} name - 用户名
 * @param {number} [age] - 年龄(可选)
 */
function create(name, age) {
  if (age !== undefined) {
    // 处理 age
  }
}

问题 3:Promise 类型 #

javascript
// 问题
/**
 * @returns {User} 用户
 */
async function getUser() {
  return fetch('/api/user');
}

// 解决
/**
 * @returns {Promise<User>} 用户 Promise
 */
async function getUser() {
  const response = await fetch('/api/user');
  return response.json();
}

问题 4:泛型函数 #

javascript
// 问题
/**
 * @param value - 输入值
 * @returns 输出值
 */
function identity(value) {
  return value;
}

// 解决
/**
 * @template T
 * @param {T} value - 输入值
 * @returns {T} 相同类型的输出值
 */
function identity(value) {
  return value;
}

文档维护 #

定期审查 #

bash
# 检查缺失文档的函数
npm run docs:check

# 检查过时的文档
npm run docs:validate

# 更新文档
npm run docs:update

版本管理 #

javascript
/**
 * @since 1.0.0
 */
function feature() {}

/**
 * @deprecated 自 2.0.0 起废弃
 * @see newFeature
 */
function oldFeature() {}

/**
 * @version 2.1.0
 * @description 新增了 xxx 功能
 */
function updatedFeature() {}

变更日志 #

javascript
/**
 * 用户服务
 * 
 * @module UserService
 * @version 2.0.0
 * 
 * @changelog
 * - 2.0.0: 重构 API,支持 Promise
 * - 1.5.0: 新增批量操作
 * - 1.0.0: 初始版本
 */

总结 #

核心原则 #

  1. 清晰简洁:描述准确,避免冗余
  2. 保持同步:文档与代码同步更新
  3. 类型具体:使用具体类型,避免模糊
  4. 示例完整:提供可运行的示例
  5. 团队协作:统一规范,代码审查

检查清单 #

  • [ ] 所有公共 API 都有文档
  • [ ] 参数和返回值类型正确
  • [ ] 描述清晰准确
  • [ ] 示例可以运行
  • [ ] 文档与代码同步
  • [ ] 使用了正确的标签
  • [ ] 类型定义完整
  • [ ] 遵循团队规范

学习路径回顾 #

text
入门阶段
├── JSDoc 简介 ✓
├── 基础语法 ✓
├── 类型定义 ✓
└── 常用标签 ✓

进阶阶段
├── 高级用法 ✓
├── 配置文件 ✓
├── 插件扩展 ✓
└── 最佳实践 ✓

实战阶段
├── 项目文档实战
├── 团队规范制定
├── CI/CD 集成
└── 持续维护

恭喜你完成了 JSDoc 文档的学习!现在你已经掌握了从基础到高级的所有知识,可以开始在实际项目中编写高质量的文档了。

最后更新:2026-03-29