Welche Regeln gelten für das automatische Einfügen von Semikolons (ASI) in JavaScript?

445

Nun, zuerst sollte ich wahrscheinlich fragen, ob dies browserabhängig ist.

Ich habe gelesen, dass, wenn ein ungültiges Token gefunden wird, der Codeabschnitt jedoch bis zu diesem ungültigen Token gültig ist, ein Semikolon vor dem Token eingefügt wird, wenn ihm ein Zeilenumbruch vorausgeht.

Das häufigste Beispiel für Fehler, die durch das Einfügen von Semikolons verursacht werden, ist:

return
  _a+b;

..was dieser Regel nicht zu folgen scheint, da _a ein gültiges Token wäre.

Auf der anderen Seite funktioniert das Aufbrechen von Anrufketten wie erwartet:

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

Hat jemand eine ausführlichere Beschreibung der Regeln?

TR
quelle
22
Es gibt eine Spezifikation ...
Meilen
33
@ Meilen Nur nicht an Ihrem kaputten Link ;-) ecma-international.org/publications/standards/Ecma-262.htm
Zach Lysobey
3
Siehe S. 26 der oben zitierten PDF.
16.
Siehe Abschnitt 11.9 Automatische Semikolon-Einfügung
Andrew Lam

Antworten:

454

Zunächst sollten Sie wissen, welche Anweisungen von der automatischen Einfügung von Semikolons betroffen sind (der Kürze halber auch als ASI bezeichnet):

  • leere Aussage
  • var Aussage
  • Ausdrucksanweisung
  • do-while Aussage
  • continue Aussage
  • break Aussage
  • return Aussage
  • throw Aussage

Die konkreten Regeln von ASI sind in der Spezifikation §11.9.1 Regeln für das automatische Einfügen von Semikolons beschrieben

Drei Fälle werden beschrieben:

  1. Wenn ein Token ( LineTerminatoroder }) gefunden wird, das von der Grammatik nicht zugelassen wird, wird ein Semikolon davor eingefügt, wenn:

    • Das Token ist durch mindestens eines vom vorherigen Token getrennt LineTerminator.
    • Das Token ist }

    zB :

    { 1
    2 } 3

    verwandelt sich in

    { 1
    ;2 ;} 3;

    Das NumericLiteral 1erfüllt die erste Bedingung, das folgende Token ist ein Zeilenabschluss.
    Das 2erfüllt die zweite Bedingung, das folgende Token ist }.

  2. Wenn das Ende des Eingabestreams von Token erreicht ist und der Parser den Eingabe-Token-Stream nicht als einzelnes vollständiges Programm analysieren kann, wird am Ende des Eingabestreams automatisch ein Semikolon eingefügt.

    zB :

    a = b
    ++c

    verwandelt sich in:

    a = b;
    ++c;
  3. Dieser Fall tritt auf, wenn ein Token durch eine Produktion der Grammatik zugelassen wird, die Produktion jedoch eine eingeschränkte Produktion ist. Vor dem eingeschränkten Token wird automatisch ein Semikolon eingefügt.

    Eingeschränkte Produktionen:

    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

    Das klassische Beispiel mit ReturnStatement:

    return 
      "something";

    verwandelt sich in

    return;
      "something";
CMS
quelle
4
# 1: Das Token, das von der Grammatik nicht zugelassen wird, ist normalerweise kein Zeilenabschluss, oder (es sei denn, Sie meinen die eingeschränkten Produktionen von # 3)? Sie sollten die Klammern weglassen. # 2 Sollte das Beispiel ++caus Gründen der Übersichtlichkeit nicht nur die Einfügung nach zeigen ?
Bergi
2
Bitte beachten Sie, dass ASI keine "Semikolons einfügen" muss, nur um die Anweisung im Parser einer Engine zu beenden ...
Aprillion
1
was es heißt "Eingabestream", bedeutet das "eine Zeile"? Der "Input Token Stream" macht es etwas schwieriger zu verstehen
Unpolarität
Funktioniert der Spezifikationslink für andere Personen? Es führte mich zu einer fast leeren Seite, auf der sich ein toter Link befand.
Intcreator
Bitte erläutern Sie, wie nach diesen Regeln das folgende Beispiel von a 者 無極 而 生 von "a [LineBreak] = [LineBreak] 3" noch funktioniert
Nir O.
45

Direkt aus der ECMA-262, fünfte Ausgabe der ECMAScript-Spezifikation :

7.9.1 Regeln für das automatische Einfügen von Semikolons

Es gibt drei Grundregeln für das Einfügen von Semikolons:

  1. Wenn beim Analysieren des Programms von links nach rechts ein Token (als beleidigendes Token bezeichnet ) auftritt, das von keiner Grammatikproduktion zugelassen wird, wird vor dem fehlerhaften Token automatisch ein Semikolon eingefügt, wenn eines oder mehrere der folgenden Zeichen vorhanden sind Bedingungen ist wahr:
    • Das fehlerhafte Token ist durch mindestens eines vom vorherigen Token getrennt LineTerminator.
    • Das beleidigende Zeichen ist }.
  2. Wenn beim Analysieren des Programms von links nach rechts das Ende des Eingabestreams von Token festgestellt wird und der Parser den Eingabe-Token-Stream nicht als einzelnes vollständiges ECMAScript analysieren kann Program, wird am Ende des automatisch ein Semikolon eingefügt Eingabestrom.
  3. Wenn beim Programmieren des Programms von links nach rechts ein Token angetroffen wird, das durch eine bestimmte Produktion der Grammatik zulässig ist, die Produktion jedoch eine eingeschränkte Produktion ist und das Token das erste Token für ein Terminal oder ein Nichtterminal unmittelbar nach der Annotation ist " [no LineTerminatorhere] " innerhalb der eingeschränkten Produktion (und daher wird ein solches Token als eingeschränktes Token bezeichnet), und das eingeschränkte Token wird durch mindestens einen LineTerminator vom vorherigen Token getrennt . Anschließend wird automatisch ein Semikolon vor dem eingeschränkten Token eingefügt.

Es gibt jedoch eine zusätzliche übergeordnete Bedingung für die vorhergehenden Regeln: Ein Semikolon wird niemals automatisch eingefügt, wenn das Semikolon dann als leere Anweisung analysiert wird oder wenn dieses Semikolon eines der beiden Semikolons im Header einer forAnweisung wird (siehe 12.6 .3).

Jörg W Mittag
quelle
44

Ich konnte diese 3 Regeln in den Spezifikationen nicht allzu gut verstehen - ich hoffe, etwas zu haben, das klarer Englisch ist -, aber hier ist, was ich aus JavaScript zusammengetragen habe: The Definitive Guide, 6. Ausgabe, David Flanagan, O'Reilly, 2011:

Zitat:

JavaScript behandelt nicht jeden Zeilenumbruch als Semikolon: Zeilenumbrüche werden normalerweise nur dann als Semikolon behandelt, wenn der Code ohne die Semikolons nicht analysiert werden kann.

Noch ein Zitat: für den Code

var a
a
=
3 console.log(a)

JavaScript behandelt den zweiten Zeilenumbruch nicht als Semikolon, da die längere Anweisung a = 3 weiter analysiert werden kann.

und:

Zwei Ausnahmen von der allgemeinen Regel, dass JavaScript Zeilenumbrüche als Semikolons interpretiert, wenn die zweite Zeile nicht als Fortsetzung der Anweisung in der ersten Zeile analysiert werden kann. Die erste Ausnahme betrifft die Anweisungen return, break und continue

... Wenn nach einem dieser Wörter ein Zeilenumbruch angezeigt wird ... JavaScript interpretiert diesen Zeilenumbruch immer als Semikolon.

... Die zweite Ausnahme betrifft die Operatoren ++ und −−. Wenn Sie einen dieser Operatoren als Postfix-Operatoren verwenden möchten, müssen sie in derselben Zeile wie der Ausdruck stehen, auf den sie angewendet werden. Andernfalls wird der Zeilenumbruch als Semikolon behandelt und ++ oder - als Präfixoperator analysiert, der auf den folgenden Code angewendet wird. Betrachten Sie diesen Code zum Beispiel:

x 
++ 
y

Es wird als x; ++y;, nicht als analysiertx++; y

Ich denke, um es zu vereinfachen, bedeutet das:

In der Regel wird JavaScript behandelt es als Fortsetzung des Codes solange es Sinn macht - außer zwei Fälle: (1) , nachdem einige Schlüsselwörter wie return, break, continueund (2) , wenn sie sieht , ++oder --auf eine neue Zeile, dann wird es hinzufügen das ;am Ende der vorherigen Zeile.

Der Teil über "Behandle es als Fortsetzung des Codes, solange es Sinn macht" lässt es sich wie das gierige Matching des regulären Ausdrucks anfühlen.

Mit dem oben Gesagten bedeutet dies, dass returnder JavaScript-Interpreter bei einem Zeilenumbruch ein einfügt;

(Nochmals zitiert: Wenn nach einem dieser Wörter ein Zeilenumbruch angezeigt wird [z. B. return] ... JavaScript interpretiert diesen Zeilenumbruch immer als Semikolon.)

und aus diesem Grund das klassische Beispiel von

return
{ 
  foo: 1
}

funktioniert nicht wie erwartet, da der JavaScript-Interpreter dies wie folgt behandelt:

return;   // returning nothing
{
  foo: 1
}

Unmittelbar nach dem return: darf kein Zeilenumbruch auftreten.

return { 
  foo: 1
}

damit es richtig funktioniert. Und Sie können ein ;selbst einfügen, wenn Sie die Regel befolgen, nach einer ;beliebigen Anweisung ein zu verwenden:

return { 
  foo: 1
};
Unpolarität
quelle
17

Achten Sie beim Einfügen von Semikolons und der Anweisung var darauf, das Komma zu vergessen, wenn Sie var verwenden, aber mehrere Zeilen umfassen. Jemand hat das gestern in meinem Code gefunden:

    var srcRecords = src.records
        srcIds = [];

Es wurde ausgeführt, aber der Effekt war, dass die srcIds-Deklaration / Zuweisung global war, da die lokale Deklaration mit var in der vorherigen Zeile nicht mehr angewendet wurde, da diese Anweisung aufgrund der automatischen Einfügung von Semikolons als beendet angesehen wurde.

Sauerstoff
quelle
4
Aus diesem Grund benutze ich jsLint
Zach Lysobey am
1
JsHint / Lint direkt in Ihrem Code-Editor mit sofortiger Antwort :)
dmi3y
5
@balupton Wenn das Komma, das die Zeile beendet hätte, vergessen wird, wird automatisch ein Semikolon eingefügt. Im Gegensatz zu einer Regel war es eher wie ein "Gotcha".
Dexygen
1
Ich denke, Balupton ist richtig, es ist ein Unterschied, wenn Sie schreiben: var srcRecords = src.records srcIds = [];in einer Zeile und vergessen Sie das Komma oder Sie schreiben "return a && b" und vergessen nichts ... aber der Zeilenumbruch vor dem a würde nach der Rückkehr ein automatisches Semikolon einfügen, was durch die ASI-Regeln definiert ist ...
Sebastian
3
Ich denke, die Klarheit der Eingabe var( let, const) in jeder Zeile überwiegt den Bruchteil einer Sekunde , die für die Eingabe benötigt wird.
Squidbe
5

Die kontextuellste Beschreibung der automatischen Semikolon-Einfügung von JavaScript, die ich gefunden habe, stammt aus einem Buch über das Herstellen von Dolmetschern .

Die JavaScript-Regel "Automatisches Einfügen von Semikolons" ist die ungerade. Wo andere Sprachen davon ausgehen, dass die meisten Zeilenumbrüche sinnvoll sind und nur wenige in mehrzeiligen Anweisungen ignoriert werden sollten, geht JS vom Gegenteil aus. Alle Ihre Zeilenumbrüche werden als bedeutungslose Leerzeichen behandelt, es sei denn, es tritt ein Analysefehler auf. Wenn dies der Fall ist, geht es zurück und versucht, die vorherige neue Zeile in ein Semikolon umzuwandeln, um etwas grammatikalisch Gültiges zu erhalten.

Er beschreibt es so, als würden Sie den Geruch codieren .

Diese Designnotiz würde sich in eine Design-Schande verwandeln, wenn ich ausführlich darauf eingehen würde, wie das überhaupt funktioniert, geschweige denn auf die verschiedenen Arten, auf die das eine schlechte Idee ist. Es ist ein Chaos. JavaScript ist die einzige Sprache, die ich kenne, in der viele Styleguides nach jeder Anweisung explizite Semikolons verlangen, obwohl Sie sie in der Sprache theoretisch entfernen können.

jchook
quelle