编写可维护的JavaScript-基本的格式化

1. 缩进层级

坚持使用适度的缩进是万里长征的第一步,可增加代码的可读性。但是对于大多数变成风格来说,代码到底应该如何缩进并没有统一的共识,有以下两种主张。

a. 使用制表符进行缩进

一个缩进层级用一个制表符来表示,两个缩进层级用两个制表符,以此类推。这种方法的好处是:第一,缩进层级与制表符之间一一对应,符合逻辑。第二,文本编辑器可以配置制表符的展现长度,表现出不同的代码风格。但是这种方式的主要缺点是,系统对制表符的理解不一致。所以会出现在一个系统用一个编辑器打开的代码与另一个系统用相同的编辑器打开的代码展现效果不一样。这对于那些追求代码一致性的团队来说是一个很难受的东西。

b. 使用空格符来进行缩进

每个缩进层级由多个空格组成,其中主要有三种做法:2个空格表示一个缩进,4个空格表示一个缩进,8个空格表示一个缩进。事实上,很多团队选择折中的方案,即4个空格表示一个缩进。使用空格进行缩进的好处是,在所有系统的编辑器中,文件的展示格式不会有任何差异。可以在文本编辑器中配置敲击一个tab键时插入几个空格,。使用这种方法的缺点是,对于单个的开发者来说,使用一个没有配置好的文本编辑器是一件很折磨人的事情。

针对遇上两种方法,个人认为应该多选用使用4个空格表示一个缩进层级的做法,也是为了保持代码的一致性,方便维护等等!

2. 语句结尾

在日常的开发中,经常遇见写js时有的语句有结尾符号——分号,有的没有。这是一个很不好的习惯,这里针对该不该写结尾符作一个简单的说明。

有赖于分析器的自动分号插入(ASI)机制,JavaScript代码省略分号也是可以正常工作的。ASI会自动寻找代码中应当使用分号但是实际没有分号的位置,并插入分号。大多数情况下ASI都会正确的插入分号,但是ASI的分号插入规则非常复杂而且难记,在某些特定情况下ASI也会插入不恰当的分号。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//原始代码
function getData () {
return
{
title: "some title",
author: "andy"
}
}
//分析器会将它理解为
function getData () {
return;
{
title: "some title",
author: "andy"
};
}

在这段代码中,函数getData()的本意是返回一个包含一些数据的对象。让人人,return之后新起了一行,导致return后被插入了一个分号,这会导致函数返回值是undefined。可以通过将花括号移至与return同一行的位置来修复这个问题。

ASI在某些场景下是很管用的,特别是,有时候ASI可以帮助减少代码的错误。当某个场景我们认为不需要插入符号而ASI认为需要插入时,常常会发生错误。

3. 行的长度

在开发中,如果一行代码过长,会出现横向 滚动条,让开发人员觉得很别扭,即便是现在在宽屏幕的显示器中,保持适当的单行代码长度也是一个很好的习惯。

很多编程语言的规范都提到,一行代码的长度最长不应当超过80个字符。这个数值来源于很久之前文本编辑器的单行最多字符限制。超过80个字符要么换行,要么被隐藏,这些都是我们不希望看到的。即使是现在很多编辑器依然保留着单行最多不超过80个字符的长度限制。针对JavaScript编程,也建议单行字符不要超过80个字符。

4. 换行

当一行长度到达单行最大字符数限制时,就需要手动将一行拆成两行。通常我们会在运算符后换行,下一行会增加两个层级的缩进。(假设一个缩进层级为四个字符,如下例所示。)

1
2
3
4
5
6
7
8
9
//好的做法,在运算符后换行,第二行追加两个缩进
callAFunction(document, element, window, "some string value", true, 123,
navigator);
//不好的做法,第二行只有一个缩进
callAFunction(document, element, window, "some string value", true, 123,
navigator);
//不好的做法,在运算符之前换行了
callAFunction(document, element, window, "some string value", true, 123
, navigator);

在这个例子中,逗号是一个运算符,应当作为前一行的结尾。这个换行位置非常重要,以为ASI机制会在某些场景下载行结束的位置插入分号。总是将一个运算符置于行尾,ASI就不会自作主张地插入分号,也就避免了错误的发生。

只有换行后第二行需要两倍的缩进,其他行依旧按照以前的规则,该是一个缩进的用一个,该是两个缩进的用两个,但是这里有一个特例。当给变量赋值的时候,第二行的位置应当和赋值运算符的位置保持对齐,如:

1
2
var result = something +anotherThing + yetAnotherThing + something +
anotherSomethingElse;

5. 空行

在编程规范中,空行是常常被忽略的一个方面。通常来讲,代码看起来应当像一列可读的段落,而不是一大段揉在一起的连续文本。有时一段代码的语义和另一段代码不相关,这是就应该使用空行将它们分隔。

1
2
3
4
5
6
7
8
9
10
11
12
13
if (w1 && w1.length) {
for (i = 0, l = w1.length; i < 1; ++i) {
p = w1[i];
type = Y.Lang.type(r[p]);
if (s.hasOwnProperty(p)) {
if (merge && type =='object') {
Y.mix(r[p], s[p]);
} else if(ov || !p) {
r[p] = s[p];
}
}
}
}

给这段代码添加了几个空行之后,得到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (w1 && w1.length) {

for (i = 0, l = w1.length; i < 1; ++i) {
p = w1[i];
type = Y.Lang.type(r[p]);

if (s.hasOwnProperty(p)) {

if (merge && type =='object') {
Y.mix(r[p], s[p]);
} else if(ov || !p) {
r[p] = s[p];
}
}
}
}

这段代码只在每个流控制语句之前添加空行,这样做能使你更加流畅的阅读这些语句,一般来讲,在下面这些场景中添加空行也是不错的主意。

  • 在方法之间。
  • 在方法中的局部变量和第一条语句之间。
  • 在多行或单行注释之前。
  • 在方法内 的逻辑片段之间插入空行,提高可读性。

6. 命名

JavaScript语言的核心ECMAscript,即是遵照了驼峰式大小写命名法。驼峰式命名法是由小写字母开始,后续每个单词首字母大写。

a. 变量和函数

变量名命名前缀应当是名词,函数命名前缀应当是动词。

1
2
3
4
5
6
7
8
9
var name =  'andy'
var count = 10

function getName () {
return name;
}
function setName (name) {
this.name = name;
}

b. 常量

在JavaScript中并没有真正的常量的概念,然而,这并不能阻止开发者讲变量作为常量,为了区分常量和普通变量,一种通用的命名约定应运而生,即使用大写字母和下划线来命名,写划线用来分隔单词。

1
2
var MAX_COUNT = 10
var URL = "http://www.baidu.com"

c. 构造函数

在JavaScript中,构造函数只不过是前面冠以new运算符的函数,用来创建对象。其明明方式遵照大驼峰明明法,即所有单词首字母都大写。

1
2
3
4
function Person (name) {
this.name = name;
}
var me = new Personn('andy')

7. 直接量

JavaScript中包含一些类型的原始值:字符串、数字、布尔值、null和undefined。同样也包含对象直接量和数组直接量。这其中,只有布尔值是自解释的,其他的类型或多或少都需要思考一下它们如何才能更精确地表示出来。

a. 字符串

和Java、PHP这些语言不同,使用单引号和双引号括起来字符串在功能上并无不同,除了内部出现字符串界定符时需要转义之外。你需要关心的就是你的代码从头到尾应该保持一个风格。

b. 数字

在JavaScript中的数字类型只有一种,因为所有的数字形式,都存储为相同的数据类型。需要注意的是在JavaScript中八进制已经被弃用。

c. null值

null是一个特殊值,常将它与undefined弄混。在下列场景中应当使用null。

  • 用来初始化一个变量,这个变量可能赋值为一个对象。
  • 用来和一个已经初始化的变量比较,这个变量可以是也可以不是一个对象。
  • 当函数的参数期望是对象时,用作参数传入。
  • 当函数的返回值期望是对象时,用作返回值输出。

还有一些场景不适合使用null

  • 不要使用null来检测是否传入了某个参数。
  • 不要用null来检测一个未初始化的变量。

理解null最好的方法是将它当做对象的占位符。

d. undefined

undefined是一个特殊值,其中一个困惑就是undefined == null结果是true。然而,这两个值用途却各不相同。那些没有被初始化的变量都有一个初始值,即undefined,表示这个变量等待被赋值。

将变量初始值赋值为null表明了这个变量的意图,它最终很可能赋值为对象。

1
2
3
//正确的用法
var person = null;
console.log(person === null); //true

e. 对象直接量

创建对象最流行的一种做法是使用对象直接量,在直接量中直接写出所有属性。

1
2
3
4
5
//好的写法
var book = {
title: "some title",
author: "andy"
}

f. 数组直接量

和对象直接量相似,数组直接量是JavaScript中定义数组最简洁的一种方式,不赞成显示地使用Array构造函数来创建数组。

1
2
3
//好的写法
var colors = ['red', 'blue', 'gray'];
var numbers = [1, 2, 3, 4];