Javascript 零基础教程

JavaScript 注释

在 JavaScript 开发中,注释不仅是记录代码逻辑的手段,更是团队协作的重要沟通工具。

高质量的注释能显著降低维护成本,帮助开发者快速理解复杂的业务逻辑或特殊的边界处理。

1. 语法维度的注释(三种形式)

每种注释都有其明确的“使用场景”:

1.1 单行注释 (//)

  • 适用场景:对单行逻辑的补充说明、变量定义的含义说明。
  • 特点:简洁快速。
  • 技巧:建议在 // 之后留一个空格,符合大多数代码风格规范(如 Airbnb/Google 规范)。

示例:

// 单行注释:解释非直观的逻辑或边界情况
const scrollOffset = element.getBoundingClientRect().top + window.scrollY - 80; // 减去 80px 是为了避开顶部固定导航栏

1.2 多行注释 (/* ... */)

  • 适用场景:逻辑块的背景说明、版权声明、或者(在调试阶段)临时屏蔽某块逻辑。
  • 注意:不要用多行注释来包裹“已失效代码”,这会导致代码库混乱。建议使用 Git 进行代码版本管理。

示例:

/*
 * 此处进行轮询请求处理:
 * 1. 检查当前网络状态
 * 2. 如果网络正常,每隔 5s 发送心跳包
 * 3. 如果达到最大重试次数,则中断请求
 */
function startPolling() {
  // ... 代码逻辑
}

1.3 文档注释 (/** ... */) - 即 JSDoc

  • 适用场景:定义函数、类、模块接口的 API 文档。
  • 强大之处:它是代码与 IDE 之间的桥梁。通过 @param@returns 等标签,IDE 能够准确识别数据类型并给予补全提示,这对于团队协作至关重要。

示例:

/**
 * 根据用户 ID 获取其权限列表
 * @param {string|number} userId - 用户的唯一标识符
 * @param {boolean} [includeExpired=false] - 是否包含已过期权限 (可选参数)
 * @returns {Promise<string[]>} 返回权限名称数组
 * @throws {Error} 当用户不存在时抛出异常
 */
async function getUserPermissions(userId, includeExpired = false) {
  // 实现逻辑...
}

2. 工程化标记 (Tags)

在多人协作的大型项目中,单纯的文字说明是不够的,我们需要统一的“状态标记”来告诉队友代码的健康程度

标记含义建议
TODO任务未完成务必加上 [姓名][日期],方便追踪。
FIXME代码有 Bug,但暂时绕过了必须注明重构计划,避免产生技术债。
HACK使用了非标准的“黑科技”说明为什么要用这种不优雅的手段(如兼容性修复)。
NOTE补充说明记录特定的业务规则或边缘案例。
DEPRECATED方法即将弃用告知开发者未来应使用哪个替代方案。

在实际项目中,这些标记可以直接在代码中充当“备忘录”。

示例:

// TODO: 孙悟空 - 2023-10-27 - 这里的 API 响应数据结构不稳定,后续需要增加 Schema 校验
const data = await fetchUserData();

// FIXME: 这里的循环在数据量超过 1 万条时会导致页面卡顿,需要优化为分页查询
for (let i = 0; i < largeArray.length; i++) {
  processItem(largeArray[i]);
}

// HACK: 由于旧版 iOS Safari 的 Flexbox 渲染 Bug,此处增加一个额外的 1px padding
const containerStyle = { padding: '1px' }; 

3. 最佳实践对比

3.1 过度注释/废话

// 将 a 和 b 相加并赋值给 c
let c = a + b;

// 循环 i 从 0 到 length
for (let i = 0; i < length; i++) {
  console.log(i);
}

这些注释完全是代码的“翻译”,不仅占空间,还会随着代码变动变得难以维护。

3.2 优秀的注释(解释决策动机)

// 为了提升性能,此处手动触发了内存回收,避免在大规模图像渲染时出现卡顿
if (window.gc) {
  window.gc();
}

// 由于第三方 SDK 只支持同步回调,这里通过 setTimeout 将任务挂载到宏任务队列,以避免阻塞主线程 UI
setTimeout(() => {
  thirdPartySDK.init();
}, 0);

这些注释解释了为什么要这样做,即使代码本身很简单,但通过注释,维护者能够理解其深层的设计考虑。