编写可维护javascript代码(二)注释

264 阅读7分钟

对于代码的整体可维护性而言,注释是非常重要的一环。任何一个无注释的文件都是对后继者的折磨。适度的添加注释可以解释说明代码的来龙去脉,编程风格通常不会包含对注释的风格约定,但是 我认为在团队中,相同的注释风格可以减少误解和代码学习成本,同时也有利于做代码复盘

单行注释

独占一行的注释,用于解释下一行代码。这行解释之前总有一个空行 缩进层级和下一行代码保持一致

// good use
if (condition) {
  
    // 如果代码执行到这里 则允许通过
    allowed();
}

// bad use 注释之前没有空行
if (condition) {
    // 如果代码执行到这里 则允许通过
    allowed();
}

// bad use 注释没有正确的缩进
if (condition) {
// 如果代码执行到这里 则允许通过
    allowed();
}

在代码行的尾部的注释 代码结束到注释之间应该至少有一个缩进。注释(包含之前的代码部分)不应当超过单行最大字符数限制,超出就将这条注释放置于当前代码行的上方

// good use 
var result = something + somethingElse; // someThingElse不应当为null

// bad use 没有缩进
var result = something + somethingElse;// someThingElse不应当为null

单行注释不应当以连续多行注释的形式出现

// good use
// if (condition) {
//     allowed();
// }

// bad use 这里应该使用多行注释
// 这段代码的实现逻辑:
// 1、***
// 2、**
// 3、*
if (condition) {
  
    // 如果代码执行到这里 则允许通过
    allowed();
}

推荐指数: 🌟🌟🌟🌟🌟

多行注释

多行注释可以包裹跨行文本。以/开始,以/结束。

合法注释示例

// legal comment usage
/* 我的注释 */

/* 另一段注释
这段注释包含两行*/

/*
又是一段注释
这段注释同样包含两行
*/

尽管从技术角度看,这些注释都是合法的 但是我更推荐java风格的多行注释。java风格的注释至少包含三行: 第一行是/,第二行是以开始且和上一行的保持左对齐,最后一行是/。like this

// java style
/*
 * 另一段注释
 * 这段注释包含两行文本
 */

多行注释总会出现在将要描述的代码段之前,注释和代码之间没有空行间隔和单行注释一样,多行注释之前应该有一个空行,且缩进层级和其描述代码应该保持一致

// good use
if (condition) {
  
    /*
     * 如果代码执行到这里
     * 说明通过了所有的安全监测
     */
    allowed();
}

// bad use 没有空行
if (condition) {
    /*
     * 如果代码执行到这里
     * 说明通过了所有的安全监测
     */
    allowed();
}

// bad use 星号之后没有空格
if (condition) {
    /*
     *如果代码执行到这里
     *说明通过了所有的安全监测
     */
    allowed();
}

// bad use 错误的缩进
if (condition) {
  
    /*
     * 如果代码执行到这里
     * 说明通过了所有的安全监测
     */
    allowed();
}

// bad use 代码尾部注释不应该使用多行注释格式
var result = something + somethingElse; /*something 不能是null*/

推荐指数: 🌟🌟🌟🌟🌟

使用注释

何时添加注释是程序员经常争论的一个话题。通用的指导原则是,当代码不够清晰时添加注释,代码足够简单明了时不应当添加注释

// bad use

// 初始化count 
var count = 10;

如果count是个特殊的变量 可以将用途或者影响到的元素注释进来,初始化是显而易见的,没有任何价值

// good use

// 这个值更改后 会对金额总计 金额小计 应收金额字段进行重新计算
var count = 10

注释中给了必要信息,没有注释,其他维护人员或者参考此处代码人员需要去查找梳理逻辑,因此添加注释的一般原则是,在需要让代码变得更清晰时添加注释

推荐指数: 🌟🌟🌟🌟🌟

难以理解的代码

难以理解的代码,通常都应该加注释。根据代码的用途,你可以添加单行注释、多行注释。或者混用两种注释。关键是让其他人更容易读懂这段代码。like this 代码摘自于 YUI类库的Y.mix()方法

// good use
if (mode) {
	
    /*
     * 当mode为2时(原型到原型,对象到对象),这里只递归一次
     * 用来执行原型到原型的合并操作,对象到对象的合并操作
     */
    if (mode == 2) {
        Y.mix(r.prototype, s.prototype, ov, wh, 0, merge)
    }
  
  ...
  
    /*
     * 如果 supplier或receiver不包含原型属性的时候
     * 则逻辑结束并返回Unfeined, 如果有原型属性
     * 返回 receiver
     */
    if (!from || !to) {
        return receiver;
    }
}

如果不写注释,仅通过这些数值无法理解各自代表的含义。这里的注释可以及时地解释这里的复杂逻辑

可能被认为错误的代码

while (element && (element = element[axis])) { // 提示:赋值操作
    if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
        return element;
    }
}

在这个例子里面,开发者在while循环控制条件中使用了一个赋值运算符。这不是一个常规的标准做法,常常会被检测工具认为是有问题的。很可能会被误认为是一个错误,如果作者在这里加了注释,证明是有意为之,这样其他人在读到的时候,不会认为是个错误就不会将其修复。

浏览器特性hack

js程序中常常会编写一些低效的,不雅的,彻头彻尾的脏代码,用以支持低级浏览器正常工作。这种代码,因为做了浏览器特性hack,可能会隐含一些错误

var ret = false;

if (!needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
    ret = false;
} else if (element[CONTAINS]) {
  	
    // 如果needle不是ELEMENT_NODE时,IE和Safari下会有错误
    if (Y.UA.opera || needle[NODE_TYPE] === 1) {
        ret = element[CONTAINS](needle)
        ...
    } else {
        ...
    }
}

这行代码很关键,IE和Safari都有内置方法contains(),但如果needle不是一个元素的时候,这个方法会报错,所以只有当浏览器是Opera时才可以采用这个方法,其他浏览器中needle必须是 一个元素(nodeType是1)。这里关于浏览器的说明同样解释了为什么需要一个if语句,这个注释不仅确保将来不会被误改动,而且也有利于,编写者在回头阅读该代码时,可以适时针对新版本IE和Safari进行兼容调整

推荐指数: 🌟🌟🌟🌟

文档注释

文档注释,目前最流行的一种格式来自于JavaDoc文档格式:多行注释以单斜线加双星号(/**)开始,接下来是描述信息,其中以@符号来表示一个或者多个属性,以下是我个人建议,规范常用的代码片段

// good use: 变量

/*
 * 一个字符或者数字的变量
 * @type {(string|number)}
 */
var val;

// good use 函数: 传入的是单个变量

/*
 * 这个函数的作用的简单汇总
 * @param {number} a 变量描述
 * @param {string} b - 或者加上连字符便于阅读
 * @param {string} c - 这个参数有很长很长很长
 * 很长的描述
 */
function myFun(a, b, c) {
		
}

// good use 函数: 传入的参数是对象

/*
 * 这个函数的作用的简单汇总
 * @param {object} options - 传入的对象参数
 * @param {string} options.name - 对象的name属性
 * 属性的解释
 * @param {number} options.age - 对象的age属性 
 * 很长的描述
 */
function myFun(options) {
		
}

// good use 函数: 传入的参数是数组

/*
 * 任意多个参数的函数
 * 返回所有传入的参数和
 * @param {...number} nums - 参数个数任意,都是数字属性
 */
function sum(nums) {
		
}

// good use 函数:有返回值和描述

/*
 * 返回两个数之和
 * @description 描述使用场景等
 * @param {number} a - 数字
 * @param {number} b - 数字
 * @returns {number} 数字之和
 */
function sum(a, b) {
    return a + b;
}
  • // TODO功能未完成,待完善
  • // FIXME 待修复
  • // XXX 实现方法待确认
  • // NOTE 代码功能说明
  • // HACK 此处写法有待优化
  • // BUG 此处有 Bug
/*
 * ...
 * NOTE: 这里只是个测试用例函数
 * FIXME: 举个栗子
 * TODO: ...
 */
function fnExample(a, b) {
    ...
}

推荐指数: 🌟🌟🌟🌟🌟🌟