跳到主要內容

JS省略分號導致的災難

· 閱讀需 4 分鍾

挺長一段時間在糾結寫JavaScript代碼要不要打分號。這是一個個人風格問題,以下觀點僅代表個人喜好。

不要省略分號!不要省略分號!不要省略分號!

雖然js引擎執行代碼時會自動插入分號,但有些時候省略分號可能導致一些隱蔽的問題。

return語句

return後面會被自動插入分號,所以下面的代碼返回undefined而不是一個字符串。

function foo() {
return
"this will return undefined"
}

當然,這完全可以避免,我們可以將返回值寫在一行或者用括號包起來。

function foo() {
return (
"this is ok."
)
}

似乎這點也不是不省略分號就能避免的,如果您不清楚這條規則,下面的代碼可能會帶來疑惑:

function foo() {
return
'a long expresion' +
'another long expresion';
}
foo(); // undefined

OK,這不是重點,事實上現代編輯器足夠聰明,這樣的情況應該會將return後面的語句給你標注為灰色,你很容易就可以看到它們沒有正確的被執行。這裡只是順帶一提。我們來看更隱蔽的坑。

函數執行

考慮下面的代碼:

var foo = 0
var bar = 1

foobar()

('some condition' && 'another condition') ? foo = -1 : bar = -1

console.log(foo, bar)

function foobar() {
return function () { return false }
}

請問上面的代碼輸出什麼?答案是0 -1

我們的本意是在兩個條件滿足時給foo變量賦值-1,否則給bar變量賦值-1。這裡本該輸出-1 1,但由於語句以括號開頭,js執行引擎誤以為上一行的語句還沒有結束,正好foobar()返回了一個函數,於是上面的代碼相當於(為了更清晰我引入了一個變量):

var condition = foobar()('some condition' && 'another condition');
condition ? foo = -1 : bar = -1;

foobar()返回的函數總是返回false,所以被賦值的總是bar變量,這完全改變了我們的初衷!而且編輯器不會有提示:沒有語法錯誤。甚至運行時也不會直接出錯,而是在某個case下才命中bug。

解構賦值

接下來是我今天親身經歷的的場景。

ES2015(ES6)新增了一個解構賦值的特性,用於交換兩個變量的值那是非常的方便。例如交換a,b變量的值:[a, b] = [b, a]。當然解構賦值還有很多其他特性,可以參考MDN

考慮如下代碼:

var m = n = 0;
var swap = -1;
var matrix = [
[1, 2],
[3, 4]
];

//do something...

[matrix[m][n], swap] = [swap, matrix[m][n]]
[m, n] = [n, m]

console.log(matrix) // [[0, 2], [3, 4]]

OK,我們不用管代碼是要做什麼,反正就是想交換matrix[m][n]swap的值,再交換mn。期望的輸出應該是[[-1, 2], [3, 4]]。然鵝...

事實上在寫這篇博客之前我是贊成省略分號的。前面兩種情況我都習慣性會注意避免,也沒出過類似的錯誤。但終於還是不留神栽在了解構賦值上。

還是解釋一下,上面的解構賦值語句等價於:

[matrix[m][n], swap] = [swap, matrix[m][n]][m, n] = [n, m]

//不太清晰?由于m == n == 0,逗号表达式m,n的值为0,所以再等价于:
[matrix[m][n], swap] = [swap, matrix[m][n]] [0] = [n, m]
//即
[matrix[m][n], swap] = swap = [n, m]
//即
[matrix[m][n], swap] = [n, m]
//即
maxtrix[0][0] = 0;
swap = 0;

同樣,由於沒有語法錯誤,IDE同樣不會檢測到。

總結

暫時沒有遇到其他情況,不過有時候是防不勝防。咱一朝被蛇咬,十年怕井繩。添上分號他不香嘛。