JavaScript擁有自由的精神, ASI就是此精神的表現形式之一, ASI是
Automatic semicolon insertion的縮寫, 在許多語句後面可以省略分号, 當然很多小白還沒有發現這一點...首先分号是必不可少的, 因為回車符号在詞法分析階段就被全部殺掉了(特殊作用域除外),是以有了ASI有些人會認為回車符也是分隔符,其實回車符就是空白符,沒有任何意義....
ASI的引入友善了開發者的同時也帶來了很多坑........ 本文介紹了自動插入分号機制在return語句中的例子,以及在es5标準下的相應規則。
楔子
之前一直寫C,寫了一段時間JavaScript之後一直很很好奇一個東西。在C和Java等語言裡面,大括号的使用一般都是類似這樣的
int main(args[])
{
return 0;
}
- 1
- 2
- 3
- 4
而到JavaScript裡面則是這樣寫
function main(args){
alert("hello");
return 0;
}
起始的大括号不獨占一行了,覺得很疑惑,查了一些資料才知道,這是和JavaScript一個自動修複機制有關系,它總是希望通過自動插入分号來修正有缺損的程式,雖然我不知道這有什麼用。
自動插入分号機制
在《JavaScript語言精粹》這本書裡,這個機制被劃入到了JavaScript的毒瘤裡面,與之并列的前面的全局變量。
有些時候,不合時宜地插入分号,會在例如return語句裡面導緻嚴重的後果。
如果一個return語句要正确傳回一個值,這個值的表達式的開始部分必須和return位于同一行。
我們來看下面這個例子
return
{
status:true;
}
看起來這是要傳回一個包含對象,但是萬惡的自動插入分号處理後,傳回值變成了
undefined
,而且不會報任何的錯誤和警告。
如果我們把大括号這樣處理的話就能避免這個問題
return{
status:true;
};
自動插入分号的詳細規則
在es5标準中定義了自動分号插入規則,包括以下三個基本規則加上兩個前置條件。
前置條件
1.如果插入分号後解析結果是空語句,那麼不會自動插入分号。
例子:
if(i>j)
else k=l
這種情況下,if後面else前面是被解析為空語句,是以不加分号
2.如果插入分号後,它會成為
for
語句頭部的兩個分号之一,那麼也不會插入分号
例如:
for(a;b
)
這種情況下,雖然分行了,但是不會被插入分号。
基本規則
從左向右解析程式的時候,當遇到一個不符合任何文法産生式的
token
也就是違規标記的時候,那麼隻要滿足下列條件之一,就會在哪個标記之前自動插入一個分号
1、前一個标記和這個違規标記之前至少存在一個行終止符
2、違規的标記是
}
舉個栗子
{1
2}3
{1
;2;}3
在第一行和第二行的1、2不符合任何産生式,且它們之間有一個行終止符,是以會在數字2之前加分号,在第二行2後面也需要加一個分号,因為後面的違規标記是一個
}
左到右解析程式,tokens 輸入流已經結束,當解析器無法将輸入 token 流解析成單個完整 ECMAScript 程式 ,那麼就在輸入流的結束位置自動插入分号。
對于受限産生式,也就是下面的5個,我們把産生式 後面的 token 叫做受限 token,如果在 token 和 受限 token 間存在了至少一個行終止符,那麼會在受限 token 前自動加上 token。
受限的産生式隻限如下5個:
字尾表達式、continue語句、break語句、return語句、throw語句
如何預防這個毒瘤
1、字尾運算符
++
或
--
和它的操作數應該出現在同一行。
2、
return
或
throw
語句的表達式開始位置應該和
return
或
throw
token 同一行。
3、
break
continue
語句的标示符應該和
break
或
continue
token 同一行。
最重要的還是多加分号
來自leviscar的小貼士
為啥隻執行函數前面要加分号?
例如我之前看到的zepto.js的源碼開頭
;(function(undefined) {
if (String.prototype.trim === undefined) // fix for iOS 3.2
String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '')
}
- 5
主要是應對代碼合并壓縮時,由于缺少分号;帶來的錯誤。知道了上面的規則,在 ( 開頭的行前加分号就可以避免錯誤了。