注释是代码中最常见的组成部分.它们是另一种形式的文档,也是程序员最后才舍得花时间去写的.但是,对于代码的总体可维护性而言,注释是非常重要的一环.打开一个没有任何注释的文件就好像趣味冒险,但如果给你的时间有限,这项任务就变成了折磨.适度的添加注释可以解释说明代码的来龙去脉,其他开发者就可以不用从头开始阅读代码,而是直接去读代码的任意部分.编程风格通常不会包含对注释的风格约定,但我任务从注释的作用即可看出它们的重要性不容忽视.

js支持两种不同类型的注释: 单行注释和多行注释.

2.1 单行注释
单行注释有三种使用方法:

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

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

  • 被注释掉的大段代码(很多编辑器都可以批量注释掉多行代码).

单行注释不应当以连续多行注释的形式出现,除非你注释掉一大段代码.只有当需要注释一段很长的文本是才使用多行注释.

// 好的写法
if(condition){
    
    // 如果代码执行到这里,则表明通过了所有安全性检查
    allowed();
}

// 不好的写法
if(condition){
    // 如果代码执行到这里,则表明通过了所有安全性检查
    allowed();
}

// 不好的写法: 错误的缩进
if(condition){
// 如果代码执行到这里,则表明通过了所有安全性检查
    allowed();
}

// 好的写法
var result = something + somethingElse; // somethingElse不应当取值为null

// 不好的写法: 代码和注释之间没有间隔
var result = something + somethingElse;// somethingElse不应当取值为null

// 好的写法
// if(condition) {
//    doSomething();
//    thenDoSomethingElse();
// }

// 不好的写法
// 接下来这段代码非常难, 那么, 让我详细解释一下
// 这段代码的作用是首先判断天剑是否为真
// 只有为真时才会执行, 这里的条件是通过
// 多个函数计算出来的, 在整个回话生命周期内
// 整个值是可以被修改的
if(condition) {
    
    // 如果代码执行到这里, 则表明通过了所有安全性检查
    allowed();
}

2.2 多行注释

// 合法的多行注释
/* 我的注释 */
/* 另一段注释
这段注释包含两行*/
/*
又是一段注释
这段注释同样包含两行
*/

//逼格高,更加清晰.java风格注释
/*
 * 另一段注释
 * 这段注释包含两行文本
 */

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

// 好的写法
if(condition){
    
    /*
     * 如果代码段执行到这里
     * 说明通过了所有的安全性检测
     */
    allowed();
}

// 不好的写法: 注释之前无空行
if(condition){
    /*
     * 如果代码段执行到这里
     * 说明通过了所有的安全性检测
     */
    allowed();
}

// 不好的写法: 星号后没有空格
if(condition){
    /*
     *如果代码段执行到这里
     *说明通过了所有的安全性检测
     */
    allowed();
}

// 不好的写法: 错误的缩进
if(condition){

/*
 * 如果代码段执行到这里
 * 说明通过了所有的安全性检测
 */
    allowed();
}

// 不好的写法: 代码尾部注释不要用多行注释格式
var result = something + somethingElse; /*somethingElse 不应当取值为null*/

2.3 使用注释
通行的指导原则是:当代码不够清晰时添加注释, 当代码很明了时不应该添加注释.

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

// 好的写法
if(mode) {

    /*
     * 当mode为2时(原型到原型, 对象到对象), 这里只递归执行一次
     * 用来执行原型到原型的合并操作, 对象到对象的合并操作 
     * 将会被挂起, 在合适的时机执行
     */
    if(mode === 2) {
        Y.mix(receiver.prototype, supplier.prototype, overwrite,
                whitelist, 0, merge);
    }
    
    /*
     * 根据指定的模式类型, 我们可能会从元对象拷贝至原型中,
     * 或是从原型拷贝至接受对象中
     */
    from = mode === 1 || mode === 3 ? supplier.prototype : supplier;
    to = mode === 1 || mode === 4 ? receiver.prototype : receiver;
    
    /*
     * 如果supplier或receiver不含有原型属性时,
     * 则逻辑结束, 并返回undefined, 如果有原型属性,
     * 则逻辑结束并返回received
     */
    if(!from || !to) {
        return receiver;
    }
}else {
    from = supplier;
    to = receiver;
}

Y.mix()方法使用常量来决定如何处理. mode参数就是常量之一, 但仅仅通过这些数值无法解释他们各自代表的含义. 这里的注释非常棒, 因为它及时地解释了这里负责的决策逻辑.

2.3.2 可能被误认为错误的代码
另一个适合添加注释的好时机是当代码看上去有错误时. 在团队开发中, 总是会有一些好心的开发者在编辑代码时发现他人的代码错误, 就立即修复. 有时这段代码并不是错误的源头, 所以"修复"这个错误往往会制造其他的错误, 因此本次修改应当是可追踪的. 当你写的代码有可能会被别的开发者任务是有错误时, 则需要添加注释.

while(element && (element = element[axis])) {
    if((all || element[]TAG_NAME) &&
            (!fn || fn(elemtnt))) {
        return element;
    }
}

这个例子中, 开发者在while循环控制条件中使用了一个赋值运算符. 这不是一种标准用法, 并常常被检测工具认为是有问题的. 如果你对这段代码不熟悉, 读到这段没有注释的代码时, 很有可能误认为这是一个错误, 猜想作者的本意是使用比较运算符==而不是赋值运算符=. 这行末尾的注释说明作者是有意为之, 即赋值而非比较. 这样, 其他开发者读到这段代码时就不会将它"修复".

2.3.3 浏览器特性hack
js程序员常常会编写一些低效的、不雅的、彻头彻尾的脏代码, 用来让低级浏览器正常工作. 实际上这种情形是一种特殊的"可能被误认为错误的代码": 这种不明显的做浏览器特性Hack的代码可能隐含一些错误. 例子, 摘自YUI类库的Y.DOM.contains()方法.

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 {
        ret = Y_DOM._bruteContains(element, needle);
    }
}else if(element[COMPARE_DOCUMENT_POSITION]) {
    if(element === needle ||
            !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)){
        ret = true;
    }
}

return ret;

这段代码第7行包含一条很重要的注释. 尽管IE和Safari中都有内置方法contains(), 但如果needle不是一个元素时, 这个方法会报错. 所以只有当浏览器是Opera时才能用这个方法, 其他浏览器中needle必须是一个元素(nodeType是1). 这里关于浏览器的说明同样解释了为什么需要一个if语句, 这个注释不仅确保将来不会被他人误改动, 而且在代码编写者回头阅读自己这段代码时, 也会适当的针对新版本的IE和Safari的兼容情况作出调整.

2.4 文档注释
文档注释并不是js的组成部分, 但是应用很广泛. 最流行的格式是JavaDoc文档格式: 多行注释以单斜线加双星号(/**)开始, 接下来是描述信息, 其中使用@符号来表示一个或多个属性.

/**
如果需要深拷贝(deep copy), 请使用'clone()'
@method merge
@param {Object} 被合并的一个或多个对象
@return {Object} 一个新的合并后的对象
**/
Y.merge = function() {
    var args = arguments,
        i = 0,
        len = args.length,
        result = {};
        
    for(; i < len; ++i) {
        Y.mix(result, args[i], true);
    }
    
    return result;
}

所有的方法

应当对方法、期望的参数和可能返回的值添加注释描述.

所有的构造函数

应当对自定义类型和期望的参数添加注释描述.

所有包含文档化方法的对象

如果一个对象包含一个或多个附带文档注释的方法, 那么这个对象也应当适当地针对文档生成工具添加文档注释

伍陆柒
1.2k 声望25 粉丝

如果觉得我的文章对大家有用的话, 可以去我的github start一下[链接]