跳到主要内容

代码风格与注释

代码风格的目标是什么?​

  • 定义: 把复杂任务写成既正确又让人读得懂的形式, 这是编程的核心手艺;
  • 判据: 这样写是否更好读? 是否更不容易出错?;
  • 前提: 没有硬性规定, 风格是团队约定, 不是教条;

哪些语法细节影响可读性?​

  • 花括号: 用 "埃及式" —— 左花括号与关键字同一行, 前面留一个空格;
if (condition) {
// do this
}
  • 单行 if 的取舍:
    • if (n < 0) {alert(...)}: 花括号没有存在意义, 差;
    • if (n < 0) 换行且不带花括号: 后续加语句极易出错, 永远不要写;
    • if (n < 0) alert(...): 极短时勉强可以接受;
    • 推荐: 一律写成代码块;
if (n < 0) {
alert(`Power ${n} is not supported`);
}
  • 行长: 过长要拆行, 上限由团队约定, 通常是 80 或 120 字符; 模板字符串可跨行, 长条件用括号换行;
  • 行内缩进: 2 或 4 个空格 (或 Tab); 空格如今更常见, 因为可以对齐参数等更灵活的排版;
  • 纵向缩进: 用空行把函数切成初始化 / 主循环 / 返回等逻辑块; 连续超过 9 行没有空行就该补一个;
  • 分号: 每条语句都写; JavaScript 里换行并不总被当作分号, 省略会埋坑; 只有整队统一 no-semicolon 风格 (如 StandardJS) 时才省略;
  • 嵌套层级: 越浅越好; 用 continue 或提前 return 先处理特殊情况, 主流程就不再缩进;
for (let i = 0; i < 10; i++) {
if (!cond) continue;
// 主逻辑, 少一层缩进
}

函数应该放在哪里?​

  • 三种摆法: 先声明全部函数再写使用代码; 先写主代码再把辅助函数放后面; 混排 (函数紧跟首次使用处);
  • 推荐: 主代码在前, 辅助函数在后 —— 读代码先要知道 "做什么", 函数名自解释时甚至不必读函数体;

如何用工具落地风格?​

  • 风格指南: Google JavaScript Style Guide、Airbnb、Idiomatic.JS、StandardJS 等, 直接选用比团队自造省事;
  • Linter: 自动检查风格并给出修改建议, 还能发现变量名拼写之类的 bug, 所以即使不在意风格也值得启用;
  • 常见工具: JSLint、JSHint、ESLint; 多数编辑器有插件, ESLint 需要先装 Node.js 再 npm install -g eslint;
  • 配置: 项目根目录放 .eslintrc, 用 extends 继承预设, 再覆盖自己的规则;
{
"extends": "eslint:recommended",
"env": { "browser": true, "node": true, "es6": true },
"rules": { "no-console": 0, "indent": 2 }
}

忍者代码反过来说是什么?​

  • 一行反面清单: 单字母变量、lst/brsr 缩写、data/value 万能名、date 与 data 相似名混用、内层变量遮蔽外层同名变量、函数名与行为不符 (checkPermission 有副作用)、同一个变量反复装载不同含义; 反着做即可读性规则;
  • 遮蔽与副作用的排查只能靠调试器, 见 调试技术;

什么算坏注释?​

  • 坏注释: 解释 "这段代码在做什么", 等于把代码逐句翻译一遍;
  • 规则: 如果代码需要注释才能看懂, 那就重写代码, 而不是补注释;
  • 处方一 "抽函数": 把一段逻辑提成函数, 函数名本身就是注释, 代码变成自描述;
function showPrimes(n) {
for (let i = 2; i < n; i++) {
if (!isPrime(i)) continue;
alert(i);
}
}

function isPrime(n) {
for (let i = 2; i < n; i++) {
if (n % i == 0) return false;
}
return true;
}
  • 处方二 "拆函数": 长流程按动作拆成 addWhiskey(glass)、addJuice(glass), 结构清楚且无需注释;
  • 残留: 复杂算法与性能技巧确实难以自描述, 这类解释性注释无法完全消灭;

什么算好注释?​

  • 架构: 高层视角说明组件如何交互、控制流怎么走 (可以用 UML);
  • 用法: 用 JSDoc 写清参数、返回值与用途, 编辑器据此补全与检查, JSDoc3 还能生成 HTML 文档;
/**
* Returns x raised to the n-th power.
*
* @param {number} x The number to raise.
* @param {number} n The power, must be a natural number.
* @return {number} x raised to the n-th power.
*/
function pow(x, n) {
/* ... */
}
  • 为什么: 记录 "为什么这样解"; 否则后来者会认为是多余写法, 改回更直观但错误的版本, 白白浪费时间;
  • 微妙点: 反直觉、容易踩坑的写法必须注明;