JavaScript代码注释的常见陷阱与最佳实践

作者:函数茶馆时间:2026-10-23 08:44:04浏览:275

本文探讨JavaScript中代码注释的常见反模式,包括自动生成注释、过度注释、冗余注释及无效的文件头注释。通过对比Linux内核源码与重构前后的代码示例,阐述了何时应删除注释,何时需保留关键说明,并强调通过提取标识符和函数封装来替代解释性注释,以提升代码的可读性与可维护性。

前言

前言

程序员常面临的挑战是阅读他人代码,甚至长期后阅读自己的代码也会感到困难。问题的核心不在于阅读者的身份,而在于代码本身的质量。

适当的注释能阐明实现细节与设计意图,从而节省团队时间。但不当的注释往往适得其反,例如自动生成的大量注释会分散注意力,而过期或失效的注释则可能误导读者。

自动生成的注释

自动生成的注释

代码注释的泛滥现象始于Eclipse、Visual Studio等集成开发环境(IDE)。这些工具提供了生成类/接口骨架、Getter/Setter属性等快捷功能。使用过IDE的开发者对以下代码模式应不陌生:


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

上述6行代码中的4行注释包含的信息量为零,既未解释参数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字段,甚至可能不知道当前文件已发生巨大变化。
  • 每次移动文件时,是否还需花费精力更新@file信息?
  • 谁会在每次代码修改后记得更新@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的具体含义,无人知晓接口如何使用。

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

作者最新文章