如何在JavaScript中谨慎使用代码注释


Posted in Javascript onJune 21, 2019

前言

最令程序员头痛的事情莫过于阅读别人的代码,但其实时间一久阅读自己的代码也会很痛苦。 问题不是出在『自己或别人』,而是在代码本身。

必要的注释可以阐明实现细节和设计意图,以此节约自己和别人的时间。 然而很多时候注释起的作用却适得其反,比如自动生成的过多的注释分散阅读者的注意力, 而过期的失效的注释更是误导阅读者。

自动生成的注释

代码注释的泛滥想必是从Eclipse,Visual Studio等IDE开始的。 这些IDE提供了很多快捷功能,生成类/接口的骨架,具有Getter/Setter的属性等等。 如果用过IDE,下面的代码你一定不会陌生:

/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}

上述6行代码中的4行注释包含的信息量是0,既没有阐释参数args是何物,也没有说明main的用途。 然而大量的项目中都充斥着这样的自动生成注释。

『建议』:如果有参数或机制需要说明,请补充这些信息。否则请删除自动生成注释。 当然,用于生成文档的注释除外。

过多的注释

总会有人不厌其烦地编写长篇累牍的注释,或无微不至,或语焉不详,或晦涩难懂,或文采飞扬。 总之没有帮助我更快阅读代码的注释都是失败的注释。

为了说明问题,Harttle克隆了4.x Linux Kernel源码, 来大致分析一下其注释行数。 我们知道内核代码95%以上是C语言,所以统计.c文件就足够说明问题了。

➜ linux git:(master) git clone git@github.com:torvalds/linux.git --depth=1
➜ linux git:(master) find . -name "*.c" -o -name "*.h" -exec grep -E '^\s*((\*)|(/[/*]))' {} \; | wc -l
724804
➜ linux git:(master) find . -name "*.c" -o -name "*.h" -exec cat {} \; | wc -l
4018961
➜ linux git:(master) node
> 724804/(4018961-724804)
0.22002715717556875

内核仓库中的代码大概是402万行(未移除空行),其中注释72万行,占比22%。 Linux内核使用低级的C语言编写,涉及到复杂的CPU调度、内存管理,驱动程序。 因此注释会偏多一些,一般的项目注释应小于这个数值。

『建议』:如果你的代码中注释超过了20%,那么显然你过度注释了。

文件头注释

很多编辑器/IDE都会生成默认的文件头,例如:

/**
* @file /tmp/xxx.js
* @author harttle(yangjvn@126.com)
* @date 2016-08-30 22:33
* @description A XXX Implementation for XXX.
*/

文件头注释清晰地列出了文件的作者、功能描述等信息,看起来很有用。 不过这样的文件头存在的问题在于其维护性:

  • 其他人做小的修改时未必会修改@author,甚至连@author都不知道现在该文件已经面目全非。
  • 每次移动该文件,是否还需要花功夫更新 @file 信息?
  • 谁会在每次代码修改后记得更新 @description,于是@description也总是误导读者

文件头注释意在维护代码文件的元信息,以便在分发和部署过程中维护作者版权等信息。 然而在拥有版本控制的代码仓库中,这些信息不再需要手动维护,甚至可以通过git blame查看每一行代码的作者和时间信息。

『建议』:使用版本控制工具,删除文件头注释。版权信息可在构建或分发时生成。

冗余的注释

意图非常清楚的代码原则上不需要注释,多余的注释反而会造成维护性问题。 尤其是非英语母语的作者常常会掉到这个坑里。比如变量和函数的注释:

/*
* 获取用户数目
*/
function getUserCount(){
// 用户的列表
var userList = [];
}

这不是废话么!冗余的注释问题仍然在于维护性,例如调整函数功能、调整参数顺序, 或者更换变量名时我们不得不更新这些注释。否则这些注释就会误导下一个读者。

【建议】:不说废话。

抽取注释到标识符

可能读者也会有这样的经验:当我们写了一大段代码时,往往需要把它们分为几块。 然后在每一块开头添加一段注释。例如:

function calcTotalCharge(movies, user){
// Calculate Movie Charge
var movieCharge = 0;
for(var i=0; i<movies.length; i++){
var charge = 0;
if(movie.type === 'discount'){
charge = movie.charge * 0.8;
}
else if(movie.type === 'short'){
charge = movie.charge * 2;
}
else if(movie.type === 'normal'){
charge = movie.charge;
}
movieCharge += charge;
}

// Calculate User Charge
var rentCharge = 0;
if(user.isVIP1){
rentCharge = 10;
}
if(user.isVIP2){
rentCharge = 200;
}
else if(user.isVIP3){
rentCharge = 300;
}
else if(user.isVIP4){
rentCharge = 500;
}
// Calculate Total Charge
return movieCharge + rentCharge;
}

上述代码中的三段注释确实加速了阅读代码的速度, 但每当代码需要注释才能读懂时就应该警醒:是不是结构设计有问题。 对于上述代码,我们可以通过更加可复用的结构来消除注释:

function calcTotalCharge(movies, user){
return calcMovieCharge(movies) + calcUserCharge(user);
}
function calcMovieCharge(movies){
var total = 0;
for(var i=0; i<movies.length; i++){
total += calcSingleMovieCharge(movie);
}
return total;
}
function calcSingleMovieCharge(movie){
if(movie.type === 'discount') return movie.charge * 0.8;
else if(movie.type === 'short') return movie.charge * 2;
else if(movie.type === 'normal') return movie.charge;
return 0;
}
function calcUserCharge(user){
if(user.isVIP1) return 10;
else if(user.isVIP2) return 200;
else if(user.isVIP3) return 300;
else if(user.isVIP4) return 500;
return 0;
}

代码重构之后原来的注释就变得毫无意义,代码意图都被清晰的表述在标识符的命名中。 通常重构会带来代码量的减小,因为封装了分支、每个单元的逻辑也更加明确。

【建议】:当我们发现不得不进行注释时,需要警醒是否结构设计发生了问题。

有用的注释

至此Harttle已描述了这么多反模式,并非为了说明代码注释不重要。 而是为了说明『代码注释存在的意义在于帮助理解代码本身』。 例如在编写一些Trick,Polyfill,临时代码,以及复杂算法时,注释变得相当重要。 例如:

  • Tricks and Polyfills。有时简单的Trick就可解决多数问题问题, 为没必要编写复杂的普适算法, 例如检测浏览器的DOM API支持,检测AMD/CommonJS环境等等。 这时我们需要清晰地说明这些Trick的意图,甚至可以将这些代码抽离为polyfill模块。
  • 复杂算法。有时我们会编写数学性非常强的算法,一眼望去不知所云。 在开始这些算法前清晰地说明其意图何在,读者也就不必花大功夫读懂这些数学了。
  • 公有接口。模块的对外接口从逻辑上定义了模块类型,公有接口代码也更容易被人读到。 尤其是JavaScript接口:如果不注释options中到底是什么,谁晓得接口如何使用。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
加载 Javascript 最佳实践
Oct 30 Javascript
jquery delay()介绍及使用指南
Sep 02 Javascript
JavaScript中字符串(string)转json的2种方法
Jun 25 Javascript
JavaScript函数中关于valueOf和toString的理解
Jun 14 Javascript
Javascript基础学习笔记(菜鸟必看篇)
Jul 22 Javascript
JS前向后瞻正则表达式定义与用法示例
Dec 27 Javascript
JS出现失效的情况总结
Jan 20 Javascript
JavaScript实现父子dom同时绑定两个点击事件,一个用捕获,一个用冒泡时执行顺序的方法
Mar 30 Javascript
Angular实现的日程表功能【可添加及隐藏显示内容】
Dec 27 Javascript
解决vue2.0动态绑定图片src属性值初始化时报错的问题
Mar 14 Javascript
Vue动画事件详解及过渡动画实例
Feb 09 Javascript
JavaScript设计模式---单例模式详解【四种基本形式】
May 16 Javascript
简单了解JavaScript中常见的反模式
Jun 21 #Javascript
通过图带你深入了解vue的响应式原理
Jun 21 #Javascript
10种JavaScript最常见的错误(小结)
Jun 21 #Javascript
微信小程序开发注意指南和优化实践(小结)
Jun 21 #Javascript
使用Vue开发自己的Chrome扩展程序过程详解
Jun 21 #Javascript
如何测量vue应用运行时的性能
Jun 21 #Javascript
Vue组件之高德地图地址选择功能的实例代码
Jun 21 #Javascript
You might like
PHP 存取 MySQL 数据库的一个例子
2006/10/09 PHP
PHP下常用正则表达式整理
2010/10/26 PHP
ThinkPHP结合AjaxFileUploader实现无刷新文件上传的方法
2014/10/29 PHP
smarty内部日期函数html_select_date()用法实例分析
2015/07/08 PHP
linux平台编译安装PHP7并安装Redis扩展与Swoole扩展实例教程
2016/09/30 PHP
JavaScript中关于indexOf的使用方法与问题小结
2010/08/05 Javascript
js读取本地excel文档数据的代码
2010/11/11 Javascript
基于JavaScript实现继承机制之构造函数+原型链混合方式的使用详解
2013/05/07 Javascript
转义字符(\)对JavaScript中JSON.parse的影响概述
2013/07/17 Javascript
jQuery实现自定义下拉列表
2015/01/05 Javascript
在AngularJS应用中实现一些动画效果的代码
2015/06/18 Javascript
js实现鼠标点击文本框自动选中内容的方法
2015/08/20 Javascript
jQuery实现元素拖拽并cookie保存顺序的方法
2016/02/20 Javascript
js判断radiobuttonlist的选中值显示/隐藏其它模块的实现方法
2016/08/25 Javascript
js对字符串进行编码的方法总结(推荐)
2016/11/10 Javascript
详解jQuery lazyload 懒加载
2016/12/19 Javascript
基于javascript的Form表单验证
2016/12/29 Javascript
jquery hover 不停闪动问题的解决方法(亦为stop()的使用)
2017/02/10 Javascript
Python合并字符串的3种方法
2015/05/21 Python
python 换位密码算法的实例详解
2017/07/19 Python
Python如何快速上手? 快速掌握一门新语言的方法
2017/11/14 Python
对Python中列表和数组的赋值,浅拷贝和深拷贝的实例讲解
2018/06/28 Python
python多行字符串拼接使用小括号的方法
2020/03/19 Python
python安装本地whl的实例步骤
2019/10/12 Python
用Python画小女孩放风筝的示例
2019/11/23 Python
Python使用循环神经网络解决文本分类问题的方法详解
2020/01/16 Python
8种常用的Python工具
2020/08/05 Python
利用纯html5绘制出来的一款非常漂亮的时钟
2015/01/04 HTML / CSS
加拿大城市本地限时优惠:Buytopia.ca
2018/09/19 全球购物
js实现弹框效果
2021/03/24 Javascript
研究生自我鉴定范文
2013/10/30 职场文书
参观邀请函范文
2015/02/02 职场文书
2015年汽车销售员工作总结
2015/07/24 职场文书
教师节主题班会方案
2015/08/17 职场文书
不要在HTML中滥用div
2021/05/08 HTML / CSS
经典《舰娘》游改全新动画预告 预定11月开播
2022/04/01 日漫