前言

好的注释可以提高代码的可读性和可维护性,从而提高代码质量。
作为前端开发,对于代码“注释”其实并不陌生。它往往作为我们代码文档的特殊补充而存在。
提倡给代码加注释,但不能滥用。团队Code Review过程中会增加审查注释规范,前端工程师都应该了解好的注释是什么样的,同时遇到不好的代码注释,也要告诉其如何改进。

注释规范

HTML 的注释

格式:

  1. <!-- 我是 HTML 注释 -->

举例:

  1. <!--头部开始-->
  2. <div class="header"></div>
  3. <!--头部结束-->
  4. <!--内容开始-->
  5. <div class="main"></div>
  6. <!--内容结束-->
  7. <!--底部开始-->
  8. <div class="footer"></div>
  9. <!--底部结束-->

CSS 的注释

举例:

  1. <style type="text/css">
  2. /* 我是 CSS 注释 */
  3. p {
  4. font-weight: bold;
  5. font-style: italic;
  6. color: red;
  7. }
  8. </style>

注意:CSS 只有/* */这种注释,没有//这种注释。而且注释要写在<style>标签里面才算生效。

JavaScript 的注释

单行注释

【推荐】单行注释使用 //
【推荐】总是在单行注释符后留一个空格,以增加可读性
【推荐】单独注释应写在被注释对象的上方,不要写在某条语句的后面:

  1. // not good
  2. const active = true; // is current tab
  3. // good
  4. // is current tab
  5. const active = true;

多行注释

【推荐】多行注释使用 /** ... */,而不是多行的 //
【推荐】总是在多行注释的结束符前留一个空格

  1. // not good
  2. // 多行注释1
  3. // 多行注释2
  4. // good
  5. /*
  6. 多行注释1
  7. 多行注释2
  8. */

【推荐】不要把注释写在多行注释的开始符、结束符所在行

  1. // not good
  2. /* start
  3. end */
  4. // good
  5. /*
  6. start
  7. end
  8. */

文档注释

文档注释将会以预定格式出现在API文档中。
它以/**开头,以*/ 结束,其间的每一行均以*开头(均与开始符的第一个*对齐),且注释内容与*间留一个空格。

  1. /**
  2. * comment
  3. */

具体 @许敏(xumin-rusnu) 内容补充。

注释原则

  1. 避免零注释
  2. 注释不应掩饰错误
  3. 注释不应与代码重复
  4. 注释不应成为不清晰代码的借口
  5. 如果不能写清楚的注释,可能是代码有问题
  6. 混乱的注释会把代码搞得更乱
  7. 在注释中用于解释不规范的代码
  8. 针对引用代码提供原始出处链接
  9. 在可能提供帮助的地方引入指向外部参考的链接
  10. 文档类注释应遵循相关规范
  11. 使用特殊注释进行标记

1.避免零注释

糟糕的代码加上完全不存在的注释,往往就是压垮程序员的最后一根稻草。一个项目,可以没有文档,可以没有设计,但是如果没有注释,我们每一次阅读代码都是灾难性的。
当我们抱怨它一行注释都没有时,其实我们是在抱怨我们很难理解代码想要表达的含义,注释是间接原因,其根本原因还是代码。

2.注释不应掩饰错误

在代码开发的过程中,变量及函数的命名是非常重要的,我们在技术规范中也有讲到,应该准确的命名每一段代码,以便其他开发人员能够理解您的代码。
而不是像以下这种方式,因为函数的名称findE命名不够规范及命名描述不完整,通过描述性注释来描述函数的功能性,这是非常糟糕的注释方式。

  1. // not good
  2. // 按状态查找员工
  3. function FindE(status) {
  4. }
  5. // good
  6. function getEmployeesByStatus(status) {
  7. }

3.注释不应与代码重复

以下是典型的废话注释,读代码时代码本身就能很好的表达具体的含义,我们完全不需要看注释,并且注释也不会给我们提供更多有效的信息。

  1. // not good
  2. // 定义名称
  3. let name;
  4. // 定义年龄
  5. let age;

许多初级程序员会写太多注释,因为他们接受了入门指导老师的培训,就好比一个儿童刚学骑自行车,注释就像是训练轮,但在孩子长大时训练轮就应该去掉。
以下是典型的无用注释:

  1. // not good
  2. age = age + 1; // 年龄加1

4.注释不应成为不清晰代码的借口

当追求「代码自注释」的时候,即代码本身就拥有较高的可读性,比如更加合理代码结构设计,这也考验开发者的编程能力。

  1. // not good
  2. // 如果已经准备好数据,就渲染表格
  3. if (data.success && data.result.length > 0) {
  4. renderTable(data);
  5. }
  6. // good
  7. const isTableDataReady = data.success && data.result.length > 0;
  8. if (isTableDataReady) {
  9. renderTable(data);
  10. }
  11. //good
  12. init: function() {
  13. // 获取配置信息
  14. const config = getConfig();
  15. // 获取用户信息
  16. const userInfo = getUserInfo();
  17. // 根据配置和用户信息,进行初始化
  18. doInit(config, userInfo);
  19. // 如果存在自定义配置时的特殊逻辑
  20. if (config.custom) {
  21. ...
  22. }
  23. }

5.如果不能写清楚的注释,可能是代码有问题

写好代码,写可读代码,写简单代码,只要不是聪明的代码就行。
试图用一些奇淫技巧和复杂性来锻炼你的编程能力,与编写干净、更好的代码的意义恰恰相反。你的代码越难理解,当它在不可避免地崩溃时,调试问题就会变得越来越困难。

  1. // not good
  2. let a;
  3. let b = a?.name;
  4. // good
  5. let a;
  6. let b;
  7. if(!!a){
  8. b = a.name;
  9. }else{
  10. b = undefined;
  11. }

6.混乱的注释会把代码搞得更乱

代码在不断的演化,注释却不总是随之变动。不准确的注释比没注释坏的多

  1. // not good
  2. // 判断用户是否登陆(事实上这段逻辑已经变成判断用户是否活跃)
  3. if(customer.isActive())
  4. // good
  5. // 判断用户是否活跃
  6. if(customer.isActive())

7.在注释中用于解释不规范的代码

我们在团队协作开发的过程中,通常会发现别人不规范的代码,我们可以注释掉可能认为不需要或冗余的代码。

  1. // not good
  2. if (x == null) {
  3. }
  4. // good
  5. // 使用 == 时,undefined和null在结果上等价,此代码无意义,应该修改为更加严谨的 ===
  6. // if (x == null) {
  7. if (x === undefined || x === null) {
  8. }

8.针对引用代码提供原始出处链接

像大多数程序员一样,当我们遇到问题,在很多情况下会使用网上找到的代码,可使用注释提供对源代码的引用,可使将来的开发者能够获得完整的上下文。

  1. // good
  2. // JS递归的方式1到100求和 via https://www.cnblogs.com/Amerys/xxx.html
  3. function sumSort(num1,num2){
  4. ....
  5. }

9.在可能提供帮助的地方引入指向外部参考的链接

提供相关文档的链接可以帮助读者理解代码正在解决的问题,虽然这些信息可能出现在设计及需求文档中的某个地方,但一个适当位置的注释会让需要帮助的开发者快速索引到指定的位置。

  1. // good
  2. // 一键取号核心代码文档 via https://www.yuque.com/docs/xxx
  3. let phoneNumber = getPhoneNumber()

10.文档类注释应遵循相关规范

我们经常在开发过程会封装一些通用的函数方法,我们可以为其提供非常详情的注释,不仅包括方法的逻辑,入参的含义,甚至还包括具体示例。
通过注释规范中特定的关键字,我们还可以自动成长API文档,比如 JSDoc 规范

  1. /**
  2. * @function 处理表格的行
  3. * @description 合并Grid的行
  4. * @param grid {Ext.Grid.Panel} 需要合并的Grid
  5. * @param cols {Array} 需要合并列的Index(序号)数组;从0开始计数,序号也包含。
  6. * @param isAllSome {Boolean} :是否2个tr的cols必须完成一样才能进行合并。true:完成一样;false(默认):不完全一样
  7. * @return void
  8. * @author polk6 2015/07/21
  9. * @example
  10. * _________________ _________________
  11. * | 年龄 | 姓名 | | 年龄 | 姓名 |
  12. * ----------------- mergeCells(grid,[0]) -----------------
  13. * | 18 | 张三 | => | | 张三 |
  14. * ----------------- - 18 ---------
  15. * | 18 | 王五 | | | 王五 |
  16. * ----------------- -----------------
  17. */
  18. function mergeCells(grid: Ext.Grid.Panel, cols: Number[], isAllSome: boolean = false) {
  19. // Do Something
  20. }

11.使用特殊注释进行标记

有时我们发现某个可能的 bug,但因为一些原因还没法修复;或者某个地方还有一些待完成的功能,这时我们需要使用相应的特殊标记注释来告知未来的自己或团队合作者。
常用的特殊标记有两种:

  • FIXME: 说明此标示处代码有错误、需要修正
  • TODO: 说明此标示处有些功能将来要实现 ```javascript // good

// FIXME: 不应该使用 == if (x == null) {

// TODO: 总数应该从接口中返回 this.total = 0;

} ```