ECMAScript 行尾要不要加 Semicolon 一直是很爭議的議題,傳統都會加上 ;
,但最近則發現越來越多 Project 都不加,如 Vue、Redux … 等,到底 ASI 是什麼 ? 行尾不加 ;
會有什麼問題嗎 ?
Version
macOS Catalina 10.15.4
VS Code 1.44.2
Quokka 1.0.287
ECMAScript 2020
ASI
let f = _ => {
let x = 1, y = 2;
return
x + y;
}
f() // ?
若將 x + y
換行,結果會回傳 undefined
。
let f = _ => {
let x = 1, y = 2;
return;
x + y;
}
f() // ?
因為 ASI 會自動在 return
之後加上 ;
,因此回傳 undefined
。
let f = _ => {
return
{
title: 'FP in JavaScript'
}
}
f() // ?
若回傳 object literal 換行,結果也會回傳 undefined
。
let f = _ => {
return;
{ title: 'FP in JavaScript' };
}
f() // ?
因為 ASI 會自動在 return
之後加上 ;
,因此回傳 undefined
。
ASI
Automatic Semicolon Insertion
ECMAScript 會自動在行尾加上;
ASI Exception
ASI 並不是在所有行尾都會加上 ;
,以下是例外:
[]、{}、() 之中的行尾
let f = _ => {
let a = [
1,
2,
3
]
return a
}
f() // ?
在 []
中行尾不會有 ASI。
let f = _ => {
let o = {
title: 'FP in JavaScript',
price: 100
}
return o
}
f() // ?
在 {}
中行尾不會有 ASI。
let f = (x,
y,
z
) => {
return x + y + z
}
f(1, 2, 3) // ?
在 ()
中行尾不會有 ASI。
行尾為 . 或 ,
let f = o => {
return o.
price
}
f({ price: 100 }) // ?
行尾為 .
時不會有 ASI。
let f = () => {
let x = 1,
y = 2
return x + y
}
f() // ?
行尾為 ,
時不會有 ASI。
結尾為 ++ 或 –
let f = x => {
++
x
return x
}
f(3) // ?
行尾為 ++
時不會有 ASI。
let f = x => {
--
x
return x
}
f(3) // ?
行尾為 --
時不會有 ASI。
for()、while()、do、if()、else 之後沒 {}
時
let f = _ => {
let result = []
for (let i = 0; i < 3; i++)
result.push(i)
return result
}
f() // ?
for()
內若只有一行可省略 {}
,此時行尾不會有 ASI。
let f = _ => {
let result = []
let i = 0
while (i < 3)
result.push(i++)
return result
}
f() // ?
while()
內若只有一行可省略 {}
,此時行尾不會有 ASI。
let f = _ => {
let result = []
let i = 0
do
result.push(i++)
while (i < 3)
return result
}
f() // ?
do()
內若只有一行可省略 {}
,此時行尾不會有 ASI。
let f = x => {
if (x === 1)
return x + 1
else
return x + 2
}
f(1) // ?
f(2) // ?
if()
與 else
內若只有一行可省略 {}
,此時行尾不會有 ASI。
下一行為 [
、(
、+
、-
、*
、/
、,
、.
時
let f = _ => {
let a =
[1, 2, 3]
return a
}
f() // ?
下一行為 [
時,此時行尾不會有 ASI。
let f = (x, y) => {
let a =
(x + y)
return a
}
f(2, 1) // ?
下一行為 (
時,此時行尾不會有 ASI。
let f = (x, y) => {
let a = x
+ y
return a
}
f(2, 1) // ?
下一行為 +
時,此時行尾不會有 ASI。
let f = (x, y) => {
let a = x
* y
return a
}
f(2, 1) // ?
下一行為 *
時,此時行尾不會有 ASI。
let f = (x, y) => {
let a = x
/ y
return a
}
f(2, 1) // ?
下一行為 /
時,此時行尾不會有 ASI。
let f = o => {
return o
.price
}
f({ price: 100 }) // ?
下一行為 .
時,此時行尾不會有 ASI。
let f = _ => {
let x = 1
,y = 2
return x + y
}
f() // ?
下一行為 ,
時,此時行尾不會有 ASI。
Must Use Semicolon
雖然有 ASI 讓我們減少使用 ;
機會,但有些場合卻一定要使用 ;
。
for()
let f = _ => {
let result = []
for (let i = 0; i < 3; i++)
result.push(i)
return result
}
f() // ?
在 for()
內三個 expression 一定要用 ;
隔開。
Multiple Statement / Expression
let f = _ => {
let x = 0; x++
return x
}
f() // ?
let x = 0
與 x++
兩個 statement 或 expression 要寫在同一行時,一定要使用 ;
隔開。
switch()
let f = x => {
let result = 0
switch(x) {
case 0: result = x + 1; break
case 1: result = x + 2; break
case 2: result = x + 3; break
default: result = x + 4
}
return result
}
f(0) // ?
f(1) // ?
f(2) // ?
f(3) // ?
case
內的 statement 若與 break
要寫在同一行,要使用 ;
隔開。
以 [
開頭
let i = 1
[1, 2, 3].map(x => x + i) // ?
這是 ECMAScript 行尾不加 ;
常遇到問題,因為之前提到 ASI exception 中,當下一行為 [
時不會加上 ;
,因此 let i = 1
與 [1, 2, 3].map()
視為同一行。
let i = 1;
[1, 2, 3].map(x => x + i); // ?
傳統在行尾都加上 ;
一定沒問題。
let i = 1
;[1, 2, 3].map(x => x + i) // ?
在行首加上 ;
強制與上一行分開也行。
若使用 ASI,這是少數
;
要加在行首例外
以 Backtick 開頭
let x = 123
`${x}` // ?
這是 ECMAScript 行尾不加 ;
常遇到問題,因為之前提到 ASI exception 中,當下一行為 [
時不會加上 ;
,因此 let x = 123
與 ${x}
視為同一行。
let x = 123;
`${x}`; // ?
傳統在行尾都加上 ;
一定沒問題。
let x = 123
;`${x}` // ?
在行首加上 ;
強制與上一行分開也行。
若使用 ASI,這是少數
;
要加在行首例外
以 +
開頭
let x = '123'
+x // ?
這是 ECMAScript 行尾不加 ;
常遇到問題,因為之前提到 ASI exception 中,當下一行為 +
時不會加上 ;
,因此 let x = 123
與 +x
視為同一行。
let x = '123';
+x; // ?
傳統在行尾都加上 ;
一定沒問題。
let x = '123'
;+x // ?
在行首加上 ;
強制與上一行分開也行。
若使用 ASI,這是少數
;
要加在行首例外
IIFE
let inc = x => x + 1
(function() {
let result = inc(2)
console.log(result)
})()
這是 ECMAScript 行尾不加 ;
常遇到問題,IIFE 會在新的一行使用 ()
刮起來,最後使用 ()
執行之,因為之前提到 ASI exception 中,當下一行為 (
時不會加上 ;
,因此 IIFE 與 let inc = x => x + 1
視為同一行。
let inc = x => x + 1;
(function() {
let result = inc(2)
console.log(result)
})();
傳統在每行結尾都加上 ;
一定沒問題。
let inc = x => x + 1
;(function() {
let result = inc(2)
console.log(result)
})()
只在 (
前加上 ;
強制與上一行分開也行。
若使用 ASI,這是少數
;
要加在行首例外
let inc = x => x + 1
void function() {
let result = inc(2)
console.log(result)
}()
若你覺得 ;
放在最前面很怪,也可以改用 void
operator,它也會執行 IIFE,也不須再行首加 ;
。
Must Not Use Semicolon
有些場景剛好相反,一定不能使用 ;
。
for()
let f = _ => {
let result = []
for (let i = 0; i < 3; i++;)
result.push(i)
return result
}
f() // ?
for()
在括號內最後一個 expression 之後不能加上 ;
。
let f = _ => {
let result = []
for (let i = 0; i < 3;)
result.push(i++)
return result
}
f() // ?
for()
內若只寫兩個 expression 之後加上 ;
是合法的,因為其省略第三個 expression。
() 之後
let f = x => {
if (x === 1); {
return 2
}
}
f(1) // ?
f(2) // ?
在 ()
之後加上 ;
於 syntax 合法,但邏輯是錯的。
let f = x => {
if (x === 1);
return 2
}
f(1) // ?
f(2) // ?
在 ()
之後加上 ;
相當於分開兩個 statement。
May Use Semicolon
有些場景 ;
可加可不加,一般建議不加。
{} 之後
function f() {
return 1
}
f() // ?
Function declaration 使用 {}
時,在行尾 ;
可加可不加,一般建議不加。
let x = 1
if (x === 1) {
x = 2
}
x // ?
if
使用 {}
時,在行尾 ;
可加可不加,一般建議不加。
let f = x => {
let result = 0
switch(x) {
case 0: result = x + 1; break
default: result = x + 4
}
return result
}
f(0) // ?
switch
使用 {}
時,在行尾 ;
可加可不加,一般建議不加。
Summary
看到以上這麼多規則可能都暈了,事實上只有兩條規則
- ECMAScript 行尾不用加
;
- 一行以
[
、(
、+
、-
、*
、/
、,
、.
、backtick 開頭,且與上一行無關時,則要在上一行行尾
加上;
,或在本行行首
加上;
這個例外常出現在以下情況:
- 以
[]
array literal 開頭使用 Array.prototype 的 method,且與上一行無關時 - 以 backtick 開頭做 template string,且與上一行無關時
- 以
+
開頭將 String 轉 Number,且與上一行無關時 - 以
()
做 IIFE,且與上一行無關時
其他幾乎就很少遇到例外,可安心使用 ASI。
Conclusion
- ECMAScript 並不是全部都在行尾加上
;
就了事,有些情況是不能加;
- 反對行尾不加
;
者多半以[]
在一行開頭與 IIFE 例外為主要反對理由,實務上會遇到的例外大概只有 4 個 - ECMAScript 本來就不完美,但若要因為少數例外而因噎廢食,放棄原本美意的 ASI 則相當可惜
Reference
Eddy Chang, JavaScript 裡的語句用分號結尾是個選項嗎 ?
Dr. Axel Rauschmayer, Speaking JavaScript