1. 缩进
使用统一的缩进风格非常重要,适当的缩进,会提高代码的可读性。
(1)使用制表符进行缩进
每一个缩进层级都使用单独的制表符(tab character)表示,一个缩进层级是一个制表符,两个缩进层级是两个制表符。好处在于,制表符和缩进层级之间是一对一的关系,而且大部分的文本编辑器都可以配置制表符的展现长度。缺点在于不同系统和不同编辑器对于制表符的解释不一样。
(2)使用空格进行缩进
三种具体做法:2个空格、4个空格、8个空格来表示一个缩进。4个空格来标识一个缩进是一种比较折中的选择,使用空格缩进的好处是所有的系统和编辑器中,文本展现的格式不会有任何差异。
选择制表符或者空格来进行缩进只是一种个人偏好,但是千万不要混合使用,这么做会导致文件的格式很糟糕,而且需要做不少的清理工作。
2. 语句结尾
有赖于分析器的自动分号插入(Automatic Semicolon Insertio, ASI)机制,JavaScript代码省略分号也是可以正常工作的。大多数场景下ASI都会正确插入分号,但是ASI的分号插入规则非常复杂且很难记住,所以推荐不要省略分号。Douglas Crockford针对JavaScript提炼出的编程规范(下文统称为Crockford的编程规范)推荐 总是使用分号,同样,jQuery核心风格指南,Google的JavaScript风格指南以及Dojo编程风格指南都推荐不要省略分号。
3. 行的长度
JavaScript风格指南中很少提及行的长度,但是Crockford的代码规范中指定一行的长度为80个字符。
4. 换行
当一行长度达到单行最大字符数限制时,就需要手动将一行拆成两行。通常会在运算符后换行,下一行会增加两个层级的缩进。
// 好的做法:在运算符后换行,第二行追加两个缩进; myFunction(document, element, window, "some string value", true, 123, navigator); // 不好的做法:第二行只有一个缩进 myFunction(document, element, window, "some string value", true, 123, navigator); // 不好的做法:在运算符之前换行了 myFunction(document, element, window, "some string value", true ,123, navigator);
当给变量赋值时,第二行的位置应当与赋值运算符的位置保持对齐。比如:
var result = something + anotherting + somethingElse + anotherSomethingElse;
5. 空行
通常来讲,代码看起来应该像一系列可读的段落,而不是一大段揉在一起的连续的文本。有时一段代码的语义和另一段代码不相关,这时就应该使用空行将它们分割,确保语义有关联的代码展现在一起。
if (status === 1) { for (var i = 0, len = result.length;i < len; i++) { obj = result[i]; if (obj.id === 1) { console.log("yse"); } else { console.log("no"); } } }
一般来讲,在下面场景中添加空行是不错的主意。
(1)在方法之间。
(2)在方法中的局部变量(local variable)和第一条语句之间。
(3)在多行或单行注释之前。
(4)在方法内的逻辑片段之间插入空行,提高可读性。
但是没有一个编程规范对空行的使用给出任何具体建议,Crockford的编程规范也只提到要谨慎地使用空行。
1.6 命名
只要是写代码,都会涉及变量和函数,因此变量和函数命名对于增强代码可读性至关重要。JavaScript语言的核心ECMAScript,即是遵照了驼峰式大小写(Camel case)命名法。驼峰式大小写(Camel case)命名法是由小写字符开始的,后续每个单词首字母都大写。
var thisIsMyName; var anotherVariable;
1.6.1 变量和函数
变量名应当总是遵守驼峰大小写命名法,并且命名前缀应当是名词。以名次作为前缀可以让变量和函数区分开来,因为函数名前缀应当是动词。
// 好的写法 var count = 10; var myName = "haha"; var found = true; // 不好的写法:变量看起来像函数 var getCount = 10; var isFound = true; // 好的写法 function getName() { return myName; } // 不好的写法:函数看起来像变量 function theName() { return myName; }
命名长度应当尽可能短,并抓住重点。尽量在变量名中体现出值的数据类型。比如,命名count、length和size表明数据类型是数字,而命名name、title和message表明数据类型是字符串。但用单个字符命名的变量,诸如i、j和k通常在循环中使用。使用这些能够体现出数据类型的命名,可以让你的代码容易被别人和自己读懂。
要避免没有意义的命名。那些诸如foo、bar和tmp之类的命名也应当避免,当然开发者工具箱中还有很多这样可以随拿随用的名字,但不要让这些命名承载其他的附加含义。
对于函数和方法名来说,第一个单词应该是动词,这里有一些使用动词常见的约定。
动词 | 含义 |
can | 函数返回一个布尔值 |
has | 函数返回一个布尔值 |
is | 函数返回一个布尔值 |
get | 函数返回一个非布尔值 |
set | 函数用来保存一个值 |
1.6.2 常量
在ECMAScript6之前,JavaScript中并没有真正的常量的概念。为了区分普通的变量(变量的值是可变的)和常量(常量的值是初始化之后就不能变了),一种通用的命名约定应运而生。这个约定源自于C语言,它使用大写字母和下划线来命名,下划线用以分割单词。
1.6.3 构造函数
在JavaScript中,构造函数只不过是前面冠以new运算符的函数,用来创建对象。正如其他的命名约定一样,构造函数的命名风格也和本地语言(Native Language)保持一致,因此构造函数的命名遵照大驼峰命名法(Pascal Case)。Pascal Case 和 Camel Case都表示“驼峰大小写”,二者的区别在于Pascal Case以大写字母开始。这样做,可以将构造函数从变量和普通函数中区分出来。构造函数的命名也常常是名词,因为它们是用来创建某个类型的实例的。
1.7 直接量
JavaScript中包含一些类型的原始值:字符串、数字,布尔值、null和undefined。同样,也包含对象直接量和数组直接量。这其中,只有布尔值自解释(self-explanatory)的,其他的类型或多或少都需要思考一下它们如何才能更精准地表示出来。
1.7.1 字符串
在JavaScript中,字符串是独一无二的。字符串可以用双引号括起来,也可以用单引号括起来。和Java、PHP这些语言不同,使用单引号和双引号括起来的字符串在功能上并无不同。除了内部出现字符串界定符时需要转义之外,两种做法在功效上完全一致。
关于字符串还有另外一个问题需要注意,即创建多行字符串。这个特性并非来自JavaScript语言本身,却在几乎所有的(JavaScript)引擎中正常工作。
// 不好的写法 var longString = "Here‘s the story, of a man named haha.";
尽管从技术上讲这种写法是非法的JavaScript语法,但的确能在代码中创建多行字符串。通常不推荐这种写法,因为它是一种奇技淫巧而非语言特性,并且在Google的JavaScript风格指南中是明确禁止的。多行字符串的一种替代写法是,使用字符串连接符(+)将字符串分成多份。
// 好的写法 var longString = "Here‘s the story, of a man " + "named haha.";
1.7.2 数字
JavaScript中的数字类型只有一种,因为所有数字形式——整数和浮点数——都存储为相同的数据类型。还有一些其他的数字直接量格式来表示不同的数据格式。
// 整数 var count = 10; // 小数 var price = 10.0; var price = 10.00; // 不推荐的小数写法:没有小数部分 var price = 10.; // 不推荐的小数写法,没有整数部分 var price = .1; // 不推荐的写法,八进制已经被弃用了 var num = 010; // 十六进制写法 var num = 0xA2; // 科学计数法 var num = 1e23;
关于八进制数字的写法。长久以来,JavaScript支持八进制数字的写法是很多错误和歧义的根源。数字直接量010不是表示10,而是表示八进制中的8。大多数开发者对八进制格式并不熟悉,也很少用到,所以最好的做法是在代码中禁止八进制直接量。
1.7.3 null
在下列场景中应当使用null。
(1)用来初始化一个变量,这个变量可能赋值为一个对象。
(2)用来和一个已经初始化的变量比较,这个变量可以是也可以不是一个对象。
(3)当函数参数期望是对象时,用作参数传入。
(4)当函数的返回值是对象时,用作返回值传出。
还有下面一些场景不应当使用null
(1)不要使用null来检测是否传入了某个参数。
(2)不要用null来检测一个未初始化的变量。
// 好的用法 var person = null; // 好的用法 function getPerson() { if (condition) { return new Person("haha"); } else { return null; } } // 好的用法 var person = getPerson(); if (person !== null) { doSomething(); } // 不好的写法:用来和未初始化的变量比较 var person; if (person !== null) { doSomething(); } // 不好的写法:检测是否传入了参数 function doSomething(arg1, arg2, arg3, arg4) { if (arg4 !== null) { doSomething(); } }
理解null的最好方式是把它当做对象的占位符(placeholder)。这个规则在所有主流编程规范中都没有提及,但是对于全局可维护性来说至关重要。
1.7.4 undefined
undefined是一个特殊值,那么没有被初始化的变量都有一个初始值,即undefined,表示这个变量等待被赋值。
// 不好的写法 var person; console.log(person === undefined); //true
尽管这段代码能正常工作,这个值常常和返回“undefined”的typeof运算符混淆。事实上,typeof的行为也很让人费解,因为不管是值是undefined的变量还是未声明的变量,typeof运算结果都是“undefined”。
通过禁止使用特殊值undefined,可以有效地确保只在一种情况下typeof才会返回“undefined”:当变量未声明时。如果你使用了一个可能(或可能不会)赋值为一个对象的变量时,则将其赋值为null。
// 好的做法 var person = null; console.log(person === null); //true
将变量初始值赋值为null表明了这个变量的意图,它最终很可能赋值为对象。typeof运算符运算null的类型时返回“object”,这样就可以和undefined区分开了。
1.7.5 对象直接量
创建对象最流行的一种做法是使用对象直接量,在直接量中直接写出所有属性,这种方式可以取代先显示地创建Object的实例然后添加属性的这种做法。当定义对象直接量时,常常在第一行包含左花括号,每一个属性的名值对都独占一行,并保持一个缩进,最后右花括号也独占一行。Google的JavaScript风格指南非常推荐这种写法,Crockford的编程规范也推荐使用直接量代替Object构造函数,但没有给出具体的书写格式。
// 不好的写法 var book = new Object(); book.title = "Maintainable JavaScript"; book.author = "Nicholas C. Zakas"; // 好的写法 var book = { title: "Maintainable JavaScript", author: "Nicholas C. Zakas" };
1.7.6 数组直接量
和对象直接量类似,数组直接量是JavaScript中定义数组最简介的一种方式。不赞成显示地使用Array构造函数来创建数组。可以使用两个方括号将数组初始元素括起来,来代替使用Array构造函数的方式来创建数组。Google的JavaScript风格指南和Crockford的编程规范都推荐这样做。
// 不好的写法 var colors = new Array("red", "yellow", "blue"); var numbers = new Array(1, 2, 3, 4); // 好的写法 var colors = ["red", "yellow", "blue"]; var numbers = [1, 2, 3, 4];