Quali sono le regole per l'inserimento automatico del punto e virgola (ASI) di JavaScript?


446

Bene, prima dovrei probabilmente chiedere se questo dipende dal browser.

Ho letto che se viene trovato un token non valido, ma la sezione del codice è valida fino a quel token non valido, viene inserito un punto e virgola prima del token se è preceduto da un'interruzione di riga.

Tuttavia, l'esempio comune citato per i bug causati dall'inserimento del punto e virgola è:

return
  _a+b;

..che non sembra seguire questa regola, poiché _a sarebbe un token valido.

D'altra parte, spezzare le catene di chiamate funziona come previsto:

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

Qualcuno ha una descrizione più approfondita delle regole?


22
V'è uno spec ...
Miles

33
@Miles Solo non al tuo link non funzionante ;-) ecma-international.org/publications/standards/Ecma-262.htm
Zach Lysobey

3
Vedi pag. 26 del PDF sopra citato.
ᴠɪɴᴄᴇɴᴛ


fare riferimento alla sezione 11.9 Inserimento automatico punto e virgola
Andrew Lam

Risposte:


455

Prima di tutto dovresti sapere quali affermazioni sono interessate dall'inserimento automatico del punto e virgola (noto anche come ASI per brevità):

  • dichiarazione vuota
  • var dichiarazione
  • dichiarazione di espressione
  • do-while dichiarazione
  • continue dichiarazione
  • break dichiarazione
  • return dichiarazione
  • throw dichiarazione

Le regole concrete dell'ASI sono descritte nella specifica §11.9.1 Regole di inserimento automatico del punto e virgola

Sono descritti tre casi:

  1. Quando viene rilevato un token ( LineTerminatoro }) non consentito dalla grammatica, viene inserito un punto e virgola prima di esso se:

    • Il token è separato dal token precedente da almeno uno LineTerminator.
    • Il token è }

    es :

    { 1
    2 } 3
    

    viene trasformato in

    { 1
    ;2 ;} 3;
    

    La NumericLiteral 1soddisfa la prima condizione, il seguente token è una terminazione di linea.
    La 2soddisfa la seconda condizione, la seguente token è }.

  2. Quando viene rilevata la fine del flusso di input di token e il parser non è in grado di analizzare il flusso di token di input come un singolo programma completo, un punto e virgola viene automaticamente inserito alla fine del flusso di input.

    es :

    a = b
    ++c
    

    si trasforma in:

    a = b;
    ++c;
    
  3. Questo caso si verifica quando un token è consentito da una certa produzione della grammatica, ma la produzione è una produzione limitata , un punto e virgola viene inserito automaticamente prima del token limitato.

    Produzioni limitate:

    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
    

    L'esempio classico, con il ReturnStatement:

    return 
      "something";
    

    viene trasformato in

    return;
      "something";
    

4
# 1: il token che non è consentito dalla grammatica di solito non è un terminatore di riga, vero (a meno che tu non intenda le produzioni limitate da # 3)? Pensa che dovresti omettere le parentesi. # 2 L'esempio non dovrebbe mostrare solo l'inserzione dopo ++cper chiarezza?
Bergi,

3
si prega di notare che l'ASI non deve effettivamente "inserire punti e virgola", solo per terminare la dichiarazione nel parser di un motore ...
Aprillion

1
cosa dice "flusso di input", significa "una linea"? Il "flusso di token di input" sta rendendo un po 'più difficile la comprensione
polarità

Il collegamento alle specifiche funziona per chiunque altro? Mi ha portato a una pagina quasi vuota che aveva un link morto su di essa.
creatore

per favore spiega come, secondo queste regole, l'esempio sotto di 太極 者 無極 而 生 di "a [LineBreak] = [LineBreak] 3" funziona ancora
Nir O.

45

Direttamente dall'ECMA-262, Specifiche della quinta edizione ECMAScript :

7.9.1 Regole di inserimento automatico del punto e virgola

Esistono tre regole di base per l'inserimento del punto e virgola:

  1. Quando, quando il programma viene analizzato da sinistra a destra, viene rilevato un token (chiamato token offensivo ) che non è consentito da alcuna produzione della grammatica, viene automaticamente inserito un punto e virgola prima del token offensivo se uno o più dei seguenti le condizioni sono vere:
    • Il token offensivo è separato dal token precedente da almeno uno LineTerminator.
    • Il token offensivo è }.
  2. Quando, quando il programma viene analizzato da sinistra a destra, viene rilevata la fine del flusso di input di token e il parser non è in grado di analizzare il flusso di token di input come un singolo ECMAScript completo Program, quindi un punto e virgola viene automaticamente inserito alla fine del flusso di input.
  3. Quando, mentre il programma viene analizzato da sinistra a destra, viene rilevato un token che è consentito da una certa produzione della grammatica, ma la produzione è una produzione limitata e il token sarebbe il primo token per un terminale o non terminale immediatamente dopo l'annotazione " [no LineTerminatorhere] " all'interno della produzione con restrizioni (e quindi un token di questo tipo è chiamato token con restrizioni) e il token con restrizioni è separato dal token precedente da almeno un LineTerminator , quindi un punto e virgola viene automaticamente inserito prima del token con restrizioni.

Tuttavia, esiste una condizione aggiuntiva aggiuntiva sulle regole precedenti: un punto e virgola non viene mai inserito automaticamente se il punto e virgola verrà quindi analizzato come un'istruzione vuota o se quel punto e virgola diventerebbe uno dei due punti e virgola nell'intestazione di forun'istruzione (vedere 12.6 .3).


44

Non riuscivo a capire troppo bene quelle 3 regole nelle specifiche - spero di avere qualcosa che sia un inglese più semplice - ma ecco cosa ho raccolto da JavaScript: The Definitive Guide, 6th Edition, David Flanagan, O'Reilly, 2011:

Citazione:

JavaScript non considera ogni interruzione di riga come un punto e virgola: in genere considera le interruzioni di riga come punti e virgola solo se non è in grado di analizzare il codice senza i punti e virgola.

Un'altra citazione: per il codice

var a
a
=
3 console.log(a)

JavaScript non considera la seconda riga come un punto e virgola perché può continuare ad analizzare l'istruzione più lunga a = 3;

e:

due eccezioni alla regola generale che JavaScript interpreta le interruzioni di riga come punti e virgola quando non è in grado di analizzare la seconda riga come continuazione dell'istruzione sulla prima riga. La prima eccezione riguarda le dichiarazioni return, break e continue

... Se appare un'interruzione di riga dopo una di queste parole ... JavaScript interpreterà sempre l'interruzione di linea come punto e virgola.

... La seconda eccezione riguarda gli operatori ++ e −− ... Se si desidera utilizzare uno di questi operatori come operatori postfix, devono apparire sulla stessa riga dell'espressione a cui si applicano. In caso contrario, l'interruzione di riga verrà trattata come punto e virgola e ++ o - verranno analizzati come operatore prefisso applicato al codice che segue. Considera questo codice, ad esempio:

x 
++ 
y

Viene analizzato come x; ++y;, non comex++; y

Quindi penso di semplificarlo, ciò significa:

In generale, JavaScript tratterà come continuazione del codice finché ha senso - ad eccezione di 2 casi: (1), dopo alcune parole chiave come return, break, continue, e (2) se vede ++o --su una nuova linea, poi si aggiunge la ;fine della riga precedente.

La parte su "trattalo come una continuazione del codice fintanto che ha senso" lo fa sentire come la corrispondenza avida dell'espressione regolare.

Detto questo, ciò significa che returncon un'interruzione di riga, l'interprete JavaScript inserirà a;

(citato di nuovo: se appare un'interruzione di riga dopo una di queste parole [come return] ... JavaScript interpreterà sempre quell'interruzione di linea come punto e virgola)

e per questo motivo, il classico esempio di

return
{ 
  foo: 1
}

non funzionerà come previsto, perché l'interprete JavaScript lo tratterà come:

return;   // returning nothing
{
  foo: 1
}

Non ci deve essere alcuna interruzione di linea immediatamente dopo return:

return { 
  foo: 1
}

affinché funzioni correttamente. E puoi inserire un ;te stesso se dovessi seguire la regola di utilizzare una ;dopo qualsiasi istruzione:

return { 
  foo: 1
};

17

Per quanto riguarda l'inserimento di punti e virgola e l'istruzione var, attenzione a dimenticare la virgola quando si utilizza var ma si estende su più righe. Qualcuno l'ha trovato nel mio codice ieri:

    var srcRecords = src.records
        srcIds = [];

Ha funzionato ma l'effetto è stato che la dichiarazione / assegnazione di srcIds era globale perché la dichiarazione locale con var nella riga precedente non era più applicata poiché quell'istruzione era considerata terminata a causa dell'inserimento automatico dei punti e virgola.


4
questo è il motivo per cui uso jsLint
Zach Lysobey il

1
JsHint / Lint direttamente nel tuo editor di codice con risposta immediata :)
dmi3y

5
@balupton Quando viene dimenticata la virgola che avrebbe terminato la riga, viene inserito automaticamente un punto e virgola. Al contrario di una regola era più simile a un "gotcha".
Dexygen,

1
Penso che il balupton sia corretto, è una differenza se scrivi: var srcRecords = src.records srcIds = [];in una riga e dimentica la virgola o scrivi "return a && b" e non dimentichi nulla ... ma l'interruzione di riga prima della a inserisce un punto e virgola automatico dopo return, quale è definito dalle regole di ASI ...
Sebastian il

3
Penso che la chiarezza della digitazione var( let, const) su ogni riga superi la frazione di secondo che serve per digitarla.
squidbe,

5

La descrizione più contestuale dell'inserimento automatico del punto e virgola di JavaScript che ho trovato proviene da un libro su Crafting Interpreters .

La regola di "inserimento automatico del punto e virgola" è quella dispari. Laddove le altre lingue assumono che la maggior parte delle nuove righe siano significative e solo alcune dovrebbero essere ignorate nelle dichiarazioni multilinea, JS assume il contrario. Tratta tutte le tue newline come spazi vuoti senza significato a meno che non incontri un errore di analisi. Se lo fa, torna indietro e prova a trasformare la nuova riga precedente in un punto e virgola per ottenere qualcosa di grammaticalmente valido.

Continua a descriverlo come faresti con l' odore del codice .

Questa nota di design si trasformerebbe in una diatriba di design se entrassi nei dettagli completi di come funziona, e tanto meno in tutti i modi in cui questa è una cattiva idea. È un casino. JavaScript è l'unica lingua che conosco in cui molte guide di stile richiedono punti e virgola espliciti dopo ogni affermazione, anche se teoricamente la lingua ti consente di sfuggirle.


Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.