Javascript分号的自动添加
除了本来就不写分号的情况,JavaScript引擎还有一个特点,就是在应该写分号却没写的情况下,它会自动添加(Automatic Semicolon Insertion,简称ASI)。
var a = b + c // 等同于 var a = b + c;
但是,这种自动添加不是绝对的。如果下一行的开始可以与本行的结尾连在一起解释,就不会自动添加分号。
var a = 3 // 等同于 var a = 3; "abc" .length // 等同于 "abc".length
上面代码举了两个例子,每行的尾部都没有分号,JavaScript并不会自动添加分号,因为每行的结尾与下一行的开头可以放在一起解释。下面这个例子也不会自动添加分号。
3 * (2 * (4 + (3 - 5))) + (10 * (27 / 6)) // 等同于 3 * (2 * (4 + (3 - 5))) + (10 * (27 / 6))
这些例子还是比较容易看出来的,但是下面的例子就不那么容易发现了。它们都不会自动添加分号。
var a = b + c (d+e).toString(); /* 结果报错,因为两行连在一起, 解释为c(d+e), 即对函数 c 的调用 */ a = b /hi/g.exec(c).map(d); /* 解释为 a = b / hi / g.exec(c).map(d), 即把正则表达式的斜杠当作除法运算符 */ var a = "b" [ "red", "green" ].forEach(function(c) { console.log(c) }) /* 结果报错,因为两行连在一起, 解释为"b"["red", "green"], 即把字符串当作一个数组,按索引取值 */ var a = 0; var f = function(x) { return x } (a++) /* f等于0,因为(a++)被 * 视为匿名函数的调用 */ return a + b; return (a + b) obj.foo(arg1, arg2)
一般来说,在没有分号结尾的情况下,如果下一行起首的是(、 [ 、+、-、/这五个字符中的一个,分号不会被自动添加。只有下一行的开始与本行的结尾,无法放在一起解释,JavaScript引擎才会自动添加分号。
if (a < 0) a = 0 console.log(a) // 等同于下面的代码, // 因为0console没有意义 if (a < 0) a = 0; console.log(a)
另外,如果一行的起首是“自增”(++)或“自减”(--)运算符,则它们的前面会自动添加分号。
a = b = c = 1 a ++ b -- c console.log(a, b, c) // 1 2 0
之所以会得到“1 2 0”的结果,原因是自增和自减运算符前,自动被加上了分号。上面的代码实际上等同于下面的形式:
a = b = c = 1; a; ++b; --c;
如果continue、break、return和throw这四个语句后面,直接跟换行符,则会自动添加分号。这意味着,如果return语句返回的是一个对象的字面量,起首的大括号一定要写在同一行,否则得不到预期结果。
return { first: "Jane" }; // 解释成 return; { first: "Jane" };
由于解释引擎自动添加分号的行为难以预测,因此编写代码的时候不应该省略行尾的分号。
省略结尾的分号,还有一个问题。有些JavaScript代码压缩器不会自动添加分号,因此遇到没有分号的结尾,就会让代码保持原状,而不是压缩成一行。