代码风格与注释
代码风格的目标是什么?
- 定义: 把复杂任务写成既正确又让人读得懂的形式, 这是编程的核心手艺;
- 判据: 这样写是否更好读? 是否更不容易出错?;
- 前提: 没有硬性规定, 风格是团队约定, 不是教条;
哪些语法细节影响可读性?
- 花括号: 用 "埃及式" —— 左花括号与关键字同一行, 前面留一个空格;
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) {
/* ... */
}
- 为什么: 记录 "为什么这样解"; 否则后来者会认为是多余写法, 改回更直观但错误的版本, 白白浪费时间;
- 微妙点: 反直觉、容易踩坑的写法必须注明;