写点什么

编写高质量可维护的代码:一目了然的注释

2021 年 5 月 04 日

编写高质量可维护的代码:一目了然的注释

前言


有一些人认为,好的代码是自我解释的。合适的命名和优秀的代码的确可以减轻开发人员阅读代码的工作量,对于不是特别复杂的代码可能确实可以做到自我解释。但并不是所有场景都可以做到这一点,我们一起来了解一下“注释”吧。


编程语言中对“注释”的解释


注释就是对代码的解释和说明。注释是开发人员在编写程序时,给一段代码的解释或提示,有助于提高程序代码的可读性。注释不会被计算机编译。


要不要加注释?为什么要加注释?


注释的存在就是为了方便自己的二次阅读和代码维护以及项目交接。可以更好的理解代码,有助于提高协作效率,加快开发进程。


试想,你添加了一段逻辑较为复杂的代码,几个月后再看,还能不能迅速看懂?你刚刚接手一个老项目,项目里基本没有注释且逻辑复杂,你能高效率的看懂代码和了解业务吗?


所以添加注释还是有一定必要滴。


基础篇


快捷键  windows:ctrl+/   mac: command+/


注释的分类


一、 HTML 中的注释

<div>  这是一行文字  <!-- 这是一行被注释的文字 --></div>
复制代码

二、CSS 中的注释

  • 在 .html 文件中

<style>  div {      /* color: #fff;  */   }</style>
复制代码
  • 在 .css 文件中

div { /* color: #fff;  */}
复制代码
  • 在 .less 或 .scss 文件中

div { /* color: #fff;*/  /* 多行注释*/ // font-size: 14px; // 单行注释 background: #000;}
复制代码

三、JS 中的注释

  • 用法

  • 可用于解释 JavaScript 代码,增强其可读性。

  • 也可以用于阻止代码执行。

  • 单行注释(行注释)—— 以 // 开头。任何位于 // 之后的文本都会被注释

// 定义一个空数组var ary = [];var ary2 = []; // 又定义一个空数组
复制代码
  • 多行注释(块注释)—— 以 /* 开头,以 */ 结尾。任何位于 /* 和 */ 之间的文本都会被注释

/* 这是多行注释 定义一个数组 */var ary = [];
复制代码
  • 用注释来阻止代码执行  —— 被注释的 JS 代码将不被执行

//alert("123")  // 执行时未弹出该信息alert("456")  // 执行时弹出该信息
复制代码
  • 函数注释

  • 一般以 /** 开头,以 */ 结尾。任何位于 /** 和 */ 之间的文本都会被注释

/** * 提交 * * @method onSubmit * @param {[Object]} 提交数据 * @return  {[Bollean]}  [返回是否提交成功 ] */const onSubmit = (params = {}) => {  const result = false;    if (params) {   result = true;  } return result;};
复制代码


四、特殊标记注释


  • TODO 在该注释处有功能代码待编写,待实现的功能在说明中会简略说明

  • FIXME 在该注释处代码需要修正,甚至代码是错误的,不能工作,需要修复,如何修正会在说明中简略说明

  • XXX 在该注释处代码虽然实现了功能,但是实现的方法有待商榷,希望将来能改进,要改进的地方会在说明中简略说明

  • NOTE 在该注释处说明代码如何工作

  • HACK 在该注释处编写得不好或格式错误,需要根据自己的需求去调整程序代码

  • BUG 在该注释处有 Bug


// TODO功能未完成,待完善// FIXME  待修复// XXX    实现方法待确认// NOTE   代码功能说明// HACK   此处写法有待优化// BUG    此处有 Bugconst arr = []
复制代码


Tips:

  • 为什么 // 注释可以在 .less 或 .scss 文件中使用,但是在 .html 和 .css 文件中不生效?

  • 在 MDN (https://developer.mozilla.org/zh-CN/docs/Web/CSS/Comments) 中关于 CSS 注释只有 /* */ 一种语法。但是在 LESS 和 SCSS 中支持注释的语法和 JS 中保持一致,有单行注释 // 和多行注释 /* */ 两种。单行注释编译之后不会被保留。

  • 单行注释为什么有时候写在代码上方,有时候写在代码后方?

  • 注释可以书写在代码中的任意位置。个人理解,一般写在代码上方的时候意为对后面一段代码的注释,而写在代码后方的时候意为对本行代码的注释。


注释写法规范


  • 文件注释

  • 位于文件头部,一般包含概要、作者、版本改动信息以及修改时间等内容

  /*   * 简述当前文件功能   * @author 作者名称   * @version 版本号 最近编辑时间   * @description 该版本改动信息   */
复制代码
  • 单行注释

  • 总是在 // 后留一个空格

  // 这是一行注释
复制代码
  • 多行注释

  • 总是保持星号纵向对齐(结束符前留一个空格)

  • 不要在开始符、结束符所在行写注释

  • 尽量使用单行注释代替多行注释

  • 注释函数时,推荐使用多行注释

  /*    这里有一行注释    这里有一行注释    这里有一行注释   */
复制代码
  • 函数注释

  • 其间每一行都以 * 开头,且与第一行第一个 * 对齐

  • 注释内容与 * 间留一个空格

  • 必须包含标签注释。例:

/*** 方法说明* @method 方法名* @for 所属类名* @param {参数类型} 参数名 参数说明* @return {返回值类型} 返回值说明*/
复制代码


注释常用标签用法


  • @type {typeName}

  • * 表示任何类型

  • ? 表示可以为 null

  • ! 表示不能为 null

  • [] 表示数组

/*** @type {number}*/var foo1;
/*** @type {*}* @desc 任何类型*/var foo2;
/*** @type {?string}* @desc string或者null*/var foo3;
复制代码


  • @param {} name - some description

  • 非必传参数需给参数名加上 []

  • 参数如有默认值需用 = 表示

  • 如果参数是 Object,可继续用 @param 对其属性进行详细说明

  • 若干个参数用 ... 表示


/** * @func * @desc 一个带参数的函数 * @param {string} a - 参数a * @param {number} b=1 - 参数b默认值为1 * @param {string} c=1 - 参数c有两种支持的取值  1—表示x  2—表示xx * @param {object} d - 参数d为一个对象 * @param {string} d.e - 参数d的e属性 * @param {object[]} g - 参数g为一个对象数组 * @param {string} g.h - 参数g数组中一项的h属性 * @param {string} [j] - 参数j是一个可选参数 */ function foo(a, b, c, d, g, j) {}
/** * @func * @desc 一个带若干参数的函数 * @param {...string} a - 参数a */function bar(a) {}
复制代码


拓展篇


IE 条件注释(IE5+)


IE 条件注释分为以下几种情况:


  • 只允许 IE 解释执行 <!--[if IE]><![endif]-->

  • 只允许 IE 特定版本解释执行 <!--[if IE 7]><![endif]-->

  • 只允许非 IE 特定版本执行注释 <!--[if !IE 7]><![endif]-->

  • 只允许高于或低于 IE 特定版本执行注释 <!--[if gt IE 7]><![endif]-->


<head>   <title>IE 条件注释</title>     <!-- 是 IE 时 -->    <!--[if IE]>         <link href="style.css" rel="stylesheet" type="text/css" />    <![endif]-->      <!-- 是 IE 7 时 -->    <!--[if IE 7]>       <link href="style.css" rel="stylesheet" type="text/css" />    <![endif]-->     <!-- 不是 IE 7 时 -->   <!--[if !IE 7]>        <link href="style.css" rel="stylesheet" type="text/css" />    <![endif]-->     <!-- 大于 IE 7 时 -->   <!--[if gt IE 7]>       <link href="style.css" rel="stylesheet" type="text/css" />    <![endif]-->    <!-- 小于 IE 7 时 -->    <!--[if lt IE 7]>       <link href="style.css" rel="stylesheet" type="text/css" />    <![endif]--></head>
复制代码


# (井号)注释 和  ''' (三引号)注释


  • # 一般出现在各种脚本配置文件中,用法与 JS 单行注释 // 基本相同。Python 中也常常用到

  • ''' 是 Python 中的多行注释语法,用两个 ''' 包含被注释的段落


# python 的单行注释一 print("I could have code like this.") # python 的单行注释二
# print("This won't run.") # 被注释的代码
''' 被三引号包裹的段落 可以随意折行 也可以注释代码 print("This won't run.")'''
复制代码


注释 “被执行” 了?


众所周知,注释的代码是不会被执行的。但是小编在查资料时看到了一段比较有意思的代码, Java 中的一行注释“被执行”了?


public class Test { public static void main(String[] args) {  String name = "赵大";  // \u000dname="钱二";  System.out.println(name); }}
复制代码


这段代码执行后的结果为钱二,也就是说在这段代码中,“被注释”的那行代码生效了!


这段代码的问题出在 \u000d 这串特殊字符上。\u000d 是一串 Unicode 字符,代表换行符。Java 编译器不仅会编译代码,还会解析 Unicode 字符。在上面这段代码把 \u000d 给解析了,后面的代码就到了下面一行,超出了被注释的范围(单行注释的注释范围仅在当前行),所以执行结果为 钱二 而非 赵大。(如下)


public class Test { public static void main(String[] args) {  String name = "赵大";  //  name="钱二";  System.out.println(name); }}
复制代码


所以本质上在代码执行的时候 name="钱二" 并没有被注释,而是被换了行(奇怪的知识增加了)。所以切记,注释确实是不会被执行的哦!


注释相关插件


在这里推荐几个个人认为比较好用的注释相关的 Vscode 插件,可在 setting.json 文件下自定义设置(可通过 '文件—首选项—设置',打开 Vscode 文件 settings.json )


  • koroFileHeader: https://marketplace.visualstudio.com/items?itemName=OBKoro1.korofileheader 在 Vscode 中用于生成文件头部注释和函数注释的插件

  • 文件头部添加注释

  • 在文件开头添加注释,记录文件信息/文件的传参/出参等

  • 支持用户高度自定义注释选项, 适配各种需求和注释。

  • 保存文件的时候,自动更新最后的编辑时间和编辑人

  • 快捷键:windowctrl+alt+imacctrl+cmd+ilinuxctrl+meta+i

  • 在光标处添加函数注释

  • 在光标处自动生成一个注释模板

  • 支持用户高度自定义注释选项

  • 快捷键:windowctrl+alt+tmacctrl+cmd+tlinuxctrl+meta+t

  • 快捷键不可用很可能是被占用了,参考这里 https://github.com/OBKoro1/koro1FileHeader/issues/5

  • 可自定义默认参数


  • Better Comments: https://marketplace.visualstudio.com/items?itemName=aaron-bond.better-comments 通过使用警报,信息,TODO 等进行注释来改善代码注释。使用此扩展,您将能够将注释分类为:

  • 快讯

  • 查询

  • 待办事项

  • 强调

  • 注释掉的代码也可以设置样式,以使代码不应该存在

  • 可自定义指定其他所需的注释样式


  • TODO Highlight: https://marketplace.visualstudio.com/items?itemName=wayou.vscode-todo-highlight 突出显示 TODO,FIXME 和任何关键字

  • 高亮内置关键字,可通过自定义设置覆盖外观

  • 也可自定义关键字


用事实说话


口说无凭,眼见为实。下面我们看下实际开发中的具体情况:


  • 没有注释

const noWarehousetemIds = beSelectSkucontainer.reduce((arr, itemId) => {    const res = Object.keys(selectRowskey[itemId]).every((skuId) => {      const sku = selectRowskey[itemId][skuId];      return !!sku.warehouseCode || lodashGet(warehouses, '[0].code');    });    if (!res) {      arr.push(itemId);    }    return arr;  }, []);  if (noWarehousetemIds.length > 0 || noStockItemIds.length > 0) {    const itemIds = Array.from(new Set([...noWarehousetemIds, ...noStockItemIds]));    const itemNames = itemIds.map(i => this.itemNameMap[i].itemName);    return Modal.warning({      title: '错误提示',      content: `“${itemNames.join(',')}”库存信息未完善,请完善库存信息`,    });  }
复制代码
  • 一般般的注释

// 遍历当前所有选中的sku,查找出没有库存的itemIdconst noStockItemIds = beSelectSkucontainer.reduce((arr, itemId) => {  const res = Object.keys(selectRowskey[itemId]).every((skuId) => {    const sku = selectRowskey[itemId][skuId];    return !!sku.stockQuantity;  });  if (!res) {    arr.push(itemId);  }  return arr;}, []);// 有一条sku的库存为空时进入校验if (noStockItemIds.length > 0) {  const itemNames = itemIds.map(i => this.itemNameMap[i].itemName);  return Modal.warning({    title: '错误提示',    content: `“${itemNames.join(',')}”库存信息未完善,请完善库存信息`,  });}
复制代码
  • 更好的注释

// 遍历当前所有选中的sku,查找出没有库存的itemIdconst noStockItemIds = beSelectSkucontainer.reduce((arr, itemId) => {    // selectRowskey是一个对象,以itemId为key,sku对象作为value,sku对象以skuId作为key,sku作为value,只有selectRowskey下所有itemId下的sku都有库存才算校验通过    /*        数据格式:        selectRowskey: {          12345678: { // itemId              123456: { // skuId              name: 'sku',              }          }        }      */    const res = Object.keys(selectRowskey[itemId]).every((skuId) => {        const sku = selectRowskey[itemId][skuId];        return !!sku.stockQuantity;    });    // 只要有一条sku没有库存时,就塞到arr中,返回给noStockItemIds数组    if (!res) {        arr.push(itemId);    }    return arr;}, []);// 有一条sku的库存为空时进入校验if (noStockItemIds.length > 0) {    // 根据id查找商品名称    const itemNames = itemIds.map(i => this.itemNameMap[i].itemName);    Modal.warning({        title: '错误提示',        content: `“${itemNames.join(',')}”库存信息未完善,请完善库存信息`,    });}
复制代码


看到上面这段代码可以很明显的体会到有没有注释以及注释写的清不清楚的重要性。若是写了注释但仍然看不懂,那还不如不写。


所以注释也不是随便写一写就可以的,要描述某段代码的功能,注明逻辑,让开发者可以”无脑“浏览。


之前在工作群中看到有人发过这样一张图(如下图),个人认为是一个很好的代码注释的范例:



结语


看到这里,对于注释的重要性各位已经有自己的认知。还有几点是我们写注释时需要注意的:


  • 注释内容要简洁、清楚明了。注释简述功能或实现逻辑即可,无需每行代码都添加注释

  • 代码若有修改,切记同步修改对应的注释。不要出现过期的注释,否则会起到反作用



头图:Unsplash

作者:亚格

原文:https://mp.weixin.qq.com/s/4Qq3-QFdyurL2ajbi4N0oQ

原文:编写高质量可维护的代码:一目了然的注释

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021 年 5 月 04 日 00:001047

评论

发布
暂无评论
发现更多内容

深入解析SpringMVC核心原理:从手写简易版MVC框架开始(SmartMvc)

Silently9527

Java mvc springmvc

全1子串算法求解、单元测试的必要性论述 John 易筋 ARTS 打卡 Week 32

John(易筋)

ARTS 打卡计划 全1子串算法求解 单元测试必要性

架构师训练营第二期 Week 10 总结

bigxiang

极客大学架构师训练营

第十周作业总结

hunk

极客大学架构师训练营

架构师训练营第2期 第10周作业

月下独酌

极客大学架构师训练营

6. 抹平差异,统一类型转换服务ConversionService

YourBatman

Spring Framework 类型转换 Converter ConversionService

提问开启创新-激发团队创新的提问法

Alan

个人成长 创新 团队文化 七日更 28天写作

《Python数据科学:关于使用Python处理数据需要了解的终极指南》PDF免费下载

计算机与AI

Python 数据科学

Week 10 模块分解

evildracula

学习 架构

架构师训练营 - 大作业二

lucian

程序员的bug修复宝典

程序员 经验总结 bug修复

Dubbo 微服务调用过程

梧桐

工具词典:数据

lidaobing

数据 28天写作

架构师训练营 - 大作业一

lucian

架构师训练营 第3期 第5周 作业和总结

ihiming

生产环境全链路压测建设历程 21:某快递 A 股上市公司的生产压测案例之彩蛋 2 中篇

数列科技杨德华

全链路压测 七日更

十、服务分解

Geek_28b526

关于Dubbo的原理

皮蛋

第 10 周 系统架构作业

心在那片海

第 10 周 系统架构总结

心在那片海

第10周作业

Rocky·Chen

Week10 作业

evildracula

架构师训练营第2期 第10周总结

月下独酌

极客大学架构师训练营

训练营第十周作业

大脸猫

极客大学架构师训练营

架构师训练营第十四周课程笔记及心得

Airs

第 5 周作业提交

Binary

极客大学架构师训练营

第 5 周学习总结

Binary

极客大学架构师训练营

冰河是谁?到底是干嘛的?

冰河

程序员 程序人生 架构师 冰河 冰河技术

训练营第十周总结

大脸猫

极客大学架构师训练营

NO.002-Java并发编程之多核硬件架构

葛一凡

操作系统 设计原则 硬件架构

第十周作业

hunk

极客大学架构师训练营

编写高质量可维护的代码:一目了然的注释-InfoQ