JavaScript/Syntax

Aus SELFHTML-Wiki
< JavaScript(Weitergeleitet von Reservierte Wörter)
Wechseln zu: Navigation, Suche

Ein JavaScript-Programm ist nach gewissen Regeln aufgebaut. Solche Syntax-Regeln kennt man aus der natürlichen Sprache, zum Beispiel dem Deutschen. Dabei müssen beim Formulieren eines Satzes gewisse Grundregeln der Grammatik eingehalten werden, damit man verstanden wird.

Glücklicherweise verstehen uns unsere Mitmenschen auch dann, wenn wir kleinere Fehler machen und uns nur grob an die Regeln halten. Bei Mehrdeutigkeiten ist oft aus der Situation ersichtlich, was gemeint ist.

Programmiersprachen sind jedoch viel strenger: Ihre Regeln sind vergleichsweise einfach, eindeutig und lassen wenig Spielraum.

Dies hat folgende Bewandnis: Damit der JavaScript-Interpreter ein Programm ausführen kann, muss er zunächst dessen Syntax verstehen. Das Zerlegen des Programms in seine Bestandteile nennt sich Parsing. Das Modul des Interpreters, das dafür zuständig ist, nennt sich Parser. Wenn der Interpreter dabei auf einen Syntaxfehler trifft, bricht er mit einer Fehlermeldung ab und das JavaScript-Programm wird gar nicht erst ausgeführt.[1]

Dieser Artikel soll eine Referenz der Sprachelemente und der Syntax von JavaScript zur Verfügung stellen, wobei vieles in den Folgekapiteln erklärt wird.

Grundsätzliches

JavaScript ist eine Programmiersprache, die als Zusatztechnik in Webseiten eingebunden wird. Im modernen Webdesign erhalten Webseiten so neben der Inhaltsstruktur und dem Aussehen eine Verhaltensschicht.

Zusammenspiel HTML-CSS-JS

JavaScript kommt in diesem Konzept die Aufgabe zu, dem Dokument »Verhalten« (behaviour) hinzuzufügen. Damit ist gemeint, dass das Dokument auf gewisse Anwenderereignisse reagiert und z. B. Änderungen im Dokument vornimmt.

→ Hauptartikel: Einstieg in JavaScript
    Dort findet sich auch ein Tutorial, das dir die ersten Schritte anhand von Live-Beispielen näher bringt.

Einbindung in Webseiten

JavaScript-Quelltexte werden in HTML in einem script-Element notiert oder referenziert. Das script-Element darf dabei im body oder head des HTML-Dokuments notiert werden, auch innerhalb von flow content.

<script> in HTML5
<script>  </script>

→ Hauptartikel: JavaScript in HTML einbinden

Der „strenge Modus“

In JavaScript gibt es aufgrund der bewegten Geschichte mit vielen proprietären Methoden und der sehr einfachen Möglichkeit, globale Variablen einzuführen, viele potentielle Fehlerquellen. Deshalb wurde mit ECMA5 der Strenge Modus (strict mode) eingeführt, der ein standardisiertes, um Fehlerquellen bereinigtes, Subset der Programmiersprache aktiviert.

strict mode auf Script-Ebene
'use strict';
 ...

Die folgenden Artikel gehen davon aus, dass der strenge Modus aktiviert ist. So wird man gezwungen, modernen und weniger fehleranfälligen Code zu schreiben.

→ Hauptartikel: JavaScript/strict mode

Anweisung

Ein JavaScript-Programm besteht aus einer Abfolge von Anweisungen (Statements).[2]

Eine Anweisung stellt eine in der Syntax einer Programmiersprache formulierte einzelne Vorschrift dar, die im Rahmen der Abarbeitung des Programms auszuführen ist.
Wikipedia: Anweisung (Programmierung)[3]

Aussagen entsprechen in etwa den Sätzen in natürlichen Sprachen. Eine Anweisung bildet eine vollständige Einheit der Ausführung.[4]

alert('Hello world!');

In dieser Anweisung wird die alert()-Methode verwendet, um eine Zeichenkette in einem Dialogfenster auszugeben. Das Semikolon markiert ausdrücklich das Ende der Anweisung.


const secondsInADay = 86400;

Diese Anweisung ist eine Variablendeklaration, in der der Wert 86400 der Konstanten secondsInADay zugewiesen wird.


let fläche = laenge * breite;

In JavaScript kann eine Anweisung eine Deklaration sein, ein Ausdruck oder eine Kontrollstruktur (Verzweigungen und Schleifen).


Anweisungen können in einer einzigen Programmzeile aufgeschrieben werden, in den meisten Fällen aber auch auf mehrere Zeilen verteilt werden, um die Lesbarkeit des Programms zu verbessern. Man kann auch mehrere Anweisungen auf eine Programmzeile schreiben (sollte das aber unterlassen, weil die Lesbarkeit des Programms darunter leidet).

Anweisungsblock

Ein Anweisungsblock besteht aus einer oder mehreren Anweisungen, die innerhalb einer übergeordneten Anweisung oder innerhalb einer Funktion stehen.[5]

Ein Anweisungsblock wird durch eine öffnende geschweifte Klammer { begonnen und durch eine schließende geschweifte Klammer } beendet.

Anweisungsblock in einer Funktion
function greet() {
  let message = 'Hello world!';
  console.log(message);
}

Eine Funktion enthält einen Block von Anweisungen, die ausgeführt werden, wenn die Funktion aufgerufen wird.


Anweisungsblock in Schleife
let count = 0;

while (count < 5) {
  console.log('Durchlauf ' + count);
  count++; 
}

Man kann die geschweiften Klammern jeweils in eine eigene Zeile schreiben, wie in den obigen Beispielen. Es ist aber auch erlaubt, die Klammern in derselben Zeile zu notieren wie die Anweisungen.


3
if (number > 5) {
  console.log('Die Zahl ist größer als 5.');
  console.log(' Dieser Anweisungsblock wird ausgeführt.');
} else { console.log('Die Zahl ist 5 oder kleiner.'); }

So können Anweisungsblöcke beispielsweise unterhalb einer bedingte Anweisung oder einer Schleife stehen.

Auch alle Anweisungen, die innerhalb einer selbst definierten Funktion stehen, bilden einen Anweisungsblock.

Das Semikolon

Grundsätzlich ist es so, dass Anweisungen durch ein Semikolon ; abgeschlossen werden müssen. Diese Vorschrift ermöglicht erst das Verteilen einer Anweisung auf mehrere Zeilen, andernfalls wäre ein Programm nicht eindeutig interpretierbar.

Es gibt aber Ausnahmen:

  • Funktionsdeklarationen und manche Kontrollstrukturen enden mit einem Anweisungsblock. Hinter dessen Abschlussklammer wird kein Semikolon gesetzt.
  • JavaScript war als simple Sprache konzipiert, die ihren Benutzern Fehler verzeihen sollte. Deshalb ergänzt JavaScript an einigen Stellen automatisch ein Semikolon, wenn es glaubt, dass dort eins hingehört, und das führt dazu, dass JavaScript an einigen Stellen verbietet, eine Anweisung auf mehrere Zeilen aufzuteilen.

Automatische eingefügte Semikolons

Automatisch eingefügte Semikolons sind eine Eigenschaft des JavaScript-Parsers. Wenn er nach einem Zeilenumbruch auf ein }-Zeichen stößt oder auf ein Token, das auf Grund der Sprachsyntax an dieser Stelle nicht erwartet wird, dann fügt er vor dem Zeilenumbruch ein Semikolon ein. Das Ende des Quelltexts wird in diesem Zusammenhang übrigens ebenfalls als ein unerwartetes Token aufgefasst.

Darüber hinaus fügt der Parser Semikolons an den folgenden Stellen ein:

Es gibt Ausnahmen: Innerhalb der Klammern der for-Schleife werden keine Semikolons eingefügt, und wenn das Semikolon eine leere Anweisung erzeugen würde, wird ebenfalls keins eingefügt.

Eine weitere Ausnahme entsteht, wenn sich aus dem unerwarteten Token irgendwie syntaktisch gültiger Code herausdeuten lässt. Bei Mozilla findet man diese Beispiele[6]:

Kein Semikolon
const a = 1
(1).toString()
Der Ausdruck 1(1) lässt sich so deuten, als wolle man die Zahl 1 als Funktion aufrufen. Dies ist inhaltlicher Unsinn, aber syntaktisch ist es richtig. Der Funktionsaufruf-Operator () erwartet laut Sprachdefinition irgendeinen Wert, auf den er angewendet wird. Ob dieser Wert eine Funktion ist, stellt sich erst im Moment der Ausführung heraus. Du erhältst deshalb die Fehlermeldung 1 is not a function.
const b = 1
[1,2,3].forEach(console.log)
Hier lässt sich 1[1,2,3] als Zugriff auf eine Eigenschaft deuten. Der Inhalt der eckigen Klammern wird als Aneinanderreihungsoperator , verstanden, dessen Wert der letzte Operand der Aneinanderreihung ist, also 3. JavaScript möchte also die Eigenschaft "3" der Zahl 1 abrufen - die es nicht gibt, weshalb das Ergebnis undefined ist. Und weil undefined keine forEach-Methode kennt, erhält man die Fehlermeldung Cannot read properties of undefined (reading 'forEach').

Der Sinn dieses merkwürdigen Verhaltens war, wegen eines vergessenen Semikolons nicht gleich das Programm abbrechen zu lassen. Der Programmtext sollte sinnvoll interpretiert werden. Bei all diesen Ausnahmen bleibt aber nur – frei nach Douglas Adams – eines zu sagen:

We apologise for the convenience
So Long, and Thanks for All the Quirks

Zu Risiken und Nebenwirkungen …

JavaScript ist, wie alle Sprachen der C-Familie, sehr stark auf die Verwendung von Ausdrücken ausgerichtet. Ein Ausdruck ist ein Sprachkonstrukt, das Werte und Operatoren benutzt, um einen neuen Wert zu ermitteln.

Manche Anweisungen bestehen nur aus einem Ausdruck:

Math.pow(2, 10); // What is 2^10? Just checking!

In der Konsole kann so eine schnelle Berechnung angezeigt werden.


typeof "Hello"; // What's the type of a string?

Mit typeof kann der Datentyp ermittelt werden.


10 * 3; // nur Berechnung

Hier dient der mathematische Ausdruck nur als Zwischenschritt.


In Realität würde so etwas als Zuweisung realisiert werden.

    const result = 10 * 3;

Das Ergebnis des Ausdrucks wird der Konstanten result über den Zuweisungsoperator = zugewiesen und kann im weiteren Verlauf des Programms immer wieder aufgerufen und weiterverwendet werden. Dies nennt man Wirkung oder in einer Rückübersetzung von side effect auch Seiteneffekt.

Weitere Operatoren mit Seiteneffekten sind Inkrement und Dekrement und delete.

    let count++;

Mit dem Post-Inkrement Operator ++ wird der Wert der Variablen count um 1 erhöht. Diese Variable kann dann z. B. in einer Schleife weiterverwendet werden.

Seiteneffekte können auch auftreten, wenn man Programmierschnittstellen des Browsers aufruft. Die Wirkung kann sein, dass sich das angezeigte Dokument verändert, Daten von einem Server abgeholt werden oder dass eine Funktion aufgerufen wird, sobald ein bestimmtes Ereignis eintritt.

nameElement.addEventListener('click', handleNameClick);

Für das Ereignis 'click' wird eine Behandlungsfunktion registriert. Ein Rückgabewert wird nicht erwartet, dieser Ausdruck hat den Seiteneffekt, dass eine Funktion in einer Liste von Ereignisbehandlern eingetragen wird.

Der Begriff Seiteneffekte bezeichnet also etwas völlig neutrales und zielt auf die beabsichtigte Wirkung der Anweisung auf weitere, nachfolgende Stellen im Programm.

Seiteneffekte vorhersehen

Woher soll man nun wissen, ob Aufrufe wie document.getElementById oder htmlElement.addEventListener Seiteneffekte haben oder nicht? Hier hilft nur Handbuchstudium (das SELFHTML-Wiki ist ebenfalls gerne behilflich). Du musst die Beschreibung der jeweiligen Funktion lesen und verstehen, welche Aufgabe sie erfüllt. Zumeist ist auch der Name der Funktion eine Hilfe:

getElementById oder addEventlistener sagen deutlich, ob hier ein Seiteneffekt eintritt oder nicht. Mit etwas Programmiererfahrung weiß man dann, bei welchen Funktionen Seiteneffekte erwarten werden müssen und bei welchen nicht.

Das letzte Beispiel ruft die Funktion motorSteuerung auf. Sie hat irgendetwas mit einer Motorsteuerung zu tun. Ob sie Seiteneffekte hat? Soll sie den linken Motor auf Leistung 5 einstellen? Soll sie die Drehzahl des fünften Motors auf der linken Seite auslesen? Wir wissen es nicht.

motorSteuerung("links", 5);

Eine Funktion wie motorSteuerung ist ein Beispiel für eine schlecht benannte Funktion. Ein Name wie setMotorPower oder getMotorSpeed wäre deutlich hilfreicher gewesen. So bleibt uns nur das Handbuch, oder bei einer selbstgeschriebenen und undokumentierten Funktion das Studium des Programmcodes.

Selbstvergebene Namen

An vielen Stellen in JavaScript musst du selbst Namen vergeben, zum Beispiel für selbst definierte Funktionen, eigene Objekte oder Variablen. Diese werden Bezeichner (engl. Identifier) genannt.[7]

Bei selbst vergebenen Namen gelten folgende Regeln:

  • sie dürfen keine Leerzeichen oder - enthalten (würde als Operator erkannt werden)
  • sie dürfen aus Unicode-Zeichen und Ziffern (0-9) bestehen - das erste Zeichen darf aber keine Ziffer sein; Groß- und Kleinbuchstaben sind erlaubt und werden unterschieden!
    • sie dürfen als einzige Sonderzeichen $ und den Unterstrich "_" enthalten
    • sie dürfen deutsche Umlaute oder ein scharfes ßenthalten, dabei ist aber zu beachten, dass andere Tastaturen diese Zeichen nicht haben,
  • sie dürfen nicht mit einem reservierten Wort identisch sein.

Im europäischen und nordamerikanischen Raum beschränken sich die meisten JavaScript-Programmierer auf lateinische Buchstaben ohne diakritische Zeichen. Der Bezeichner größe ist zwar möglich. Üblicher ist allerdings, groesse zu notieren, um Schwierigkeiten bei der Zeichenkodierung aus dem Weg zu gehen. Im internationalen, kommerziellen Umfeld werden meistens englischsprachige Bezeichner verwendet.

Empfehlung: Vergib sprechende Variablennamen, die dir auch ein halbes Jahr, nachdem du das Script geschrieben hast, noch signalisieren, welche Bedeutung sie haben.
Es dürfen ruhig auch deutschsprachige Wörter sein.
Da keine Bindestriche erlaubt sind, verwendet man vorzugsweise CamelCase-Schreibweise.

Reservierte Wörter

Es gibt eine Reihe von reservierten Wörtern (Reserved Words), die nicht als Name für einen Bezeichner verwendet werden dürfen. Andernfalls wird ein Syntaxfehler erzeugt, der die Ausführung des Programms verhindert. Es ist deshalb bei der Vergabe von Bezeichnern, etwa für Funktionen oder Variablen, stets darauf zu achten, dass es sich bei dem gewählten Namen nicht um ein reserviertes Wort handelt.[8][9]


Beispiel
// Syntax Error

function break() {
  return null;
}


Wird wie bei der Funktionsdeklaration in dem Beispiel oben ein reserviertes Wort als Name für den Bezeichner gewählt, in diesem Fall also das Schlüsselwort break, dann wird das Programm erst gar nicht ausgeführt. Diese Funktion macht ihrem Namen also alle Ehre.

as
-Alias beim Export von Modulen, bzw. deren Inhalten
break
Teil einer Abbruchanweisung. Wird verwendet in Schleifen und bei switch.
case
Für Fallunterscheidungen mit switch.
catch
Wird bei der Fehlerbehandlung mit try und catch verwendet.
class
Für die Definition einer Klasse (ES6).
const
Leitet die Deklaration einer oder mehrerer Konstanten ein.
continue
Teil einer Fortsetzungsanweisung.
debugger
Definiert einen Haltepunkt im Programm für Debugger.
default
Standardklausel bei switch. Auch verwendet bei Standardexport in einem Modul.
delete
Operator zum Löschen von Objekteigenschaften.
do
Für Schleifen mit do und while.
else
Teil einer bedingten Anweisung mit if.
export
Wird bei der Deklaration eines Exports in einem Modul verwendet.
extends
Mit dem Schlüsselwort extends kann eine abgeleitete Klasse definiert werden.
finally
Kann bei der Fehlerbehandlung mit try und catch verwendet werden.
for
Wird in verschiedenen Schleifen verwendet.
function
Schlüsselwort zur Definition einer Funktion.
if
Für bedingte Anweisungen.
implements *
import
Wird verwendet bei der Deklaration eines Imports aus einem Modul.
in
Operator in einem relationalen Ausdruck. Auch verwendet in einer Schleife mit for.
instanceof
Hiermit kann geprüft werden, ob ein Objekt eine Instanz eines Konstruktors ist.
interface *
new
Operator für den Konstruktorenaufruf.
package *
private *
protected *
public *
return
Teil der Rückgabeanweisung einer Funktion.
super
Schlüsselwort zum Methoden- oder Konstruktorenaufruf in Objekt oder abgeleiteter Klasse.
switch
Für Fallunterscheidungen.
this
Eingebaute Kontextvariable von Funktionen.
throw
Erzeugt eine benutzerdefinierte Ausnahme.
try
Wird bei der Fehlerbehandlung mit try und catch verwendet.
typeof
Operator, der den Datentyp des Operanden zurückgibt.
var
Schlüsselwort für die Deklaration einer oder mehrerer Variablen.
void
Wertet den Ausdruck auf der rechten Seite aus und gibt standardmäßig undefined zurück.
while
Wird bei Schleifen verwendet.
with
wurde früher verwendet, um mehrere Anweisungen mit einem Objekt durchzuführen.
yield
Pausiert die Ausführung einer Generatorfunktion.

* Für die zukünftige Entwicklung der Sprache reservierte Wörter (Future Reserved Words). Sie sind nur dann reserviert, wenn das Programm im Strict Mode ausgeführt wird. Jedoch gilt auch hier, dass von der Verwendung diese Wörter als Namen für Bezeichner von Variablen, Konstanten oder Funktionen abzuraten ist, selbst wenn das im normalen Modus möglich wäre.

ToDo (weitere ToDos)

Sollte man nicht oben im Kapitel den strict mode erwähnen und empfehlen und hier nur eine Anmerkung machen


* gilt nur im strict mode


Es sind allerdings in Bezug auf die reservierten Wörter einige Besonderheiten zu beachten, die nicht unbedingt sofort ersichtlich sind.


Beispiel
const object = {
  delete(property) {
    return delete this[property];
  }
};

object.default = 'value';

console.info(object.delete('default')); // true


So sind die Namen von Objekteigenschaften und Methoden (Property Names) zum Beispiel grundsätzlich keine Bezeichner im Sinne der Sprache, weshalb das Verbot der Verwendung reservierter Wörter hier nicht einschlägig ist. Dabei spielt es auch keine Rolle, ob eine Eigenschaft oder Methode in einem Objektliteral definiert wird oder nicht, weshalb in dem Beispiel oben durch die Verwendung der Schlüsselwörter delete und default als Eigenschafts- beziehungsweise Methodenname kein Fehler erzeugt wird.


Beispiel
let public = 'valid';


// Syntax Error

function test() {
  'use strict';
  const static = 'invalid';
}


Darüber hinaus ist zu beachten, dass manche Wörter nur im Strict Mode reserviert sind. So erzeugt etwa die Verwendung des Namens public als Bezeichner bei der Variablendeklaration in dem Beispiel oben keinen Fehler, da der globale Kontext im normalen Modus ausgeführt wird. Weil die Funktion test jedoch im Strict Mode ausgeführt werden soll und in ihr eine Konstante deklariert wird, deren Name in diesem Modus reserviert ist, wird hier dennoch ein Syntaxfehler erzeugt.


Beispiel
<script>
  let await = 'Godot';
</script>


Schließlich kann der Status als reserviertes Wort auch davon abhängen, ob der auszuführende Code Teil eines Scripts oder Teil eines Moduls ist. Wird etwa, wie in dem Beispiel oben, eine Variable innerhalb eines Scripts deklariert und dabei der Bezeichner await verwendet, dann ist das kein Problem. Jedoch würde es sich anders verhalten, wenn die Deklaration innerhalb eines Moduls vorgenommen worden wäre, denn dort wäre der Name await reserviert.


Weblinks

  1. JavaScript: Syntax-Grundlagen von molily
  2. MDN: Statements and declarations
  3. Wikipedia: Anweisung (Programmierung), abgerufen am 27.04.2023
  4. Expressions, Statements, and Blocks (docs.oracle.com)
  5. MDN: Block statement
  6. MDN: Automatic semicolon insertion
  7. MDN: Identifier
  8. ECMAScript 2017 (7th Edition, ECMA-262 Draft): Reserved Words
  9. MDN: Keywords