《编写可维护的javascript》推荐的编码规范之——编程风格
javaScript编码规范
这正是本书的内容:如何站在团队的角度去写javascript代码。目标是解决多人开发的环境中很多工程师如何书写统一风格的代码的问题。对于个人来说,需要在一定程度上牺牲个人偏好、个人观点甚至个人英雄主义,但你所收获的将是一个能做大事的高效团队。
确定并一致的遵循约定比这个约定具体是什么更为重要。
“java语言编码规范”指出编码规范如此重要的几个原因:
- 软件生命周期中80%的成本消耗在了维护上。
- 几乎所有的软件维护者都不是它的最初作者。
- 编码规范提高了软件的可读性,它让工程师能够快速且充分的理解新的代码。
- 如果你将源码作为产品来发布,你需要确保它是可完整打包的,且像你创建的其他产品一样整洁。
编码规范包括:
- 编程风格
- 编程最佳实践
- 文件和目录的规划以及注释等方面
规范的制定和规范的执行是两码事。
检查工具:JSLint和JSHint(JSLint的一个分支项目),推荐使用JSHint .
一、编程风格
1、格式规则(formating rule)
1.1 缩进
- 推荐使用四个空格的缩进。你可以在编辑器中配置tab键插入4个空格;
- 也可以使用制表符缩进,但需要知道的是不同系统或不同编辑器很可能会看到不同的缩进。
决不能两者混用!
1.2 语句结尾
自动分号插入(Automatic Semicolon Insertion,ASI)机制虽然在大多数场景下都会正确插入分号,但它的分号插入规则非常复杂,因此推荐不要省略分号。
1.3 行的长度
建议将行长度限制在80个字符。
1.4 换行
建议在运算符后换行,下一行增加两个层级的缩进,如:
callAFunction( document, elm, 'some string value', true );
这个例子中,逗号是运算符,应当作为前一行的行尾。总是将一个运算符置于行尾,ASI就不会自作主张地插入分号,也就避免了错误的发生。又一例:
if( isLeapYear && isFebruary && day == 29 && itsYourBirthday && noPlans ) { waitAnotherFourYears(); } var result = something + anotherThing + yetAnotherThing + somethingElse + anotherSomethingElse;
1.5 空行
通常来讲,代码看起来应当像一系列可读的段落,而不是一大段揉在一起的连续文本。有时一段代码的语义和另一段代码不相关,这是就应该使用空行将它们分隔,确保语义相关的代码展现在一起。一般来讲,在下面这些场景中添加空行也是不错的注意。
- 方法之间。
- 方法中的局部变量和第一条语句之间。
- 注释之前。
- 方法内的逻辑片断之间
1.6 命名
Javascript语言的核心ECMAscript,即是遵循了驼峰式大小写(Camel case)命名法。
1.6.1 变量和函数
变量命名前缀应当是名词;函数名前缀应当是动词。如:
var count = 10; function getName () { return name; }
命名长度应该尽可能短,并抓住要点。尽量在变量名中体现出值的数据类型。比如,count、length 和 size 表明数据类型是数字;name、title和 message 表明数据类型是字符串。i、j 和 k 通常在循环中使用。
一些使用动词常见的约定:
- can:函数返回一个布尔值
- has:函数返回一个布尔值
- is:函数返回一个布尔值
- get:函数返回一个非布尔值
- set:函数用来保存一个值
当给变量赋值时,如果右侧是含有比较语句的表达式,需要用括号包裹:
// 好的写法 var aflag = (i < count); // 不好的写法 var flag = i < count;
另外,三元运算符应当仅仅在条件赋值语句中,而不要作为if语句的替代品
// 好的写法 var val = condition ? value1 : value2; // 不好的写法 condition ? doSomething(): doSomethingElse();
1.6.2 常量
使用大写字母和下划线来命名,下划线用以分隔单词。如:
var MAX_COUNT = 10;
1.6.3 构造函数
构造函数的命名通常是名词,且采用大驼峰命名法(Pascal Case);
1.7 直接量
1.7.1 字符串
使用单引号还是双引号都可以,重要的是你的代码从头到尾只保持一种风格。
1.7.2 数字
Javascript只有一种数字类型。
- 不推荐没有整数部分的小数写法:var price = .1;
- 不推荐已经被弃用的八进制写法:var num = 010;
1.7.3 null
null是一个特殊值,但我们常常误解它,将它和undefined 搞混。下列场景中应当使用null:
- 用来初始化一个可能赋值为一个对象的变量。
- 用来和一个已经初始化的变量做比较,这个变量可以是也可以不是一个对象。
- 当函数的参数期望是对象时,用作做参数传入。
- 当函数的返回值期望是对象时,用作返回值传出。
理解null最好的方式是将它当做对象的占位符,这一点对于全局可维护性来说至关重要。
1.7.4 undefined
undefined,表示这个变量等待被赋值。建议避免在代码中使用undefined。
1.7.5 对象直接量
推荐采用 对象直接量 的方式来创建对象。如:
var obj = { key1: value1, key2: value2, func: function() { }, keyn: valuen };
如果属性或者方法是私有的,应当以下划线作为前缀。
1.7.6 数组直接量
推荐采用 数组直接量 的方式来创建数组。
2、注释
2.1 单行注释
// 在双斜杠后敲入一个空格
单行注释有三种使用方法:
- 独占一行的注释,用来解释下一行代码。这行注释之前总是有一个空行,且缩进层级与下一行代码保持一致
- 在代码行尾的注释。代码结尾到注释之间至少有一个缩进。代码和注释不应当超过单行最大字符数限制,如果超过了,就将这条注释放置于当前代码行的上方。
if ( condition ) { // 行尾注释,与代码行尾保持一个缩进的距离 // 前面空一行; 缩进层级与下一行保持一致 allowed(); }
注释一个代码块时在连续多行使用单行注释是唯一可以接受的情况。多行注释不应当在这种情况下使用:
// if (condition) { // doSomething(); // thenDoSomethingElse(); //}
2.2 多行注释
推荐java风格的多行注释。Java风格的注释至少包含三行:第一行是 /* ,第二是以 * 开始且和上一行的 * 保持左对齐,最后一行是 */ 。如:
/* * Helper functions for managing events -- not part of the public interface. * Props to Dean Edwards' addEvent library for many of the ideas. */
2.3 何时使用注释
- 难于理解的代码
- 可能被认为错误的代码
- hack
2.4 (API)文档注释
推荐最流行的一种格式来自于JavaDoc文档格式:多行注释以单斜线双星好 /** 开始,接下来是描述信息,其中使用 @ 符号来表示一个或多个属性。如:
/** * 模板引擎路由函数 * 若第二个参数类型为 Object 则执行 render 方法, 否则 compile 方法 * @name template * @param {String} 模板ID (可选) * @param {Object, String} 数据或者模板字符串 * @return {String, Function} 渲染好的HTML字符串或者渲染方法 */
推荐使用文档生成工具来生成:YUIDoc 或 JSDoc Toolkit
2.5 注释声明
注释有时候可以用来给一段代码声明额外的信息。这些声明的格式以单个单词打头并紧跟一个双引号。可以使用的声明如下。
TODO,说明代码还未完成。应该包含下一步要做的事情。
// TODO:我希望找到一种更快的方式
doSomething();
HACk,表明代码走了一个捷径。应当包含为何使用hack的原因。这也可能表明该问题可能会有更好的解决方法。
/* * HACK:不得不针对IE做的特殊处理。我计划后续有时间时 * 重写这部分。这些代码可能需要在 v1.2 版本之前替换掉。 */ if (document.all) { dosomething(); }
XXX,说明代码是有问题的并应当尽快修复。
FIXME,说明代码是有问题的并应该尽快修复。重要性略次于XXX。
REVIEW,说明代码任何可能的改动都需要评审。
// REVIEW:有更好的方法吗? if (document.all) { doSomething(); }
以上这些声明可能在一行或多行注释中使用,并且应当遵循同一般注释类型相同的格式规则。
3、语句和表达式
所有的块语句,无论包含多行还是单行代码,都应当总是使用花括号。这包括:
- if
- for
- while
- do...while...
- try...catch...finally
3.1 花括号对齐方式
建议将左花括号放置在块语句中第一行代码的末尾,比如:
if ( condition ) { doSomething(); } else { doSomethingElse(); }
3.2 块语句间隔
有三种风格:
// Dojo 编程风格指南推荐 if(condition){ doSomething(); } // Google Javascript 风格指南推荐 if (condition) { doSometing(); } // jQuery 核心编码风格指南推荐 if ( condition ) { doSomething(); }
任选一种,保持一致。
3.3 switch 语句
case 语句的“连续执行”(也称穿越),即省略case末尾的break ,使得程序执行完一个case后继续执行下一个case。这是一个备受争议的问题。Douglas Crockford认为会这会导致bug的产生,但jQuery和Dojo编程风格指南则允许使用。看着办吧~
switch中还有一个需要讨论的议题是,是否需要default。作者的建议是,如果default什么也没有做,就把它省略掉。
3.4 with语句
强烈推荐避免使用该语句。
3.5 for 循环
有两种方法可以更改循环的过程,一个是break终止;一个是continue 终止本次,跳到下次。
Crockford的编程规范不允许使用continue。他主张代码中与其使用continue不如使用条件语句。如:
for ( var i = 0, leng = values.length; i < len; i++ ) { if ( i != 2 ) { process( values[i] ); } }
Crockford解析说这种方法更易于理解且不容易出错。但Dojo编程风格指南允许使用continue。作者推荐尽可能避免使用continue,但也没有理由完全禁止使用,它的使用应当根据代码可读性来决定。
3.5 for-in 循环
建议总是在该循环中使用hasOwnProperty()。不要使用该循环历遍数组。
4、变量、函数和运算符
4.1 变量声明
因为变量声明“提前”的缘故,所以建议总是将局部变量的定义作为函数内第一条语句,并采用单var模式。如:
funtion each (items, callback) { var i, len, value = 10, result = value + 10; for ( i=0, len=items.lengt; i<len; i++ ) { callback( items[i] ); } }
4.2 函数声明
和变量声明一样,函数声明也会被提前,因此推荐函数内部的局部函数应当紧接着变量声明之后声明。
函数声明不应当出现在语句块中!
4.3 函数调用
推荐的风格是,在函数名和左括号之间没有空格。这样做是为了将它和块语句区分开来。如:
doSomething(item); doSomething (item); // 反例 // 用做对比的块语句 while (item) { }
jQuery核心风格指南更进一步,它规定应当在左括号之后和右括号之前都加上空格。如:
doSomething( item );
这种风格是为了让参数更易读。但它也列出了这种风格的一些例外:
doSomething(function() { }); doSomething({ item: item }); doSomething([ item ]); doSomething(“Hi!”);
4.4 立即函数
为了让立即函数能够被一眼看出来,可以将函数用一对圆括号包裹起来,如:
var value = (function() { }());
4.5 严格模式
ES5引入了“严格模式”(strict mode),推荐尽可能使用严格模式,但不推荐在全局作用域中使用,应当在局部作用域中使用:
(function() { 'use strict'; // 代码 }());
4.6相等
由于(隐式)强制类型转换的缘故,不推荐使用 == 和 != ,而应当使用 === 和 !== 。
二元运算符前后必须使用一个空格来保持表达式的整洁
4.6.1 eval()
一个通用的原则是:
- 严禁使用Function
- 只在别无他法的时候使用eval()
- setTimeout()和setInterval()不要字符串形式而要用函数
jQuery核心风格指南禁止使用eval(),但有一个唯一的例外,即涉及到回调中解析JSON的情形。Google的javascript风格指南只允许在将Ajax的返回值转换为javascript值的场景下使用eval()。
ES5严格模式对于eval()有着严格的限制,禁止在一个封闭的作用域中使用它创建新的变量或函数。这条限制帮助我们避免了eval()先天的安全漏洞。
4.6.2 原始包装类型
避免使用String、Number或Boolean创建新对象。
二、编程实践
后续...
三、自动化
后续...