《编写可维护的JavaScript》——2.3 使用注释

    xiaoxiao2024-04-18  10

    本节书摘来自异步社区《编写可维护的JavaScript》一书中的第2章,第2.3节,作者:【美】Nicholas C. Zakas著,更多章节内容可以访问云栖社区“异步社区”公众号查看

    2.3 使用注释

    何时添加注释是程序员经常争论的一个话题。一种通行的指导原则是,当代码不够清晰时添加注释,而当代码很明了时不应当添加注释。比如这个例子中,注释是画蛇添足。

    // 不好的写法 // 初始化count var count = 10;

    因为代码中初始化count的操作是显而易见的。注释并没有提供其他有价值的信息。换个角度讲,如果这个值10具有一些特殊的含义,而且无法直接从代码中看出来,这时就有必要添加注释了。

    // 好的写法 // 改变这个值可能会让它变成青蛙 var count = 10;

    当然不可能因为修改了count的值它就变成了青蛙,但这的确是一个好的注释写法的例子,因为注释中给出了必要的信息,如果没有注释,你不可能获得这些信息。想象一下如果你修改了count的值它真的变成了青蛙,实在是让人困惑不解,一切都源于你没有写这句注释。

    因此,添加注释的一般原则是,在需要让代码变得更清晰时添加注释。

    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。如果有原型属性, * 则逻辑结束并返回receiver */ if (!from || !to) { return receiver; } } else { from = supplier; to = receiver; }

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

    2.3.2 可能被误认为错误的代码另一个适合添加注释的好时机是当代码看上去有错误时。在团队开发中,总是会有一些好心的开发者在编辑代码时发现他人的代码错误,就立即将它修复。有时这段代码并不是错误的源头,所以“修复”这个错误往往会制造其他错误,因此本次修改应当是可追踪的。当你写的代码有可能会被别的开发者认为有错误时,则需要添加注释。这里是另一段来自YUI源码的例子。while (element &&(element = element[axis])) { // 提示: 赋值操作 if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) { return element; } } 在这个例子中,开发者在while循环控制条件中使用了一个赋值运算符。这不是一种标准用法,并常常被检测工具认为是有问题的。如果你对这段代码不熟悉,读到这段没有注释的代码时,很可能误以为这是一个错误,猜想作者的本意是使用比较运算符==而不是赋值运算符=。这行末尾的注释说明作者是有意为之,即赋值而非比较。这样,其他开发者在读到这段代码时就不会将它“修复”。

    2.3.3 浏览器特性hackJavaScript程序员常常会编写一些低效的、不雅的、彻头彻尾的肮脏代码,用来让低级浏览器正常工作。实际上这种情形是一种特殊的“可能被误认为错误的代码”:这种不明显的做浏览器特性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]) { // gecko if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) { ret = true; } } return ret;

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

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)