簡體   English   中英

JavaScript 的自動分號插入 (ASI) 的規則是什么?

[英]What are the rules for JavaScript's automatic semicolon insertion (ASI)?

好吧,首先我應該問這是否依賴於瀏覽器。

我讀過如果發現無效令牌,但代碼部分在該無效令牌之前有效,如果分號前面有換行符,則會在令牌之前插入一個分號。

但是,對於由分號插入引起的錯誤引用的常見示例是:

return
  _a+b;

..這似乎不遵循這條規則,因為 _a 將是一個有效的標記。

另一方面,分解調用鏈按預期工作:

$('#myButton')
  .click(function(){alert("Hello!")});

有沒有人對規則有更深入的描述?

首先你應該知道哪些語句會受到自動分號插入的影響(也稱為 ASI):

  • 空語句
  • var語句
  • 表達式語句
  • do-while語句
  • continue聲明
  • break語句
  • return聲明
  • throw語句

ASI 的具體規則在規范§11.9.1 自動分號插入規則中進行了描述

描述了三種情況:

  1. 當遇到語法不允許的違規標記時,如果出現以下情況,則會在其前面插入分號:
  • 該標記與前一個標記至少有一個LineTerminator分開。
  • 令牌是}

例如

    { 1
    2 } 3

被轉化為

    { 1
    ;2 ;} 3;

NumericLiteral 1滿足第一個條件,后面的記號是行終止符。
2滿足第二個條件,下面的記號是}

  1. 當遇到輸入標記流的結尾並且解析器無法將輸入標記流解析為單個完整程序時,會在輸入流的末尾自動插入分號。

例如

    a = b
    ++c

轉換為:

    a = b;
    ++c;
  1. 這種情況發生在語法的某些產生式允許令牌,但產生式是受限產生式時,會在受限令牌之前自動插入分號。

限制生產:

    UpdateExpression :
        LeftHandSideExpression [no LineTerminator here] ++
        LeftHandSideExpression [no LineTerminator here] --
    
    ContinueStatement :
        continue ;
        continue [no LineTerminator here] LabelIdentifier ;
    
    BreakStatement :
        break ;
        break [no LineTerminator here] LabelIdentifier ;
    
    ReturnStatement :
        return ;
        return [no LineTerminator here] Expression ;
    
    ThrowStatement :
        throw [no LineTerminator here] Expression ; 

    ArrowFunction :
        ArrowParameters [no LineTerminator here] => ConciseBody

    YieldExpression :
        yield [no LineTerminator here] * AssignmentExpression
        yield [no LineTerminator here] AssignmentExpression

帶有ReturnStatement的經典示例:

    return 
      "something";

被轉化為

    return;
      "something";

我不能很好地理解規范中的這 3 條規則——希望有一些更簡單的英語——但這是我從 JavaScript 收集的內容:權威指南,第 6 版,David Flanagan,O'Reilly,2011:

引用:

JavaScript 不會將每個換行符都視為分號:它通常僅在無法解析沒有分號的代碼時才將換行符視為分號。

另一個引用:對於代碼

var a
a
=
3 console.log(a)

JavaScript 不會將第二個換行符視為分號,因為它可以繼續解析更長的語句 a = 3;

和:

當 JavaScript 無法將第二行解析為第一行語句的延續時,JavaScript 將換行符解釋為分號的一般規則有兩個例外。 第一個例外涉及 return、break 和 continue 語句

...如果在這些單詞中的任何一個之后出現換行符... JavaScript 將始終將該換行符解釋為分號。

...第二個例外涉及 ++ 和 −− 運算符 ...如果您想將這些運算符中的任何一個用作后綴運算符,它們必須與它們所應用的表達式出現在同一行。 否則,換行符將被視為分號,而 ++ 或 -- 將被解析為應用於后面代碼的前綴運算符。 考慮這段代碼,例如:

x 
++ 
y

它被解析為x; ++y; x; ++y; , 不像x++; y x++; y

所以我認為簡化它,這意味着:

一般來說,只要有意義,JavaScript 就會將其視為代碼的延續——除了 2 種情況:(1) 在一些關鍵字之后,如returnbreakcontinue ,以及 (2) 如果它看到++--在 a新行,然后它將添加; 在上一行的末尾。

“只要有意義就將其視為代碼的延續”的部分讓人感覺像是正則表達式的貪婪匹配。

如上所述,這意味着對於帶有換行符的return ,JavaScript 解釋器將插入一個;

(再次引用:如果在這些詞中的任何一個之后出現換行符 [例如return ] ... JavaScript 將始終將該換行符解釋為分號)

由於這個原因,經典的例子

return
{ 
  foo: 1
}

將無法按預期工作,因為 JavaScript 解釋器會將其視為:

return;   // returning nothing
{
  foo: 1
}

return之后必須沒有換行符:

return { 
  foo: 1
}

使其正常工作。 你可以插入一個; 如果你要遵循使用 a ;規則,你自己在任何聲明之后:

return { 
  foo: 1
};

直接來自ECMA-262,第五版 ECMAScript 規范

7.9.1 分號自動插入規則

分號插入的三個基本規則:

  1. 當從左到右解析程序時,遇到任何語法生成都不允許的標記(稱為違規標記)時,如果出現以下一項或多項,則在違規標記之前自動插入分號條件為真:
    • 違規標記與前一個標記至少有一個LineTerminator分開。
    • 有問題的令牌是}
  2. 當程序從左到右解析時,遇到輸入令牌流的結尾,並且解析器無法將輸入令牌流解析為單個完整的 ECMAScript Program ,則在末尾自動插入分號輸入流。
  3. 當程序從左到右解析時,遇到某個語法產生式允許的記號,但產生式是受限制的產生式,並且該記號將是緊跟注釋之后的終端或非終端的第一個記號“ [no LineTerminator here] ”在受限產生式中(因此這樣的記號被稱為受限記號),並且受限記號與前一個記號至少相隔一個LineTerminator ,然后在受限記號前自動插入分號.

但是,上述規則還有一個額外的覆蓋條件:如果分號隨后將被解析為空語句,或者如果該分號將成為for語句標題中的兩個分號之一,則永遠不會自動插入分號(參見12.6.3)。

關於分號插入和 var 語句,請注意在使用 var 但跨越多行時忘記逗號。 昨天有人在我的代碼中發現了這個:

    var srcRecords = src.records
        srcIds = [];

它運行了,但效果是 srcIds 聲明/賦值是全局的,因為前一行帶有 var 的本地聲明不再適用,因為由於自動分號插入,該語句被認為已完成。

我發現的關於 JavaScript 的自動分號插入的最符合上下文的描述來自一本關於Crafting Interpreters的書。

JavaScript 的“自動分號插入”規則很奇怪。 在其他語言假設大多數換行符是有意義的並且在多行語句中只應忽略少數的情況下,JS 假設相反。 除非遇到解析錯誤,否則它將所有換行符視為無意義的空格。 如果是這樣,它會返回並嘗試將前一個換行符轉換為分號以獲得語法上有效的內容。

他繼續描述它,就像你會編碼氣味一樣。

如果我詳細介紹它的工作原理,更不用說這是一個壞主意的所有各種方式,那么這個設計說明就會變成一個設計誹謗。 一團糟。 JavaScript 是我所知道的唯一一種語言,許多風格指南要求在每條語句后使用明確的分號,盡管理論上該語言允許您省略它們。

只是補充一下,

const foo = function(){ return "foo" } //this doesn't add a semicolon here.
(function (){
    console.log("aa");
})()

看到這個, 使用立即調用的函數表達式(IIFE)

JavaScript 中的大多數語句和聲明必須以分號結尾,但是,為了程序員的方便(更少的打字、風格偏好、更少的代碼噪音、更低的進入門檻),在某些源文本位置可以省略分號,與運行時根據規范中規定的一組規則自動插入分號。

總體規則:如果分號隨后將被解析為空語句,或者該分號將成為for語句的 header 中的兩個分號之一,則永遠不會自動插入分號。

規則1

如果 JavaScript 解析器遇到一個標記,則將自動插入分號,如果分號不存在,則兩者都不允許,並且該標記由一個或多個行終止符(例如換行符)與前一個分隔符,關閉大括號}或 do-while 循環的最后一個括號 ( )

換句話說:對於一個可運行的程序來說,語句總是需要終止的源文本位置,如果省略了語句終止符 ( ; ),則會自動插入它。 這條規則是 ASI 的核心。

規則 2

如果源文本不是有效的腳本或模塊,則將在程序末尾插入分號。 換句話說:程序員可以在程序中省略最后的分號。

規則 3

如果遇到分號,則將自動插入分號,如果分號不存在通常允許,但分號存在於幾個特殊源文本位置(受限制的產生式)之一中,為了避免歧義,這些位置明確禁止行終止符。

禁止使用線路終結器的限制產品有:

  • 在后綴++和后綴之前-- (因此換行符之后的一元遞增/遞減運算符將綁定到以下(不是前一個)語句,作為前綴運算符)
  • continuebreakthrowreturnyield之后
  • 在箭頭 function 參數列表之后,以及
  • 在 async function 聲明和表達式中的async關鍵字之后,生成器 function 聲明和表達式和方法,以及異步箭頭函數

該規范包含完整的詳細信息,以及以下實用建議:

給 ECMAScript 程序員的實用建議是:

  • 后綴 ++ 或 -- 運算符應與其操作數位於同一行。

  • return 或 throw 語句中的 Expression 或yield表達式中的 AssignmentExpression 應該與returnthrowyield標記在同一行開始。

  • breakcontinue語句中的 LabelIdentifier 應與breakcontinue標記位於同一行。

  • 箭頭函數參數的結尾和它的=>應該在同一行。

  • 異步 function 或方法之前的async令牌應與緊隨其后的令牌位於同一行。

這是 Web 關於這個主題的最佳文章

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM