注释是代码中最常见的组成部分.它们是另一种形式的文档,也是程序员最后才舍得花时间去写的.但是,对于代码的总体可维护性而言,注释是非常重要的一环.打开一个没有任何注释的文件就好像趣味冒险,但如果给你的时间有限,这项任务就变成了折磨.适度的添加注释可以解释说明代码的来龙去脉,其他开发者就可以不用从头开始阅读代码,而是直接去读代码的任意部分.编程风格通常不会包含对注释的风格约定,但我任务从注释的作用即可看出它们的重要性不容忽视.
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;
}
所有的方法
应当对方法、期望的参数和可能返回的值添加注释描述.
所有的构造函数
应当对自定义类型和期望的参数添加注释描述.
所有包含文档化方法的对象
如果一个对象包含一个或多个附带文档注释的方法, 那么这个对象也应当适当地针对文档生成工具添加文档注释
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。