Automatic semicolon insertion (englisch für automatische Semikolon-Einfügung, kurz ASI) bezeichnet ein Verhalten des JavaScript-Parsers. Die meisten Anweisungen in JavaScript werden durch ein Semikolon abgeschlossen. In vielen Fällen muss dieses Semikolon jedoch nicht explizit im Quelltext angegeben werden, da es der Parser von sich aus ergänzt. Das Verhalten ist umstritten, da es einerseits das Programmieren erleichtert, indem viele Semikola ausgelassen werden können, andererseits in Spezialfällen zu unerwartetem Verhalten und damit zu schwer zu entdeckenden Programmierfehlern führen kann.

Definition Bearbeiten

Die automatic semicolon insertion ist im ECMAScript-Standard festgelegt und gilt damit neben JavaScript auch für ActionScript. In den folgenden Fällen fügt der Parser automatisch ein Semikolon ein:[1]

  • Der Parser stößt auf ein unerwartetes Token und dieses Token ist
  • Der Parser stößt an das Ende des Quelltextes, obwohl die Grammatik dies nicht erlaubt.
  • Der Parser stößt auf ein Token, das vom vorherigen Token durch einen Zeilenumbruch getrennt ist, das mit diesem aber eine Anweisung bilden würde, innerhalb der ein Zeilenumbruch explizit verboten ist. Dies trifft etwa auf return rueckgabewert zu, hier ist zwischen dem Schlüsselwort return und dem Rückgabewert kein Zeilenumbruch erlaubt.

Das Semikolon wird vor dem unerwarteten Token bzw. vor dem Ende des Quelltextes eingefügt. Ein Semikolon, das nach diesen Regeln automatisch eingefügt würde, wird dennoch nicht eingefügt, falls einer der beiden folgenden Fälle zutrifft:

  • Das eingefügte Semikolon bildet eine leere Anweisung.
  • Das eingefügte Semikolon wird eines der beiden Semikola im Kopf einer For-Schleife.

Details Bearbeiten

Bei einzeiligen Kommentaren gehört der Zeilenumbruch, der den Kommentar abschließt nicht zum Kommentar dazu, stellt also einen Zeilenumbruch im Sinne der obigen Regeln dar. Mehrzeilige Kommentare werden ebenfalls wie Zeilenumbrüche behandelt. Das eingefügte Semikolon steht nach dem Zeilenumbruch, während ein explizit angegebenes Semikolon meist ans Ende der Zeile gesetzt wird; dies macht jedoch für den Parser keinen Unterschied.

Die Anweisungen, in denen ein Zeilenumbruch verboten ist, sind die Postinkrement- und -dekrementoperatoren, continue und break mit Label, sowie return und throw mit Rückgabewert. Mit der Version 6 von ECMAScript kommen noch yield und die Pfeilnotation hinzu.

Beispiele Bearbeiten

In den folgenden Beispielen stehen a, b, c und d für Variablen, foo(), bar() und baz() für Funktionen, und print() für eine Methode von Zahlen.

Der Normalfall, für den die automatic semicolon insertion gedacht ist, zeigt sich im folgenden Code:

{ foo()
bar() } baz()

Dies widerspricht eigentlich der Syntax von ECMAScript, wird aber durch das automatische Einfügen von Semikola zu folgendem korrektem Code:

{ foo()
;bar() ;} baz();

In folgendem Code wird kein Semikolon ergänzt:

a =   b
    + c
    + d;

Der Code ist nämlich gültig, ohne dass bei den Zeilenumbrüchen Semikola ergänzt werden. Dies erlaubt es, lange Codezeilen auf mehrere Zeilen zu verteilen.

In einigen Fällen führt das Weglassen von Semikola zu Syntaxfehlern, wenn diese nicht wie erwartet ergänzt werden:

if (a > b)
else c = d

Hier wird nach der ersten Zeile kein Semikolon ergänzt, da dieses eine leere Anweisung ergeben würde. Damit verursacht das else einen Syntaxfehler. Zur Korrektur müsste das Semikolon (oder ein leerer Block) explizit ergänzt werden. Natürlich könnte die if-Konstruktion in diesem Fall auch umgestellt werden um das Problem zu vermeiden.

Ein problematisches Verhalten der automatic semicolon insertion zeigt sich im folgenden Beispiel:

return
  a + b;

Da direkt nach return kein Zeilenumbruch zulässig ist, ist dieser Code gleichbedeutend zu:

return;
a + b;

Statt der Summe a + b wird also nichts (undefined) zurückgegeben. Dieser Fehler kann leicht übersehen werden. Wird ein langer Rückgabewert also auf mehrere Zeilen aufgeteilt, so muss der Anfang in der gleichen Zeile wie return stehen, folgender Code wäre korrekt:

return a
     + b;

Ein ähnlich unerwarteter Fehler entspringt dem folgenden Code:

a = b + c
(c + d).print()

Hier sollte eigentlich in zwei Anweisungen zunächst die Summe b + c in a gespeichert werden, dann die print()-Methode auf die Summe c + d angewendet werden. Tatsächlich aber wird zwischen den beiden Zeilen kein Semikolon ergänzt, da die Syntax auch ohne dieses stimmt. Der Code wird also behandelt wie:

a = b + c(c + d).print();

Es wird also versucht c als Funktion mit dem Argument c + d aufzurufen, vom Rückgabewert die Methode print() aufzurufen und dieses Ergebnis zu b zu addieren um diese Summe in a zu speichern. Dies wird höchstwahrscheinlich einen Laufzeitfehler auslösen, in jedem Fall aber nicht zum erwarteten Ergebnis führen. Ein ähnliches Problem kann sich ergeben, wenn eine Zeile mit einem regulären Ausdruck beginnt, der mit der vorherigen Zeile als Division interpretiert werden kann.

Bewertung Bearbeiten

Douglas Crockford bezeichnet die automatic semicolon insertion als einen „furchtbaren Designfehler“ von JavaScript und empfiehlt von wenigen Ausnahmen abgesehen, das Semikolon immer explizit anzugeben.[2] JSLint und ähnliche Hilfsmittel zur Stilkontrolle warnen in der Regel, wenn ein Semikolon automatisch eingefügt würde.

Auf der anderen Seite gibt es auch Programmierer, die die automatic semicolon insertion gerne nutzen. Um unerwartete Fehler zu vermeiden können Zeilen, die mit einer Klammer oder einem regulären Ausdruck beginnen, ein Semikolon vorangestellt werden.[3] So wird vermieden, dass nach dem Verschieben von Codezeilen an eine andere Stelle ein ursprünglich explizit gesetztes Semikolon nicht mehr an der notwendigen Stelle steht.

Einzelnachweise Bearbeiten

  1. Ecma-262 Edition 5.1, The ECMAScript Language Specification. 7.9: Automatic Semicolon Insertion
  2. Douglas Crockford: Das Beste an JavaScript. O’Reilly-Verlag, 2008. ISBN 978-3-89721-876-5. S. 103, 109f., 127f.
  3. inimino: JavaScript Semicolon Insertion: Everything you need to know. (Memento vom 2. Oktober 2017 im Internet Archive) Veröffentlicht am 28. Mai 2010, abgerufen am 2. Februar 2015.