0% fanden dieses Dokument nützlich (0 Abstimmungen)
15 Ansichten422 Seiten

Java Script 2025

Javascript 2025

Hochgeladen von

Xeno123
Copyright
© All Rights Reserved
Wir nehmen die Rechte an Inhalten ernst. Wenn Sie vermuten, dass dies Ihr Inhalt ist, beanspruchen Sie ihn hier.
Verfügbare Formate
Als PDF, TXT herunterladen oder online auf Scribd lesen
0% fanden dieses Dokument nützlich (0 Abstimmungen)
15 Ansichten422 Seiten

Java Script 2025

Javascript 2025

Hochgeladen von

Xeno123
Copyright
© All Rights Reserved
Wir nehmen die Rechte an Inhalten ernst. Wenn Sie vermuten, dass dies Ihr Inhalt ist, beanspruchen Sie ihn hier.
Verfügbare Formate
Als PDF, TXT herunterladen oder online auf Scribd lesen

Ralph Steyer

Programmieren
mit JavaScript
Die vielseitige Sprache für
Webentwicklung & mehr –
Grundlagen und fort-
geschrittene Techniken
Programmieren mit JavaScript
Ralph Steyer

Programmieren mit
JavaScript
Die vielseitige Sprache für
Webentwicklung & mehr – Grundlagen
und fortgeschrittene Techniken
Ralph Steyer
Bodenheim, Rheinland-Pfalz, Deutschland

ISBN 978-3-658-45576-7 ISBN 978-3-658-45577-4 (eBook)


[Link]

Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detail-
lierte bibliografische Daten sind im Internet über [Link] abrufbar.

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH,
ein Teil von Springer Nature 2025

Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Jede Verwertung, die nicht
ausdrücklich vom Urheberrechtsgesetz zugelassen ist, bedarf der vorherigen Zustimmung des Verlags.
Das gilt insbesondere für Vervielfältigungen, Bearbeitungen, Übersetzungen, Mikroverfilmungen und die
Einspeicherung und Verarbeitung in elektronischen Systemen.
Die Wiedergabe von allgemein beschreibenden Bezeichnungen, Marken, Unternehmensnamen etc. in diesem
Werk bedeutet nicht, dass diese frei durch jede Person benutzt werden dürfen. Die Berechtigung zur Benutzung
unterliegt, auch ohne gesonderten Hinweis hierzu, den Regeln des Markenrechts. Die Rechte des/der jeweiligen
Zeicheninhaber*in sind zu beachten.
Der Verlag, die Autor*innen und die Herausgeber*innen gehen davon aus, dass die Angaben und Informationen
in diesem Werk zum Zeitpunkt der Veröffentlichung vollständig und korrekt sind. Weder der Verlag noch die
Autor*innen oder die Herausgeber*innen übernehmen, ausdrücklich oder implizit, Gewähr für den Inhalt des
Werkes, etwaige Fehler oder Äußerungen. Der Verlag bleibt im Hinblick auf geografische Zuordnungen und
Gebietsbezeichnungen in veröffentlichten Karten und Institutionsadressen neutral.

Planung/Lektorat: David Imgrund


Springer Vieweg ist ein Imprint der eingetragenen Gesellschaft Springer Fachmedien Wiesbaden GmbH und ist
ein Teil von Springer Nature.
Die Anschrift der Gesellschaft ist: Abraham-Lincoln-Str. 46, 65189 Wiesbaden, Germany

Wenn Sie dieses Produkt entsorgen, geben Sie das Papier bitte zum Recycling.
Vorwort

JavaScript! Eine Skript- bzw. Programmiersprache, die sich längst etabliert hat. Lange
Zeit wurde sie eher belächelt und nicht wirklich ernst genommen. In den letzten Jahren
hat sich das massiv geändert. Aus dem hässlichen Entlein ist ein stolzer Schwan gewor-
den. JavaScript schafft es jetzt schon viele Jahre in fast allen Statistiken zu den meistge-
nutzten bzw. wichtigsten Programmiersprachen weltweit auf das Podium.
Dazu trägt sicher bei, dass man mit JavaScript (oder kurz JS) nicht nur Leben, Dyna-
mik und Aktivität in Webpräsentationen bringen kann, wie es am Anfang der Geschichte
von JavaScript und auch die ersten Jahre danach der Fall war. JavaScript hat längst das
Korsett eines Browsers, wo es die ideale Ergänzung zu HTML (Hyper Text Markup Lan-
guage) und Style Sheets darstellt, verlassen. Dennoch ist ein Webbrowser immer noch
das „natürliche Habitat“ von JavaScript. Das ursprüngliche Ziel von Netscape (Erfinder
von JavaScript), Webseiten zu dynamisieren, den Browser zu steuern und Aktivität vom
Webserver auf den Clientrechner zu verlagern, wurde nie aufgegeben oder eingeschränkt
und so gut wie keine Webseiten im Internet kommt ohne JavaScript aus. Im Gegenteil –
im Web übernimmt JavaScript als einzig relevante Möglichkeit zum Programmieren im
Browser immer mehr neue Aufgaben. Sogenannte Rich Internet Applications (RIAs) mit
„reichhaltigen“ Möglichkeiten, die Webapplikationen auf eine Stufe wie Desktopappli-
kationen stellen, basieren im Client wesentlich auf JavaScript. Dies umfasst sowohl viele
optische als auch funktionale Features wie animierte Inhaltsaufbereitung, das dynami-
sche Nachladen von Daten oder komfortable Benutzereingabemöglichkeiten. Auch wenn
dies oft HTML5 zugeordnet wird. HTML5 bietet vielfach nur in Verbindung mit Java-
Script echten Nutzen oder ist sogar bei einigen Features in Wirklichkeit nur JavaScript
selbst.
Die Einsatzgebiete von JavaScript haben sich aber parallel ebenfalls jenseits des
Browsers massiv erweitert. So gibt es – um erst einmal im Web zu bleiben – Webserver,
die auf JavaScript als zentrale Technik zur serverseitigen Programmierung setzen. Das
schafft einmal die Situation, dass sowohl im Client (Browser) als auch Server (Webser-
ver) die gleiche Programmiertechnologie verwendet wird. Das erleichtert viele Dinge.
Zudem hat sich gezeigt, dass man auf Serverseite durch JavaScript sowohl in der Ent-
wicklungszeit als auch der Performance im laufenden Betrieb riesige Vorteile erreichen

V
VI Vorwort

kann, die vor wenigen Jahren noch undenkbar gewesen waren. Auch in mächtige Spra-
chen wie Java bzw. JavaFX/FXML wird JavaScript mehr und mehr als Ergänzung aus-
drücklich integriert und ebenso bei der Steuerung von Micro-Controllern setzt man ver-
stärkt auf JavaScript.
JavaScript bietet uns also so viele interessante Möglichkeiten, dass eine Beschäfti-
gung damit äußerst nützlich und vor allem spannend und unterhaltsam ist. Sie können
dabei JavaScripts erstellen, ohne ein einziges Programm kaufen zu müssen. Zudem ist
JavaScript einfach zu lernen und vor allem unabhängig von einer speziellen Plattform.
Ob Sie Linux, Windows, macOS oder ein anderes Betriebssystem verwenden – Java-
Script wird auf nahezu allen Plattformen zu finden sein. Sie können unter der einen Platt-
form entwickeln und Anwender können eine beliebige andere Plattform verwenden.
Um mit einer persönlichen Bemerkung zum Abschluss des Vorworts zu kommen.
Ich nutze professionell gut ein Dutzend Programmiersprachen und habe vor allen Din-
gen sehr viel mit Java und C# zu tun. JavaScript, was ich seit dessen erstem Auftauchen
schon verwende, ist aber klar meine Lieblingssprache, der im Moment höchstens Python
halbwegs Konkurrenz machen kann. Und noch etwas – Programmieren mit JavaScript
macht einfach Spaß.
Kontact: [Link] und [Link]

Frühjahr Ihr Autor


Sommer 2024 Ralph Steyer
Inhaltsverzeichnis

1 Einleitung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1 Das Ziel des Buchs. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 An wen sich das Buch wendet. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.3 Voraussetzungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.1 Hardware. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.3.2 Die Software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Erste Beispiele – Der Sprung ins kalte Wasser. . . . . . . . . . . . . . . . . . . . . . . . 11
2.1 Der Interaktivmodus – die Kommandozeile bei JavaScript. . . . . . . . . . . 11
2.2 Anweisungen in (echten) Quelltext auslagern. . . . . . . . . . . . . . . . . . . . . 13
2.3 Ein einfaches Mitteilungsfenster. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.4 Schreiben in die Webseite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.5 Entgegennahme einer Benutzereingabe. . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.6 Besonderheit bei der Webprogrammierung. . . . . . . . . . . . . . . . . . . . . . . 19
3 Versionen von JavaScript und Einbindung in Webseiten. . . . . . . . . . . . . . . 23
3.1 Versionszyklen von JavaScript, ECMAScript & Co. . . . . . . . . . . . . . . . 24
3.2 JavaScript in Webseiten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
3.2.1 Die Inline-Referenz. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.2.2 Notation eines Skript-Containers in der Webseite. . . . . . . . . . . 26
3.2.3 Verwendung von externen JavaScript-Dateien . . . . . . . . . . . . . 29
3.3 Der gemeinsame Namensraum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
3.4 Kann man testen, ob bei einem Browser JavaScript aktiviert ist?. . . . . . 33
3.4.1 Eine Browserweiche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4 Elementare JavaScript-Grundstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.1 Token und Parser. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.1.1 Zerlegen von Quelltext . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
4.2 Kommentare. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
4.3 Schlüsselwörter und definierte Literale in JavaScript. . . . . . . . . . . . . . . 39
4.4 Bezeichner und Namenskonventionen. . . . . . . . . . . . . . . . . . . . . . . . . . . 40

VII
VIII Inhaltsverzeichnis

4.4.1 Regeln für Bezeichner in JavaScript. . . . . . . . . . . . . . . . . . . . . 40


4.4.2 Namenskonventionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.5 Anweisungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
4.5.1 Ausdrücke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.5.2 Blöcke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
4.6 Datentypen, Variablen und Literale. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.6.1 Datentypen und die JavaScript Global Properties. . . . . . . . . . . 45
4.6.2 JavaScript Global Properties und das globale Objekt. . . . . . . . 49
4.6.3 Variablen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.6.4 Konstanten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
4.6.5 Spezielle Schreibweisen für Literale. . . . . . . . . . . . . . . . . . . . . 57
4.7 Operatoren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.7.1 Die Operatoren typeof und delete. . . . . . . . . . . . . . . . . . . . . . . 63
4.7.2 Arithmetische Operatoren und der Verknüpfungsoperator . . . . 63
4.7.3 Vergleichsoperatoren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.7.4 Logische Operatoren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.7.5 Bit-Operatoren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.7.6 Zuweisungsoperatoren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
4.7.7 Die Operatorenrangfolge. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5 Kontrollflussanweisungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.1 Entscheidungsanweisungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.1.1 Die if-Entscheidungsanweisungen. . . . . . . . . . . . . . . . . . . . . . . 80
5.1.2 Die switch-case-Fallunterscheidung . . . . . . . . . . . . . . . . . . . . . 83
5.2 Iterationsanweisungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
5.2.1 Die while-Schleife. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
5.2.2 Die do-while-Schleife. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87
5.2.3 Die for-Schleife. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
5.2.4 Verschachtelte Schleifen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
5.3 Sprunganweisungen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
5.3.1 Abbruch mit break. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.3.2 Fortsetzen mit continue. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
5.3.3 Rückgabe mit return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
5.3.4 Unterbrechung mit throw. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6 Arrays, JSON und andere iterierbare Elemente. . . . . . . . . . . . . . . . . . . . . . 93
6.1 Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.1.1 Ein Array erzeugen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
6.1.2 Deklaration mit einem Array-Literal. . . . . . . . . . . . . . . . . . . . . 96
6.1.3 Der Datentyp eines Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.1.4 Zugriff auf Array-Elemente. . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.1.5 Verschachtelte Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6.1.6 Die Eigenschaft length. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
Inhaltsverzeichnis IX

6.1.7 Textindizes. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.2 Die Objekt-Notation – JSON. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.2.1 Der Datentyp bei JSON. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
6.2.2 Zugriff auf Elemente in einem JSON-Objekt . . . . . . . . . . . . . . 100
6.2.3 Verschachtelte JSON-Arrays. . . . . . . . . . . . . . . . . . . . . . . . . . . 101
6.3 Iteration über Arrays und JSON. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
6.3.1 Zugriff auf numerisch indizierte Arrays . . . . . . . . . . . . . . . . . . 103
6.3.2 Zugriff auf alle Elemente eines Arrays über for-in
und for-of sowie forEach. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
6.3.3 Über verschachtele Strukturen iterieren . . . . . . . . . . . . . . . . . . 107
6.4 Weitere iterative Datenstrukturen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
6.4.1 Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
6.4.2 Sets. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
6.4.3 Weitere Datenstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
7 Funktionen, Prozeduren und Methoden. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
7.1 Was ist allgemein ein Unterprogramm?. . . . . . . . . . . . . . . . . . . . . . . . . . 117
7.2 Die Deklaration eigener Funktionen in JavaScript . . . . . . . . . . . . . . . . . 118
7.3 Aufruf einer Funktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
7.4 Lokale Variablen in Funktionen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen . . . . 122
7.5.1 Funktionen redefinieren – wer zuletzt kommt. . . . . . . . . . . . . . 123
7.5.2 Funktionen mit Parametern. . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
7.5.3 Funktionsreferenzen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
7.5.4 Benannte versus anonyme Funktionen . . . . . . . . . . . . . . . . . . . 131
7.5.5 Hoisting bei Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
7.5.6 Innere Funktionen – Closures. . . . . . . . . . . . . . . . . . . . . . . . . . 133
7.5.7 Lambda-Ausdrücke. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
7.5.8 Callbacks. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
7.5.9 JavaScript-Generatoren. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
7.5.10 Weitere Möglichkeiten für die Deklaration von Funktionen. . . 138
7.5.11 Rekursive Funktionsaufrufe. . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
7.6 Vordefinierte Funktionen in JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . 145
8 Module. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
8.1 Was sind Module?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
8.2 CommonJS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
8.3 ES6-Module. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
8.4 Module in Browsern. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
9 Objekte und JavaScript. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
9.1 Grundlagen der OOP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
9.1.1 Was sind Objekte?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
X Inhaltsverzeichnis

9.1.2 Klassen, Instanzen, Prototypen und Vererbung. . . . . . . . . . . . . 162


9.1.3 Ziele der OOP – Wiederverwendbarkeit und bessere
Softwarequalität. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
9.1.4 Kernkonzepte der Objektorientierung. . . . . . . . . . . . . . . . . . . . 164
9.1.5 Wie lange gibt es Objektorientierung als Konzept?. . . . . . . . . . 165
9.1.6 Hintergründe der OOP. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
9.1.7 Funktionales Paradigma. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
9.1.8 Alternative Paradigma. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
9.2 Wie Objekte in JavaScript realisiert werden . . . . . . . . . . . . . . . . . . . . . . 168
9.2.1 Array = Objekt?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
9.2.2 Klassen vs Prototypen in JavaScript . . . . . . . . . . . . . . . . . . . . . 170
9.2.3 Was zeichnet in JavaScript eine Methode gegenüber
einer normalen Funktion aus?. . . . . . . . . . . . . . . . . . . . . . . . . . 171
9.2.4 Ein einzelnes Objekt erweitern. . . . . . . . . . . . . . . . . . . . . . . . . 174
9.3 Details zu Prototyping. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
9.3.1 Prototyping von Objekttypen. . . . . . . . . . . . . . . . . . . . . . . . . . . 176
9.3.2 Und doch nicht ganz ein Klassenelement. . . . . . . . . . . . . . . . . 177
9.3.3 Erstellen von Konstruktormethoden . . . . . . . . . . . . . . . . . . . . . 178
9.4 Vererbung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
9.4.1 Vererbung mit Prototyping. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
9.4.2 Vererbung mit class und extends. . . . . . . . . . . . . . . . . . . . . . . . 183
9.5 Datenkapselung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184
9.6 Module bei OOP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
9.7 Frameworks und POJO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
9.7.1 Frameworks und Toolkits. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
9.7.2 POJOs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
10 Eingebaute Objektdeklarationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
10.1 Alles sind Objekte. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
10.2 Standardobjekte in JavaScript erzeugen. . . . . . . . . . . . . . . . . . . . . . . . . . 190
10.3 Klassenmethoden und Klasseneigenschaften verwenden . . . . . . . . . . . . 191
10.4 Native Objektdeklarationen in JavaScript. . . . . . . . . . . . . . . . . . . . . . . . 192
10.4.1 Math. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
10.4.2 Instanzen und Elemente von Number. . . . . . . . . . . . . . . . . . . . 194
10.4.3 Arrays als Objekte. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
10.4.4 Objekte vom Typ Boolean. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196
10.4.5 Datumsoperationen mit Date. . . . . . . . . . . . . . . . . . . . . . . . . . . 196
10.4.6 String-Operationen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203
10.4.7 Objekte vom Typ Function . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
10.4.8 Native Bildobjekte. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
10.4.9 Reguläre Ausdrücke. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207
Inhaltsverzeichnis XI

11 Der DOM – das Mysterium der JavaScript-Welt . . . . . . . . . . . . . . . . . . . . . 215


11.1 Was ist das DOM-Konzept?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
11.1.1 Grundsätzliches zum Weg durch den DOM . . . . . . . . . . . . . . . 216
11.2 Wichtige DOM-Objekte. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218
11.2.1 Objekthierarchien im DOM. . . . . . . . . . . . . . . . . . . . . . . . . . . . 220
11.3 Eventhandling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
11.3.1 Verschiedene Ereignisse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
11.3.2 Grundlagen zu Ereignisobjekten. . . . . . . . . . . . . . . . . . . . . . . . 223
11.4 Generelle Zugriffsmöglichkeiten auf DOM-Objekte. . . . . . . . . . . . . . . . 229
11.4.1 Der Zugriff über einen Namen – getElementsByName. . . . . . . 230
11.4.2 Zugriff über eine Id – getElementById. . . . . . . . . . . . . . . . . . . 231
11.5 Zugriff auf Inhalte von Elementen in der Webseite. . . . . . . . . . . . . . . . . 234
11.5.1 Zugriff auf Textinhalte. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 234
11.5.2 Zugriff auf Formulare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235
11.5.3 Zugriff auf klassische HTML-Attribute. . . . . . . . . . . . . . . . . . . 247
11.6 Das Objekt document. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249
11.6.1 Ein Dokument mit Knoten dynamisch aufbauen. . . . . . . . . . . . 250
11.7 Das node-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
11.7.1 Knotenarten. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 254
11.7.2 Eigenschaften eines node-Objekts. . . . . . . . . . . . . . . . . . . . . . . 255
11.7.3 Methoden eines node-Objekts. . . . . . . . . . . . . . . . . . . . . . . . . . 255
11.8 Fenstertechniken . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 260
11.8.1 Member des window-Objekts. . . . . . . . . . . . . . . . . . . . . . . . . . 260
11.8.2 Anwendungen mit Fenstern. . . . . . . . . . . . . . . . . . . . . . . . . . . . 261
11.8.3 Frames, IFrames und das Objektfeld frames. . . . . . . . . . . . . . . 271
11.9 Browserauswertung und das Objekt navigator . . . . . . . . . . . . . . . . . . . . 277
11.9.1 Auflistung aller Eigenschaften von navigator. . . . . . . . . . . . . . 278
12 Ausnahmebehandlung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
12.1 Was ist Ausnahmebehandlung?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
12.1.1 Eine Ausnahme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
12.1.2 Wozu ein Ausnahmekonzept?. . . . . . . . . . . . . . . . . . . . . . . . . . 283
12.1.3 Das Auffangen einer Ausnahme . . . . . . . . . . . . . . . . . . . . . . . . 284
12.1.4 Der finally-Block. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 287
12.2 Selbstdefinierte Ausnahmen erzeugen und verwenden. . . . . . . . . . . . . . 287
12.2.1 Erstellen einer selbstdefinierten Ausnahme. . . . . . . . . . . . . . . . 287
12.2.2 Auswerfen von Ausnahmen mit throw . . . . . . . . . . . . . . . . . . . 288
12.2.3 Ein Beispiel zum manuellen Werfen von Ausnahmen. . . . . . . . 288
13 Asynchrone Programmierung. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
13.1 Was versteht man unter asynchronem JavaScript? . . . . . . . . . . . . . . . . . 291
13.2 Die Beziehung zwischen Webserver und Browser. . . . . . . . . . . . . . . . . . 292
XII Inhaltsverzeichnis

13.2.1 Der grundsätzliche Ablauf einer Kommunikation –


HTTP und HTTPS. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 293
13.3 Ajax & Web 2.0. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
13.3.1 Das Datenformat. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301
13.3.2 Was ist Ajax und was bezeichnet Web 2.0?. . . . . . . . . . . . . . . . 303
13.3.3 Der grundsätzliche Ablauf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
13.3.4 Ein XMLHttpRequest-Objekt erzeugen . . . . . . . . . . . . . . . . . . 304
13.3.5 Die Anforderung von Daten. . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
13.3.6 Verschiedene Datentypen vom Server per Ajax
nachfordern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
13.4 Deferred Object und Promises. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 321
13.4.1 Promises. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322
13.4.2 Deferred Objects. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326
13.5 Web Worker. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
13.5.1 Was ist ein Web Worker?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
13.5.2 Erzeugen von Web Workern . . . . . . . . . . . . . . . . . . . . . . . . . . . 328
13.5.3 Die Kommunikation mit einem Web Worker. . . . . . . . . . . . . . . 328
13.5.4 Kommunikation mit einem Web Worker. . . . . . . . . . . . . . . . . . 329
13.5.5 Einen Worker mit terminate beenden. . . . . . . . . . . . . . . . . . . . . 329
13.5.6 Beispiele zu Web Worker. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
13.6 WebSockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
13.6.1 Allgemeiner Ablauf. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 336
13.6.2 Die WebSocket-Spezifikation . . . . . . . . . . . . . . . . . . . . . . . . . . 337
13.6.3 Ein WebSocket mit JavaScript erzeugen. . . . . . . . . . . . . . . . . . 338
13.6.4 Der WebSocket- Handshake . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
13.6.5 Senden von Daten an den Server – die Methode send. . . . . . . . 339
13.6.6 Reaktion mit Callbacks und Eventhandlern. . . . . . . . . . . . . . . . 339
13.6.7 Die Serverseite anhand von [Link] . . . . . . . . . . . . . . . . . . . . . 340
13.7 Webservices. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
14 Erweiterte Techniken. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
14.1 Daten im Browser speichern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
14.1.1 Cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348
14.1.2 Local Storage und Session Storage. . . . . . . . . . . . . . . . . . . . . . 349
14.2 Geolocation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
14.2.1 Hintergründe der Geolokalisierung. . . . . . . . . . . . . . . . . . . . . . 356
14.2.2 Die konkreteGeolokalisierung. . . . . . . . . . . . . . . . . . . . . . . . . . 358
14.3 DHTML, Zeichnen und Animationen. . . . . . . . . . . . . . . . . . . . . . . . . . . 361
14.3.1 DHTML und Animation mit reinem JavaScript oder CSS . . . . 362
14.3.2 Verbinden von JavaScript und Style Sheets. . . . . . . . . . . . . . . . 363
14.3.3 Zeichnen mit Canvas und SVG. . . . . . . . . . . . . . . . . . . . . . . . . 370
14.3.4 Umgang mit Scalable Vector Graphics – SVG . . . . . . . . . . . . . 378
Inhaltsverzeichnis XIII

15 Eine Frage der Qualität. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385


15.1 Was versteht man allgemein unter Softwarequalität? . . . . . . . . . . . . . . . 385
15.2 Allgemeine Konzepte für bessere Qualität . . . . . . . . . . . . . . . . . . . . . . . 386
15.2.1 KISS-Prinzip. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 386
15.2.2 Das DRY-Prinzip. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
15.2.3 Weitere Konzepte. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
15.3 Ergänzende Regeln für JavaScript-Kodierung. . . . . . . . . . . . . . . . . . . . . 388
15.4 Ein leidiges Thema – Fehler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
15.4.1 Welche Fehler gibt es?. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 390
15.4.2 Vorbeugen statt heilen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
15.4.3 Fehler suchen. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 392
15.4.4 Fehler beheben. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394
15.4.5 Fehler zur Laufzeit abfangen. . . . . . . . . . . . . . . . . . . . . . . . . . . 395
15.5 Erweiterte Techniken. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
15.5.1 Automatische Dokumentation des Quelltextes und
Dokumentationskommentare. . . . . . . . . . . . . . . . . . . . . . . . . . . 396
15.5.2 Automatische Kontrolle und Qualitätssicherung
mit JSLint. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
15.5.3 xUnit-Testing. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
15.5.4 Cross-Engine-Probleme vermeiden. . . . . . . . . . . . . . . . . . . . . . 406
15.5.5 Optimierung von JavaScripts. . . . . . . . . . . . . . . . . . . . . . . . . . . 407

Stichwortverzeichnis. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
Einleitung
1

Übersicht
Bevor es mit JavaScript richtig losgeht, sollen in diesem einleitenden Kapitel ei-
nige Dinge geklärt werden, die Ihnen die folgende Arbeit mit diesem Buch und der
Sprache im Allgemeinen erleichtern werden. Insbesondere sorgen wir an der Stelle
dafür, dass Ihnen die Voraussetzungen zum Programmieren und Ausführen von Ja-
vaScript überhaupt zur Verfügung steht.

1.1 Das Ziel des Buchs

JavaScript wurde ursprünglich im Jahr 1995 von Netscape für dynamisches HTML in
Webbrowsern vorgestellt. Ursprünglich LiveScript genannt, wurde der Name in Ko-
operation mit Sun geändert, um eine gemeinsame Marketingstrategie mit dessen damals
fast gleichzeitig veröffentlichter Sprache Java fahren zu können.

Netscape Communications war ein amerikanisches Softwareunternehmen, das in den 90er-Jah-


ren äußert prägend für das WWW war. Netscape stellte 1994 den Browser Netscape Navigator als
Nachfolger des NCSA Mosaic vor und vollzog eine wechselvolle Geschäftsentwicklung, bis es
2003 aufgelöst wurde. Sun Microsystems, Inc. war ein amerikanischer Hersteller von Hard- und
Software, der im Jahr 2010 durch die Oracle Corporation übernommen wurde.

Die Bedeutung von JavaScript, die schon aktuell größer als je in der Geschichte des
Webs ist, wächst aufgrund der immer stärkeren Verbreitung von Rich Internet Applica-
tions, mobilen Apps auf Basis von Webtechnologien, serverseitigem Scripting und di-
versen weiteren Zielgebieten. Nicht zuletzt hat HTML5 die Bedeutung von JavaScript
noch einmal gesteigert und die Anforderungen an JavaScripts wachsen. Waren es frü-
her hauptsächlich eher einfache, kleine Skripte, die eine Webseite ein bisschen ergänzt

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 1


Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
2 1 Einleitung

haben, sind aktuelle JavaScripts in der Regel tragende Bestandteile der RIAs oder server-
seitiger Applikationen. Damit muss man in solchen Applikationen richtig professionell
mit JavaScript programmieren. Moderne JavaScript-Applikationen werden auf dem glei-
chen Niveau konzipiert und erstellt wie Desktop-Applikationen mit .NET oder verteilte
Client-Server-Applikationen – teilweise noch viel professioneller durch das heterogene
Umfeld, in denen diese RIAs funktionieren müssen, sowie die mangelnden Fangzäune
in der Sprache selbst, die Programmierer in Technologien wie .NET oder Java absichern.
Bei JavaScript wird deshalb mehr die Qualität des Programmierers selbst gefordert. Java-
Script schafft den Spagat zwischen einer einfachen Einsteigerlösung und – mittlerweile –
einem hochprofessionellen, wenngleich für Programmierer aus strengen Sprachen etwas
eigenwilligen, Programmierumfeld.

Gelegentlich nutzt man statt dem Begriff „Webseite“ auch „Website“. Es stellt sich die Frage, ob das
identisch ist? Die Antwort ist aber nicht ganz einfach und nicht streng festgelegt. Der Unterschied
kann einmal rein auf die Sprachpräferenz und regionale Konventionen reduziert werden. Es sind dann
synonyme Begriffe. Jedoch wird teils „Website“ auch verwendet, um auf eine ganze Sammlung von
Webseiten zu verweisen, die unter einer bestimmten Domain oder Subdomain gruppiert sind. Der Be-
griff steht also für die gesamte Präsenz im Internet, einschließlich aller Seiten, die unter einer be-
stimmten Domain oder Subdomain gehostet werden. „Webseite“ wird dann jedoch manchmal spezi-
fischer für eine einzelne Seite innerhalb einer Website verwendet. Also etwa eine HTML-Datei.

Dieses Buch ist ein Praxisbuch für den ambitionierten Einstieg in JavaScript und eng
verwandte Technologien sowie einige ergänzende Techniken. Es soll Ihnen sowohl beim
Selbststudium helfen als auch Basis dafür sein, in entsprechenden Kursen und Seminaren
JavaScript zu lernen.

 Wir erstellen im Laufe des Buchs immer wieder praktische Beispiele. Die Quell-
codes des Buchs finden Sie nach Kapiteln und teils darin erstellten Projekten
sortiert auf den Webseiten des Verlags bzw. in einem Repository auf GitHub.
Die Namen der jeweilig aktuellen Dateien beziehungsweise Projekte werden
als Hinweise oder direkt im Text vor den jeweiligen Beispielen angegeben und
bei Bedarf wiederholt. Ich empfehle allerdings, dass Sie die Beispiele allesamt
von Hand selbst erstellen. Das ist für Verständnis und Lernen eindeutig besser
als ein reines Kopieren oder nur Anschauen.

1.2 An wen sich das Buch wendet

Das Buch wendet sich im Wesentlichen an Leser, die von Grund auf den sicheren Um-
gang im großen Umfeld der Programmierung mit JavaScript lernen wollen. Dies umfasst
neben JavaScript Aspekte der allgemeinen Webprogrammierung sowie Grundlagen der
Webserver-Programmierung. Dabei sind als Zielgruppe dieses Werks auch Webseiteners-
teller gedacht, die JavaScript als Ergänzung zur „normalen“ Arbeit betrachten und ein
vollständiges Webprojekt (nicht nur reine HTML-Seiten) realisieren wollen. Dabei soll
1.3 Voraussetzungen 3

der Umgang mit JavaScript und ergänzenden Technologien von Anfang an erklärt wer-
den. Allerdings werden wir nicht auf einem zu niedrigen Level aufsetzen und auch bis
zu fortgeschrittenen JavaScript-Techniken kommen. Von daher haben wir insbesondere
in den letzten Kapiteln schon den Anspruch, richtig anspruchsvolle Techniken kennenzu-
lernen und beherrschen. Wir beginnen jedoch sehr langsam und grundlegend.
Diese Beschreibung der Zielgruppe soll bedeuten, Sie benötigen nicht unbedingt
Vorwissen im Bereich der Webprogrammierung und auch der Programmierung im All-
gemeinen. Allerdings erleichtert es den Umgang mit dem Buch, wenn Sie schon einmal
ein Programm oder Skript geschrieben haben (egal in welcher Programmiersprache) oder
zumindest wissen, was Programmierung ist. Gerade in den späteren Kapiteln ist das von
Vorteil. Was ich bei einem Leser bzw. einer Leserin voraussetzen möchte, ist Folgendes:

• Sie haben einen Rechner zur Verfügung und können ihn einigermaßen sicher be-
dienen. Das bedeutet, Sie kennen sich in Grundzügen mit Ihrem Betriebssystem aus.
• Sie sollten Zugang zum Internet haben und mit dem Internet zurechtkommen.
• Es ist sinnvoll, wenn Sie bereits Webseiten erstellt haben. Nicht notwendigerweise
mit CSS oder Skripten, aber zumindest sollte das Thema in Grundzügen bekannt sein.
Sie brauchen keinesfalls ein HTML-Experte zu sein, da dieses Thema für JavaScript
nicht in jedem Detail bekannt sein muss. Aber grundsätzlich sollten Ihnen die Idee
und das Konzept von HTML vertraut sein.
• Es ist wie gesagt hilfreich, wenn Sie bereits Programme oder Skripte erstellt haben,
aber es ist nicht zwingend notwendig. Allerdings ist ein professioneller Programmier-
hintergrund sicher kein Nachteil. Sollten Sie noch nie programmiert haben, können
Sie das meines Erachtens anhand von JavaScript hervorragend lernen. Sie müssen
sich nur etwas anstrengen und aufpassen, dass Sie sich ob der wenig strengen Re-
geln in JavaScript nicht gleich von Anfang an einen unsauberen Programmier-
stil angewöhnen. Ich gebe mir Mühe, Sie an einen sauberen und professionellen
Programmierstil heranzuführen und Sie davon abzuhalten, die konzeptionellen
Schwächen von JavaScript für eine schlampige Programmierung auszunutzen.

1.3 Voraussetzungen

Es gibt ein paar Dinge, die Sie unbedingt brauchen, wenn Sie erfolgreich Programmie-
rung mit JavaScript lernen und in der Praxis anwenden wollen. Aber keine Angst – viel
ist das nicht und kosten tut es auch wenig bis gar nichts.

1.3.1 Hardware

Natürlich ist ein Computer mit Internetzugang notwendig. Meist wird dabei ein PC mit
Intel-kompatibler Architektur die bevorzugte Wahl sein und darauf werden wir uns bei
4 1 Einleitung

Ausführungen beschränken. Die Fraktion der Apple-Anwender soll natürlich nicht ver-
gessen werden, denn im Web ist sie (wie allgemein im medialen Umfeld) ziemlich stark
vertreten. Aber grundsätzlich laufen JavaScripts auf allen wichtigen Computerplatt-
formen, weshalb wir hier keine echten Besonderheiten beachten müssen. Ihr Computer
braucht auch nicht sonderlich leistungsfähig zu sein.

1.3.2 Die Software

Die Erstellung von JavaScripts erfordert im Grunde außer einem beliebigen Editor und
einem Browser zum Testen keine weitere Software. Gleichwohl ist eine Reihe von Pro-
grammen sinnvoll bzw. nützlich. Schauen wir das etwas genauer an.

[Link] Das Betriebssystem
Die im Rahmen des Buchs zum Einsatz kommenden Technologien sind – wie fast alle
wichtigen Internettechnologien – plattformneutral. Sie lassen sich unter jedem moder-
nen Betriebssystem nutzen und auch die Entwicklung von Webseiten sowie JavaScript-
Applikationen kann man auf verschiedensten Plattformen durchführen. Für die meisten
Leser werden sich eines der folgenden drei Betriebssysteme anbieten. Dies sind Linux,
Windows oder macOS. Nutzen Sie das Betriebssystem, das Ihnen am besten passt und
nicht zu alt ist. Eine Hürde könnte es darstellen, wenn Sie keine Rechte zur Installation
von Software haben. Das ist bei Firmenrechnern oft der Fall. Um bestimmte Programme
wie einen Webserver oder [Link] verwenden zu können, müssen Sie aber vermutlich
Installationsrechte haben.

[Link] Der Editor bzw. die IDE


Wie erwähnt genügt zur Erstellung von JavaScripts oder auch Webseiten im Allgemeinen
(und auch serverseitigen Sprachen wie PHP oder Beschreibungssprachen wie HTML
oder XML) als Minimalausstattung ein reiner Klartexteditor, wie er bei jedem Betriebs-
system mitgeliefert wird. In der Praxis verwenden fast alle professionellen Programmie-
rer jedoch Programmierwerkzeuge, die sie bei der Erstellung und Analyse des Quell-
textes unterstützen. Solche Programme kennen einige Bestandteile einer Programmier-
oder Beschreibungssprache und unterstützen einfache und teilweise auch komplexere
Standardvorgänge, beispielsweise das Maskieren (die kodierte Darstellung) von Sonder-
zeichen, das Einfügen von Quellcodeschablonen oder eine bessere Übersicht durch farb-
liche Kennzeichnung von bekannten Befehlen. Einige Editoren bieten auch die Befehle
einer verwendeten Sprache direkt an – etwa durch Menüs oder Symbolleisten, wo man
diese auswählen kann (auch mit der Maus). Notepad++ ([Link]
[Link]/de/[Link]) unter Windows bzw. dessen „Kopie“ Notepadqq ([Link]
[Link]) unter Linux hat zum Beispiel so eine Unterstützung und ist im Grunde noch
als echter Editor zu sehen. Bei vielen mächtigen Programmiersprachen wie Java, C/C++
oder C# kommen sogenannte IDEs (Integrated Development Environment) zum Einsatz.
1.3 Voraussetzungen 5

Diese gestatten eine Programmierung und Ausführung von Applikation aus einer inte-
grierten, gemeinsamen Oberfläche heraus. Microsoft stellt etwa mit Visual Studio eine
sehr mächtige IDE zur Verfügung, die in der Community-Version kostenlos ist (https://
[Link]/germany) und auch für die Webprogrammierung verwendet wer-
den kann. Andere IDEs für Webentwicklung basieren auf der mächtigen Entwicklungs-
umgebung Eclipse ([Link] Es gibt zudem noch eine ganze Reihe
­weiterer IDEs, die Sie gerne verwenden können. Allerdings sollten Sie beachten, dass der
Umgang mit diesen umfassenden IDEs oft nicht ganz trivial ist und sie meist sehr „über-
laden“ daherkommen. Zudem fordern sie oft nicht unerhebliche Ressourcen von der
Hardware. Visual Studio Code oder kurz VS Code ([Link] was
eine leichtere, abgespeckte Version von Visual Studio darstellt und eigentlich näher an
einem Editor denn einer IDE ist, ist meines Erachtens eine perfekte Wahl zur Program-
mierung mit JavaScript und anderen Webtechnologien. VS Code steht für verschiedene
Betriebssysteme bereit und bietet Codevervollständigung, Syntax Highlighting, einen
Navigationsbaum etc. VS Code wird im Folgenden das Referenzsystem darstellen.

[Link] Verschiedene Browser und JavaScript-Engines


Gerade wenn Sie JavaScript für den Einsatz in Webseiten erstellen, sollten Sie mehrere
Browser zur Verfügung haben. Zwar sind die früher leider üblichen hohen Abweichungen
in der Darstellung und Funktionalität von Webseiten in verschiedenen Browsern mittler-
weile nicht mehr so gravierend. Dennoch sollte man unbedingt in verschiedenen relevan-
ten Browsern Webseiten bzw. JavaScripts testen. Denn in der Regel wissen Sie ja nicht,
mit welchen Browsern Ihre Besucher auf Ihren Webseiten vorbeikommen. Moderne
Browser sind aber nicht nur primitive Anzeigeprogramme für Webseiten, sondern mit viel-
fältigen Features aufgebohrt worden, die auch die Webentwicklung betreffen. Es gibt in
allen Browsern Entwicklertools, die Sie meist mit der Taste F12, einem entsprechenden
Menübefehl oder dem Kontextmenü auf einer Webseite aufrufen können. Dazu gibt es
meist Erweiterungen (Add-ons), die für die Webentwicklung sinnvoll sein können. Ebenso
haben moderne Browser allesamt eine integrierte JavaScript-Engine zur Verfügung.

Hintergrundinformation
Eine JavaScript-Engine ist vereinfacht ausgedrückt ein Programm, das JavaScript-Code a­ usführen
kann. Sie besteht in der heutigen Zeit in der Regel aus einem Compiler, der den JavaScript-Code bei
der Ausführung in Maschinencode übersetzt, und einem Laufzeitsystem, das den übersetzten Code
ausführt und die erforderlichen Umgebungen bereitstellt, um den Code auszuführen.
Einige bekannte JavaScript-Engines sind das:

• V8 wurde von Google entwickelt und ist eine der am häufigsten verwendeten JavaScript-
Engines. Das Programm wird in Google Chrome und dem [Link]-Laufzeitumfeld verwendet.
• SpiderMonkey ist die JavaScript-Engine, die von Mozilla entwickelt wurde und in Firefox ver-
wendet wird. SpiderMonkey stellte einen der ersten JavaScript-Compiler bereit und hat seitdem
viele Optimierungen erfahren.
• JavaScriptCore wird von Apple entwickelt und ist in Safari und einigen anderen Apple-Produk-
ten integriert. Es ist auch die Engine, die im WebKit-Projekt verwendet wird.
6 1 Einleitung

• Chakra wurde ursprünglich von Microsoft entwickelt und in älteren Versionen des Internet
Explorer-Browsers verwendet. Später wurde es durch ChakraCore ersetzt, eine Open-Source-
Version, die in Microsoft Edge und [Link] integriert ist.
• Rhino ist eine JavaScript-Engine, die in Java geschrieben wurde und die von Mozilla ent-
wickelt wurde. Rhino unterstützt die Ausführung von JavaScript in Java-Anwendungen.

Diese Engines sind entscheidend für die Ausführung von JavaScript-Code auf verschiedenen Platt-
formen und in verschiedenen Anwendungsfällen und haben im Laufe der Zeit erhebliche Fort-
schritte bei der Leistung und den Funktionen gemacht. Mittlerweile bemerkt man auch so gut wie
keine relevanten Unterschiede hinsichtlich des Verhaltens.
Nun ist der Begriff des Compilers gefallen und man muss den Kontext ausführlicher be-
trachten. Denn JavaScript ist von Anfang an eine reine Interpreter-Sprache gewesen. Interpreter
als auch Compiler dienen der Übersetzung von Quellcode und die Konzepte sollen nicht einfach
vorausgesetzt werden. Insbesondere nicht das JIT-Konzept, das moderne JavaScript-Engines ver-
folgen. Wenn Sie Skripte oder Programme erstellen, schreiben Sie den sogenannten Quellcode
oder Quelltext, der der englischen Sprache angelehnt ist. Aber zur Ausführung muss dieser über-
setzt werden, und zwar in ein Format, dass ein Computer versteht und das ist Maschinencode.
Dazu gibt es verschiedene Möglichkeiten.

• Einmal gibt es den Interpreter. Dieser sorgt für eine Übersetzung in Maschinencode während
der Programmausführung. Interpreter übersetzen den Quellcode eines Programms zeilenweise.
• Die Alternative ist der Compiler, der den kompletten Quelltext vor der Laufzeit übersetzt.
• In den vergangenen Jahren hat sich auch eine zweistufige Übersetzung etabliert – die Über-
setzung mit Zwischencode. Das wird in Java oder dem .NET-Framework gemacht. Denn ein
Nachteil bei kompilierten Programmen ist, dass diese Programme maschinenabhängig sind und
somit nicht auf jeder Computerplattform, zum Beispiel Linux und Windows, ausgeführt wer-
den können. In Java oder bei der .NET-Plattform wird Quellcode nicht zu einem ausführbaren
Programm, sondern in einen Zwischencode, kompiliert (1. Schritt). Dieser Code ist für alle
Plattformen gleich und kann mithilfe des entsprechenden plattformspezifischen Interpreters (2.
Schritt) auf der jeweiligen Plattform ausgeführt werden.

Bei JavaScript als interpretierte Sprache, wird Quellcode Zeile für Zeile ausgeführt wird und alte
Webbrowser führen den JavaScript-Code auf diese Weis aus, was zu relativ langsamer Ausführung
führte. Just-In-Time-Compilation (JIT) ist ein Ansatz, um die Leistung von interpretierten Sprachen
zu verbessern. Bei der JIT-Kompilierung wird der JavaScript-Code nicht nur interpretiert, sondern
auch in maschinenlesbaren Code übersetzt, der direkt von der CPU ausgeführt werden kann. Dieser
Prozess geschieht zur Laufzeit, was bedeutet, dass der JIT-Compiler während der Ausführung des
Programms aktiv ist. So funktioniert die moderne JIT-Kompilierung in JavaScript-Engines:

1. JavaScript-Code wird zunächst interpretiert, um die Ausführung zu starten. Dies ermöglicht


eine schnelle Bereitstellung des Codes, da kein komplexer Kompilierungsprozess durchlaufen
werden muss.
2. Dann findet sogenanntes Profiling statt: Während der Interpretation sammelt der JIT-Compiler
Informationen über den tatsächlichen Code, der ausgeführt wird, wie häufig bestimmte Funktio-
nen aufgerufen werden und welche Typen von Daten verwendet werden.
3. Daraufhin beginnt eine Optimierung, die auf den gesammelten Profiling-Daten basiert. Ins-
besondere identifiziert der JIT-Compiler Abschnitte des Codes, die häufig ausgeführt wer-
den und die für eine Leistungssteigerung optimiert werden könnten. Es kann verschiedene
1.3 Voraussetzungen 7

Optimierungstechniken anwenden, wie z. B. das Entfernen von unnötigen Operationen oder das
Inline-Aufrufen von Funktionen.
4. Die nun folgende Kompilierung übersetzt die optimierten Teile des Codes in maschinenlesbaren
Code, der direkt von der CPU ausgeführt werden kann. Dieser Prozess kann mehr Zeit in An-
spruch nehmen, aber da er nur für die am häufigsten verwendeten Codeabschnitte durchgeführt
wird, ist der Overhead akzeptabel.
5. Durch Iteration wird dieser Prozess immer wieder dynamisch wiederholt, während das Pro-
gramm läuft. Wenn sich das Verhalten des Codes ändert oder neue Teile des Codes ausgeführt
werden, passt sich der JIT-Compiler an und optimiert den Code entsprechend neu.

Durch die Verwendung von JIT-Kompilierung können JavaScript-Engines die Ausführungs-


geschwindigkeit erheblich verbessern, indem sie den Code dynamisch optimieren, während er
ausgeführt wird. Dies trägt dazu bei, dass JavaScript-Anwendungen performanter und reaktions-
schneller als früher sind.

[Link] [Link]
Wenn wir JavaScript ohne Browser als Umgebung nutzen als auch uns mit serverseitiger
JavaScript-Programmierung sowie fortgeschrittenen JavaScript-Techniken beschäftigen
wollen, werden wir auf ein Projekt mit Namen [Link] beziehungsweise NodeJS set-
zen. Bei [Link] ([Link] handelt es sich erst einmal im weiteren Sinn sowohl
um ein Framework als auch um ein Interpreter-System unter der MIT-Lizenz. [Link]
ist also eine Open-Source-JavaScript-Laufzeitumgebung, die auf der V8-JavaScript-
Engine von Google basiert. Genaugenommen versteht man [Link] als eine Plattform
zum Betrieb von Netzwerkanwendungen und das können auch Webserver sein. Kompakt
kann man sich merken – [Link] ermöglicht die Ausführung von JavaScript-Code außer-
halb eines Webbrowsers und wurde entwickelt, um hochskalierbare Anwendungen zu
erstellen und zu betreiben. [Link] ist modular aufgebaut und es gibt einige Standard-
module, die direkt in das Binärpaket kompiliert werden, das Sie bei einer Standard-
installation erhalten. Aber es können auch zusätzliche Module eingebunden werden, was
wir an mehreren Stellen machen werden.

 In JavaScript bezieht sich ein Modul auf eine Möglichkeit, Code in separaten Dateien
zu organisieren und zu strukturieren. Diese Dateien können dann über Import- und Ex-
portanweisungen verbunden werden, um Funktionalität zwischen ihnen zu teilen.

Zur Verwaltung der Module gibt es den Paketmanager npm, der mittlerweile auch von
vielen verteilten OpenSource-Projekten zur Softwareverwaltung eingesetzt wird. [Link]
steht für Linux, macOS, Windows und weitere Betriebssystem zur Verfügung und kann
über einen einfachen Assistenten auf einem Computer installiert werden. Das sollten Sie
tun, wenn Ihnen [Link] noch nicht zur Verfügung steht, denn wir werden in der Folge
recht oft auf [Link] zum Ausführen von JavaScripts setzen. Unter [Link]
download/ (Abb. 1.1) erhalten Sie die aktuellsten Setup-Dateien für Ihr Betriebssystem
zum Download.
8 1 Einleitung

Abb. 1.1 Hier gibt es [Link]

[Link] Webserver
Auch wenn Sie eine HTML-Datei direkt vom Dateisystem in den Browser laden und
damit ein enthaltenes JavaScript ausführen können, ist es besser, wenn Sie die Dateien
stattdessen von einem Webserver anfordern. In einigen Situationen (etwa zum realisti-
schen Durchführen einer Ajax-Datenanfrage) ist es meist sogar unabdingbar, dass Sie
Ihre Webapplikation nicht direkt vom Dateisystem, sondern über das Protokoll http
oder https vom Server anfordern. Dementsprechend benötigen Sie Zugang zu einem
Webserver. Einige IDEs beinhalten einen internen Webserver. Aber es gibt auch andere
Möglichkeiten. Entweder stellt Ihnen Ihr Provider einen solchen Zugang bereit oder aber
Sie installieren auf Ihrem Rechner oder in Ihrem Netzwerk lokal einen solchen Server
zum Testen. Unter Windows bietet sich der IIS (früher stand das für Internet Information
Server und mittlerweile für Internet Information Services) von Microsoft an, besonders
dann, wenn Sie sowieso mit Microsoft-Technologien arbeiten wollen. Für viele Fälle
­eignet sich eine Rundum-sorglos-Lösung namens XAMPP. XAMPP können Sie für ver-
schiedene Betriebssysteme aus dem Internet laden können (unter [Link]
[Link]/de/). Hinter XAMPP steckt ein Non-profit-Projekt namens Apache Friends,
das sich die Förderung des Apache Webservers und verbundener Technologien wie
MySQL, PHP und Perl auf die Fahne geschrieben hat. Für XAMPP gibt es nach der Ins-
tallation ein Control-Panel, worüber sich die einzelnen Dienste starten lassen (Abb. 1.2).
1.3 Voraussetzungen 9

Abb. 1.2 Das Control-Panel von XAMPP

Der Webserver steht Ihnen nach dem Start über die Angabe von localhost in der
Adresszeile des Browsers zur Verfügung, wenn Sie ihn auf dem gleichen Rechner laufen
lassen wie Ihren Browser. Das http-Protokoll davor wird in der Regel automatisch vom
Browser ergänzt. Andernfalls geben Sie in der Adresszeile einfach die IP-Nummer oder
den Namen des Rechners ein, auf dem der Webserver läuft.

Die Angabe localhost ist ein Alias für den aktuellen Rechner. Mithilfe dieser sogenannten Loop-
back-Adresse, die der IP-Nummer [Link] entspricht, können Sie auf Ihren eigenen Rechner so
zugreifen, als würden Sie dies über ein Netzwerk tun. Sie benötigen dazu nicht einmal eine Netz-
werkkarte.

Das Bereitstellen von Daten über den Webserver erfolgt bei Apache in der Regel über
das Unterverzeichnis htdocs. Dieses ist das Wurzelverzeichnis aller Zugriffe über den
Webserver. Oder mit anderen Worten: Wenn ein Anwender die Adresse des Webservers
ohne weitere Verzeichnis- oder Dateiangaben angibt, bekommt er den Inhalt dieses Ver-
zeichnisses beziehungsweise die Vorgabedatei darin angezeigt. Wurzelverzeichnis eines
Webservers bedeutet aber auch, dass sämtliche Verzeichnisangaben beim Zugriff aus
einem Browser auf einem Host ab diesem Verzeichnis aus gesehen werden. Wo konkret
sich htdocs im Verzeichnisbaum des Rechners befindet, kann je nach Konfiguration von
Apache individuell angegeben werden. Bei XAMPP unter Windows kann es sich zum
Beispiel auf dem Installationslaufwerk unter apachefriends und dann xampp befinden.
10 1 Einleitung

Das sieht aber ein Aufrufer der Webseite nicht und darf dieses aus Sicherheitsgründen
auch nicht sehen.

 Wenn Sie die folgenden Beispiele des Buchs zum Testen über den Apache-
Webserver anfordern wollen oder müssen, können Sie die Dateien direkt in
htdocs bereitstellen, aber es ist zu empfehlen, in htdocs ein eigenes Verzeichnis
für unsere Experimente zu erstellen und diese sogar für einzelne Projekte bzw.
Kapitel noch zu unterteilen.

 Beachten Sie, dass bei Pfadangaben unter Apache in der Regel Groß- und
Kleinschreibung relevant ist. Gerade Windows-Anwender sind das meist nicht
gewohnt, aber dies ist ja auch im Internet so gut wie immer der Fall. Am bes-
ten benennen Sie alle Verzeichnisse und Dateien konsequent klein. Vermeiden
Sie auch Sonderzeichen oder deutsche Umlaute bei Verzeichnis- und Datei-
namen.
Erste Beispiele – Der Sprung ins kalte
Wasser 2

Übersicht
In dem Kapitel kommen wir zum Erstellen erster JavaScript-Programme beziehungs-
weise -Skripte und Sie lernen, wie Sie diese ausführen können. Dabei werden wir
verschiedene Wege kennenlernen bzw. im ersten Einsatz sehen. Damit sind Tools
und Strategien gemeint, die Sie im Umgang mit JavaScript immer wieder benötigen.
Dazu möchte ich Ihnen direkt ein paar einfache JavaScript-Anwendungen vorstellen,
deren Syntax erst an späterer Stelle tiefer beleuchtet wird. Geben Sie jeweils nur den
Quelltext exakt wie vorgegeben ein (mit Beachtung der Groß- und Kleinschreibung,
Klammern, Semikolon etc.). Bevor wir uns in Details einarbeiten, schaffen wir uns
also praktische Grundlagen, damit nachfolgende Schritte leichter verständlich wer-
den. Wichtig ist hier, dass Sie den Umgang mit einem Editor und der grundsätzlichen
Erstellung von Quelltext sowie einem Browser (in Bezug zu JavaScript) und [Link]
üben und dabei erste Effekte sehen, die auf JavaScript beruhen.

2.1 Der Interaktivmodus – die Kommandozeile bei JavaScript

Nachfolgend erstellen wir ein paar ganz einfache Programme1 mit minimaler Syntax,
die wir in der sogenannten Kommandozeile ausführen wollen. Damit werden die ein-
gegebenen JavaScript-Anweisungen unmittelbar ausgeführt. Das nennt man den Inter-
aktivmodus. Wie schon erwähnt, ist die Verwendung von JavaScript traditionell mit
einer Integration in eine Webseite verbunden, aber diese Beschränkung ist schon lange

1 „Programme“ ist eigentlich auch schon zu hoch gegriffen. Aber es ist wirklich JavaScript, was Sie
hier sehen werden.

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 11


Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
12 2 Erste Beispiele – Der Sprung ins kalte Wasser

gefallen und war auch früher – etwas verborgen –nicht zwingend. JavaScript kann ent-
weder in der Konsole eines Webbrowsers direkt ausgeführt werden oder man verwendet
eine JavaScript-Laufzeitumgebung wie [Link]. In beiden Fällen wird keine Webseite als
Gerüst bzw. Verankerung des Skripts benötigt. Allerdings können Sie dann auch nicht
auf die Bestandteile der Webseite wie Formulare oder Grafiken zugreifen. Wenn Sie in
einem Browser die Konsole öffnen (beispielsweise mit einem Rechtsklick in eine be-
liebige Webseite und Öffnen der Entwicklertools oder dem zugehörigen Menübefehl
oder meist auch der Taste F12), können Sie dort direkt JavaScript-Anweisungen notie-
ren. Zum Beispiel für die Berechnung des Ergebnisses von 1 + 1. Sie brauchen bloß 1 + 1
(oder auch irgendeine andere einfache mathematische Formel) dort einzugeben, um das
Ergebnis in der Konsole zu sehen (Abb. 2.1).
Die Berechnung wird mit JavaScript durchgeführt und sie sehen das Ergebnis 2, denn
in der Konsole eines modernen Browsers haben Sie unmittelbar Zugriff auf dessen inte-
grierter JavaScript-Engine. Teils müssen Sie mit der Enter-Taste die Eingabe bestätigen,
bevor Sie das Ergebnis sehen. Teils wird das Ergebnis direkt schon angezeigt, wenn Sie
noch bei der Eingabe sind. Zumindest soweit die Eingabe schon auszuwerten ist. Das
geht auch mit [Link]. Wenn Sie [Link] mit Standardeinstellungen installiert haben, öff-
nen Sie eine Eingabeaufforderung/Shell/Konsole Ihres Betriebssystems und geben dort
node ein. Sie gelangen danach im Eingabemodus von [Link] (REPL) und können da
vollkommen analog wie in der Browserkonsole JavaScript-Befehle eingeben (Abb. 2.2).

Abb. 2.1 Interaktivmodus in der Konsole von Firefox


2.2 Anweisungen in (echten) Quelltext auslagern 13

Abb. 2.2 REPL bei [Link]

REPL steht für Read Evaluate Print Loop und ist eine Programmiersprachenumgebung (im
Grunde ein Konsolenfenster), die einen einzelnen Ausdruck als Benutzereingabe akzeptiert und
das Ergebnis nach der Ausführung an die Konsole zurückgibt. Verwandte Umgebungen kennt man
bei MySQL oder Python.

In der REPL können Sie analog wie in der Browserkonsole JavaScript-Befehle eingeben
und Ausgaben durch JavaScript vornehmen. Sie sehen auch da nach der Bestätigung
mit der Enter-Taste unmittelbar das Ergebnis. Die REPL-Sitzung bietet damit allgemein
eine bequeme Möglichkeit, einfachen JavaScript-Code schnell zu testen, aber auch Java-
Script-Code aus Dateien zu laden oder in Dateien zu speichern. Dabei gibt es in der
REPL ein paar grundlegende Befehle, die Sie kennen sollten. Um diese von JavaScript-
Anweisungen deutlich zu trennen, beginnen diese alle mit einem Punkt. Mit der An-
weisung .exit (beachten Sie den vorangehenden Punkt) beenden Sie die [Link]-Konsole.
Dazu gibt es weitere REPL-Anweisungen. Mit .help können Sie sich diesen Befehlssatz
der REPL anzeigen lassen. Wichtig sind die Anweisungen .load zum Laden einer ex-
ternen JavaScript-Datei in die REPL-Session und .save zum Speichern aller ausgeführten
Befehle in der aktuellen REPL-Session in eine Datei. Eine externe JavaScript-Datei kön-
nen Sie aber auch als Parameter an [Link] beim Aufruf übergeben und damit direkt aus-
führen, ohne in den Eingabemodus von [Link] gehen zu müssen. Das werden wir gleich
im auch tun.

2.2 Anweisungen in (echten) Quelltext auslagern

Wenn Sie in der Kommandozeile direkt Anweisungen notieren, werden diese nach Ein-
gabe der Enter-Taste unmittelbar interpretiert, also einzeln und nacheinander im Rahmen
der speziellen Umgebung (Interaktivmodus). In der Regel wird man aber Anweisungen
in eine extra Textdatei auslagern – den Quelltext (auch Sourcecode oder Quellcode), der
dann ohne Bestätigen der einzelnen Anweisungen abgearbeitet wird. Das hat auch den
14 2 Erste Beispiele – Der Sprung ins kalte Wasser

Vorteil, dass die Anweisungen wiederverwendet werden können. Diesen Klartext kann
man mit einem beliebigen Editor erstellen.

 Die übliche Dateierweiterung für JavaScript--Quellcode ist .js. Das entspricht


auch der Tatsache, dass man gelegentlich JavaScript durch JS abkürzt.

Die Anweisung zur Berechnung zweier Zahlen, die direkt in die Konsole eines Browsers
oder der REPL eingegeben und dann dort ausgegeben wurden, kann man in eine externe
Datei mit JavaScript-Quellcode auslagern. Diese kann in eine Webseite eingebunden
oder von [Link] direkt geladen und ausgeführt werden. Der reine Quellcode für Node.
js und einen Browser wird identisch sein, sofern nicht auf Elemente einer Webseite zu-
gegriffen werden soll. Da wir uns in der Situation jedoch nicht innerhalb der JavaScript-
Laufzeitumgebung befinden, muss man angeben, wo die Ausgabe von einer eventuellen
Berechnung erfolgen soll. Man muss erst einmal festhalten, dass JavaScript selbst keinerlei
Möglichkeiten hat, eine Eingabe oder Ausgabe bereitzustellen. Diese sind jedoch ganz ent-
scheidend, denn Computerprogramme arbeiten so gut wie immer nach dem EVA-Prinzip.

 Das EVA-Prinzip steht für Eingabe, Verarbeitung und Ausgabe und beschreibt ein
grundlegendes Konzept in der Informationstechnologie, ohne dessen Einhaltung Soft-
ware meist nicht sinnvoll genutzt werden kann.

Wie kann man das Dilemma lösen? JavaScript läuft im Rahmen einer Laufzeitumgebung
und diese stellt eine Schnittstelle mit passenden Objekten bereit. Da JavaScript objekt-
basierend (in neueren Versionen sogar objektorientiert) ist, gibt es also als Ergänzung der
eigentlichen Kernsprache zur Ausgabe passende Objekte.

 Objekte sind fundamentale Datenstrukturen, die verwendet werden, um Daten zu


organisieren und zu speichern. Objekte können Eigenschaften/Attribute haben sowie
Methoden, die aufgerufen werden können. Zusammengefasst nennt man Eigenschaften/
Attribute und Methoden Member. Für den Zugriff auf die Member verwendet man die
Punktnotation. Sie wird durch einen Punkt (.) zwischen dem Objektnamen und dem
Eigenschaftsnamen oder dem Methodennamen dargestellt. Wenn Sie die Punktnotation
verwenden, um auf eine Eigenschaft zuzugreifen, erhalten Sie den Wert dieser Eigen-
schaft. Wenn Sie sie verwenden, um auf eine Methode zuzugreifen, können Sie die Me-
thode aufrufen und deren Rückgabewert verwenden.

Das Objekt zur Ausgabe in der Konsole nennt sich passender Weise console und wird
sowohl von einem Browser als auch [Link] bereitgestellt. Mit dem Objekt console und
dessen Methode log erhalten Sie Zugang zu einer Ausgabe in der Konsole.

 Mit der log-Methode können Sie einen Parameter oder auch mehrere – durch
Kommata getrennte –Parameter in einer Zeile der Konsole ausgeben.
2.2 Anweisungen in (echten) Quelltext auslagern 15

Daher notieren Sie in die JavaScript-Datei [Link] folgenden Code:

[Link] (1 + 1);

Beachten Sie Groß- und Kleinschreibung, die Klammern und das Semikolon am Ende.
Sie führen diese JavaScript-Datei [Link] so mit [Link] ohne explizites Öff-
nen der RPEL aus, wenn Sie sich in dem Verzeichnis mit der JavaScript-Datei befinden:

Beispiel

node [Link]. ◄

Nach dem oben genannten Aufruf sehen Sie das Ergebnis in der Konsole, aus der Sie
node aufgerufen haben(Abb. 2.3).
Der externe JavaScript-Code kann auch in eine Webseite eingebunden werden. Diese
könnte in einer ganz einfachen Form so aussehen (kap 2_1.html):

<html><body><script src="lib/js/[Link]"></script>
</body></html>

 Wir werden aus Platzgründen insbesondere HTML-Code im Buch kompakt for-


miert ausgeben und wiederholende oder nicht relevante Passagen immer wieder
weglassen. Die vollständigen Quellcodes finden Sie zum Download. In der Praxis
werden Sie den Quellcode auch meist übersichtlicher schreiben.

Diesen Quellcode sollten Sie in ein eigenes Verzeichnis speichern. Etwa kap2. Beachten
Sie, dass die JavaScript-Datei in einem Unterverzeichnis lib und dessen Unterverzeich-
nis js gespeichert ist. So Strukturierung ist in der Praxis fast immer zu finden. Wenn Sie
die HTML-Datei direkt per Doppelklick oder über einen Webserver laden, sehen Sie die

Abb. 2.3 [Link] hat die externe JavaScript-Datei geladen und ausgeführt
16 2 Erste Beispiele – Der Sprung ins kalte Wasser

Abb. 2.4 Auch im Browser kann man mit der externen JavaScript-Datei in die Konsole schreiben –
hier Edge

Ausgabe in der Konsole des Browsers (Abb. 2.4). Falls der Webserver auf dem gleichen
Rechner läuft wie Ihr Webbrowser, würde der Aufruf wie folgt lauten, wenn Sie sich an
meine Empfehlung bezüglich der Wahl eines Verzeichnisses gehalten haben und dieses
dann im Installationsverzeichnis von Xampp unter htdocs bereitstellen:

Beispiel

[Link] ◄

Sollte das Beispiel nicht funktionieren, überprüfen Sie zuerst Ihren Quelltext. Achten Sie
auf Klammern, Hochkommata, Semikolon sowie Groß- und Kleinschreibung. Stimmt der
Quellcode exakt mit der Vorgabe überein und das Beispiel funktioniert dennoch nicht,
wird eventuell JavaScript in Ihrem Browser deaktiviert oder blockiert sein. Dann müssen
Sie JavaScript in den Einstellungen Ihres Browsers aktivieren bzw. eine eventuell blockie-
rende Erweiterung wie NoScript oder eine Firewall ausschalten, womit die Ausführung
von Skripten unterbunden wird. Der Zugriff über [Link] wird in der Regel (bei
den meisten Browsereinstellungen) auch als sicher angesehen, der Zugriff über das file-
Protokoll (einfaches Öffnen der Webseite mit einem Doppelklick) hingegen oft nicht.

 Das direkte Laden der JavaScript-Datei selbst in den Browser zeigt nur den Quell-
text an. Er wird nicht interpretiert. JavaScript im Browser wird also immer (!)
über eine Webseite an die JavaScript-Engine übergeben. Einzige Ausnahme ist
der oben genannte Weg, dass Sie JavaScript-Befehle direkt in das Konsolenfenster
2.3 Ein einfaches Mitteilungsfenster 17

des Browsers schreiben, was dem Interaktivmodus in [Link] entspricht. In älte-


ren Browsern konnte man mit einem vorangestelltem javascript: (das ist als Proto-
koll zu verstehen und der Doppelpunkt zwingend) in der Adressleiste des Browsers
JavaScript-Anweisungen ausführen lassen. Das ist aber in vielen neueren Browser
deaktiviert und zudem wenig sinnvoll.

2.3 Ein einfaches Mitteilungsfenster

Die Ausgabe bei einem Browser in die Konsole vorzunehmen ist unüblich und meist
auch wenig sinnvoll. Ausgaben durch JavaScript sollten in der Webseite selbst oder
einem visuellen Dialog des Browsers erfolgen. Kommen wir nun dazu, wie eine Ausgabe
in einem Browser über ein einfaches Mitteilungsfenster vornehmen kann. Dazu muss das
JavaScript wieder über eine Webseite (ein HTML-Dokument) in den Browser geladen
werden. Dieses Mal soll aber ohne externe JavaScript-Datei gearbeitet werden, sondern
JavaScript direkt in den HTML-Code geschrieben werden. Um – wie versprochen – die
Theorie in diesem Kapitel knapp zu halten, wollen wir nur festhalten, dass JavaScript so
oder so in einem Browser als Bestandteil einer Webseite zu sehen ist und damit in einer
HTML-Seite referenziert wird. Damit haben Sie Zugang zum DOM, was eine Schnitt-
stelle von Objekten des Browsers darstellt. Diese wiederum stellen zahlreiche nützliche
Eigenschaften und Methoden bereit. Beispielsweise die Methode alert, um ein Dialog-
fenster anzuzeigen.

 DOM steht für Document Object Model, und ist eine Spezifikation einer
Programmierschnittstelle mit Objekten, welches vor allen Dingen HTML- oder XML-
Dokumente (XML – Extensible Markup Language) als eine Baumstruktur darstellt.
Jedes Element als auch Attribut wird als Knoten verstanden. Knoten sind Objekte,
die einen Teil des Dokumentes repräsentieren. Die Schnittstelle ist plattform- und
programmiersprachenunabhängig und erlaubt standardisierte Zugriffe auf die Struktur
und das Layout eines Dokumentes. Die DOM-Schnittstelle ist in die gesamte Objekt-
schnittstelle eines Browsers nahtlos integriert.

Erstellen wir also so eine HTML-Datei. Geben Sie den nachfolgenden Quelltext in
einem Editor ein und nennen Sie die Datei kap 2_2.html.

<html><body><script>alert(1 + 1);</script></body></html>

In der Webseite finden Sie einen vereinfachten internen Skript-Container. Die eigentliche
JavaScript-Anweisung steht darin. Wenn Sie die Datei mit einem Doppelklick oder den
lokalen Webserver aufrufen, sollte der Browser ein kleines Dialogfenster mit dem Text
anzeigen, der das Ergebnis der Berechnung darstellt (Abb. 2.5).
18 2 Erste Beispiele – Der Sprung ins kalte Wasser

Abb. 2.5 Ein Dialogfenster des Browsers

Schauen wir uns auch hier etwas Troubleshooting an. Sollte das Beispiel nicht funk-
tionieren, überprüfen Sie wieder zuerst Ihren Quelltext, Einstellungen des Browsers
zu JavaScript etc. Aber bei dem Quellcode greift noch ein potenzielles Problem. Sollte
das Beispiel (scheinbar) immer noch nicht funktionieren, kann es daran liegen, dass das
Dialogfenster geblockt wird. Aufgrund diverser Sicherheitsprobleme im WWW ver-
hindern einige (vor allem neuere) Browser sogenannte aktive Inhalte bzw. Popups. Sollten
Sie eine entsprechende Warnung bekommen, müssen Sie zur Ausführung des Beispiels
die aktiven Inhalte für dieses Beispiel zulassen.

2.4 Schreiben in die Webseite

Spielen wir ein weiteres Beispiel durch, in dem das aktuelle Tagesdatum dynamisch mit
JavaScript in die Webseite geschrieben wird. Geben Sie den nachfolgenden Quelltext in
einem Editor ein (kap 2_3.html):

<html><body>Das aktuelle Datum:


<script>[Link](new Date());</script></body></html>

Im Body der Webseite finden Sie wieder einen Skript-Container und die eigentliche
JavaScript-Anweisung steht im Inneren des Containers. Beachten Sie, dass vor dem
Skript-Container reiner Text steht, der nicht zum Skript zählt. Hier sind wir noch in der
HTML-Welt. Wenn Sie die Datei im Browser laden, sollte im Anzeigebereich des Brow-
sers ein Text zu sehen sein, der aus den statischen Textpassagen und einer dynamisch
per JavaScript generierten Information (dem Tagesdatum mit einer Genauigkeit eines
Bruchteils einer Sekunde) besteht. Statische und dynamisch generierte Information kann
ein Anwender nicht unterscheiden. Die Details zu document behandeln wir später, aber
schon vorab die Information – das Objekt gehört zum DOM-Konzept.
2.6 Besonderheit bei der Webprogrammierung 19

Abb. 2.6 Ein Eingabedialog, der mit JavaScript generiert wurde

2.5 Entgegennahme einer Benutzereingabe

Das abschließendes Einstiegsbeispiel kap 2_4.html. zeigt die Entgegennahme einer Be-
nutzereingabe die Methode prompt. Beachten Sie, dass [Link] für die Eingabe von
Benutzereingaben keinen einfachen Weg bereitstellt und zudem ein externes Modul
benötigt. Das ist definitiv kein Hexenwerk und geht auch mit einigen wenigen Zeilen
Quellcode. Aber es ist noch zu früh, diesen Aufwand zu treiben. Das kommt später.
Eingaben im Rahmen des Browsers als auch die Ausgaben in der Webseite oder einem
Dialogfenster des Browsers sind einfacher, da sie die DOM-Schnittstelle sowie imple-
mentierte Objekte verwenden.

<html><body>Das ist die Webseite von


<script>[Link](prompt("Ihr Name"));</script>.</body></html>

Sie sehen wieder den Skript-Container und die eigentliche JavaScript-Anweisung. Auch in
diesem Beispiel steht sowohl vor dem Skript-Container als auch danach reiner Text, der
nicht zum Skript zählt. Beim Öffnen der Datei in einem Browser sollten Sie ein kleines
Eingabefenster angezeigt bekommen, in dem Sie Ihren Namen eingeben sollen (Abb. 2.6).
Im Anzeigebereich des Browsers sehen Sie nach der Eingabe eines Namens und der
Bestätigung des Dialogs einen Text, der aus den statischen Textpassagen und der Ein-
gabe durch den Anwender besteht (Abb. 2.7).

2.6 Besonderheit bei der Webprogrammierung

Kommen wir zum Abschluss des Kapitels zu ein paar Details rund um das Internet und
das WWW, die bei der Programmierung von Web-Applikation relevant sind. Das Internet
mit all seinen Diensten gehört als Ganzes niemandem und doch allen Internetteilnehmern
20 2 Erste Beispiele – Der Sprung ins kalte Wasser

Abb. 2.7 Eine Seite, die aus statischen und dynamischen Informationen zusammengesetzt wurdet

zugleich. Es gibt einige Organisationen, welche sich mit den unterschiedlichsten Aspek-
ten des Internets beschäftigen. Insbesondere auf Ebene der einzelnen Dienste existieren
Organisationen zur Lenkung verschiedenster Belange. Besonders wichtig in Bezug auf
die Webprogrammierung ist das W3C (World Wide Web-Consortium – [Link]
[Link]), das für die Standards im WWW und damit auch DOM, HTML, Cascading Style
Sheets und XML verantwortlich ist. Das W3C ist – wie alle diese Organisationen – je-
doch weitestgehend auf die freiwillige Kooperation der Internetgemeinde und vor allen
Dingen der Browserhersteller angewiesen. JavaScript hingegen wird bei der ECMA
International ([Link] standardisiert.
Nun muss man bei der Programmierung in so einem heterogen Umfeld wie dem
WWW ein paar Details beachten. In unserem Zusammenhang bedeutet dies, dass wir
im Internet die unterschiedlichsten Plattformen vorfinden: Sowohl bei der Hardware
auch den Betriebssystemen. Auch die Kommunikationswege sind uneinheitlich. Diese
Heterogenität erzwingt eine ganz besondere Technik, auf welche Art und Weise Daten
zwischen den Welten ausgetauscht werden müssen. Die Daten müssen für alle Systeme
verständlich sein. Auf der einen Seite – dem Transport – übernehmen dies im Internet
Protokolle wie TCP/IP, welche eine plattformneutrale Übertragung gewährleisten. Die
darauf aufsetzenden Dienstprotokolle wie HTTP oder FTP gewährleisten die plattform-
neutralen Anwendungsumgebungen. Aber auch aufseiten der konkreten Anwendung dür-
fen keine plattformbezogenen Anweisungen verwendet werden, wenn man nicht bewusst
bestimmte Zielplattformen ausschließen will. Zwar gibt es auch im Internet einige Tech-
niken, die beim Anwender bestimmte Voraussetzungen notwendig machen (etwa nur
Windows und den Internet Explorer als Basis oder das Vorhandensein einer hersteller-
abhängigen Erweiterung) und dann eine weitreichende Funktionalität bieten. Im Internet
wurden und werden solche dogmatischen Techniken jedoch kritisch gesehen und sind
im Grunde über die Geschichte allesamt gescheitert. Vor allem werden Anwender aus-
geschlossen, die bestimmte Produkte nicht verwenden können oder wollen.
2.6 Besonderheit bei der Webprogrammierung 21

Die beste Variante für die Akzeptanz einer Technik im Internet scheint die voll-
kommene Plattformneutralität zu sein. Das ist auf jeden Fall bei Klartexttechniken ge-
währleistet, bei denen jede Anweisung auf jeder Plattform verstanden wird. Die An-
weisungen werden durch ein spezifisches Programm interpretiert, das auf der jeweiligen
Plattform läuft, und die neutralen Befehle werden in konkrete Prozessorbefehle über-
setzt, die dann ausgeführt werden. Ich rede also vom Interpreter-Prinzip, das im Web
den Königsweg darstellt. Deshalb verwenden Sprachen wie HTML oder JavaScript, aber
auch XML diesen Ansatz. Sowohl HTML als auch JavaScript werden auf jeder Rechner-
plattform verstanden. Über einen Interpreter werden die Anweisungen in die jeweiligen
Prozessorbefehle übersetzt und ausgeführt (also interpretiert), wobei im Fall von Java-
Script ja in modernen Systemen JIT zur Optimierung zum Einsatz kommen. Das ändert
aber nichts an dem grundsätzlichen Ansatz. Interpretation hat noch weitere Vorteile. Die
Menge der zu übertragenden Daten ist kleiner, wie wenn die gesamte Funktionalität zu
übertragen wäre. Außerdem ist es erheblich leichter, Änderungen durchzuführen, und die
gesamte Wartung ist unkomplizierter. Der aufwendige Schritt der erneuten Kompilierung
entfällt.
Ein großes Problem ist, dass Interpretation immer einen erheblichen Spielraum lässt,
wie eine Anweisung zu verstehen ist. Insbesondere ist dies der Fall, wenn es sich nur
um eine Beschreibungsanweisung handelt, aber grundsätzlich gilt das Problem auch für
Skriptsprachen. Anhand von HTML lässt sich das Problem aber leichter verdeutlichen.
Es gibt in HTML beispielsweise eine Anweisung, dass der Anweisung folgender Text
hervorgehoben dargestellt werden soll (<strong>), bis die Anweisung wieder aufgehoben
wird (</strong>). Aber wer legt fest, was hervorgehoben eigentlich bedeutet? Neigung
der Schrift. Oder fett darstellen? Oder unterstreichen? Jede der Varianten wäre denk-
bar. Der Hersteller des Browsers A hat sich für die eine Variante entschieden, während
der Hersteller des Browsers B eine andere vorzieht. Beide Browser werden also die An-
weisung verstehen und durchführen, das Resultat wird aber bei beiden unterschiedlich
aussehen. Diesen Interpretationsspielraum finden Sie bei den unterschiedlichsten An-
weisungen in HTML und teilweise auch in anderen Techniken wie Style Sheets oder Ja-
vaScript. Mittlerweile haben sich die Hersteller verschiedener Browser als auch Engines
für JavaScript weitgehend geeinigt. Es gibt jedoch immer noch Situationen, die unter-
schiedlich ausgelegt werden. Gerade bei neuen Befehlen und Techniken dauert es lange
Zeit, bis sich die verschiedenen Lager einigermaßen geeinigt haben. Ein großes Problem
beim Interpreter-Konzept ist auch das Veralten der Interpreter. Wenn eine Interpreter-
Sprache einen neuen Befehl hinzugefügt bekommt, können die bis dahin entwickelten
Interpreter diesen Befehl noch nicht kennen und ihn entsprechend nicht ausführen. An-
wender mit solchen Interpretern können unter Umständen ein Programm nicht laufen
oder ein Dokument nicht darstellen lassen.
Versionen von JavaScript und
Einbindung in Webseiten 3

Übersicht
Wir beschäftigen uns in diesem Kapitel damit, wie Sie JavaScripts in Webseiten
einbinden können, denn bevor Sie mit JavaScript einen Browser beziehungs-
weise die Webseite programmieren können, müssen Sie eine Verbindung zwischen
HTML (dem Gerüst der Webseite) und der Skript-Funktionalität schaffen. Es sind
hier einige Feinheiten zu beachten, die das Leben schwermachen können. Auf der
einen Seite werden diese Feinheiten meist keine Rolle spielen, aber es gibt durch-
aus kritischen Situationen, die man nur durch die richtige Einbindung von Java-
Scripts in die Webseite entschärfen kann. Sie lernen in diesem Kapitel

• die Inline-Referenz kennen,


• wie man die Referenz auf eine externe JavaScript-Datei herstellt,
• das Einbinden eines Skript-Containers in die Webseite,
• die Festlegung der JavaScript-Version,
• Varianten von JavaScript und
• einen Test, ob bei einem Browser JavaScript aktiviert ist.

Dazu ist es wichtig zu beachten, dass JavaScript durch mehrere Versionen ge-
gangen ist und inwieweit diese Versionen in den verschiedenen relevanten Brow-
sern unterstützt werden.

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 23


Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
24 3 Versionen von JavaScript und Einbindung in Webseiten

3.1 Versionszyklen von JavaScript, ECMAScript & Co

JavaScript ist seit seiner Vorstellung durch Netscape unter dem ursprünglichen Namen
LiveScript und der Umbenennung im Jahr 1995 durch diverse Versionszyklen gegangen.
Grundsätzlich muss beachtet werden, dass über die Geschichte der Sprache für nahezu
alle neu eingeführten Sprachzyklen von JavaScript kaum ein zeitnah aktueller Browser
den offiziellen Standard vollständig unterstützt hat und es immer Zeit dauerte, bis die
nächsten Browserversionen einen vollständigen Sprachzyklus verdaut hatten! Obwohl
sich im November 1996 Netscape und die internationale Industrievereinigung ECMA
(European Computer Manufactures Association –früher ([Link] und
mittlerweile [Link] zusammentaten und beschlossen hatten, Ja-
vaScript zu standardisieren. Aus dem Projekt entstand ECMAScript, was der Name für
das, von der mittlerweile ECMA International genannten Organisation standardisierte,
JavaScript ist. Im Juni 1997 wurde die erste Version freigegeben (der Standard ECMA-
262). Alle neueren JavaScript-Versionen versuchen, mit der jeweilig aktuellen Version
von ECMAScript so weit wie möglich kompatibel zu sein. Nach der ECMAScript-­
Edition 3, die im Dezember 1999 freigegeben wurde, richteten sich die meisten Brow-
ser sehr lange Zeit. Die dritte Version des ECMAScript-Standards entspricht JavaScript
1.5. Man sollte festhalten, dass die JavaScript-Versionen nach 1.5 mehrere Jahre von der
Mozilla-Foundation (Nachfolgerorganisation von Nescape –[Link]
org/de/) proprietär vorangetrieben wurden, was durchaus kein Vorteil ist. Im Gegenteil –
die Versionen nach der Version 1.5 wurden lange Zeit in der Praxis in nur wenigen Brow-
sern unterstützt. Was aber an Neuerungen besonders interessant war (etwa Web Worker
oder Local Storage), wurde dann irgendwann unter dem Deckmantel HTML5 etabliert.
Der als ECMAScript standardisierte Sprachkern von JavaScript beschreibt also letzt-
endlich eine dynamisch typisierte, objektorientierte, aber klassenlose Skriptsprache.
Sie wird allen objektorientierten Programmierparadigmen unter anderem auf der Basis
von Prototypen gerecht, deren Deklaration ab ECMAScript 6 mit einer Syntax ermög-
licht wird, wie sie ähnlich bei klassenbasierten Programmiersprachen üblich ist. Diverse
moderne Browser als auch [Link] unterstützen entsprechende Syntax mittlerweile ganz
oder zumindest teilweise. In JavaScript lässt sich je nach Bedarf damit objektorientiert,
prozedural oder funktional programmieren. Nun sollte man festhalten, dass es neben
JavaScript weitere Sprachen gibt, die sich an ECMAScript orientieren oder als Klone
von JavaScript entwickelt wurden. Insbesondere muss man JScript und TypeScript er-
wähnen, was beides von Microsoft kommt.
Während JScript nur ein besagter „Klon“ von JavaScript war, den Microsoft rein
aus Lizenzgründen herausgegeben hat (die Lizenz von JavaScript gehörte Netscape
und wurde nie von Microsoft erworben), ist TypeScript eine von Microsoft auf Basis
von JavaScript bzw. ECMAScript entwickelte Skriptsprache, die 2012 erstmals in einer
Betaversion und 2014 dann offiziell vorgestellt wurde. TypeScript brachte einige echte
eigene Features und Erweiterungen gegenüber JavaScript. Insbesondere eine statische
3.2 JavaScript in Webseiten 25

Typisierung und Klassen, Vererbung oder Module. Später kamen noch Syntaxelemente
wie die Möglichkeit einer Methodensignatur, Schnittstellen, Aufzählungstypen und
Tupel, generische Programmierung und Namensräume hinzu. TypeScript kann man
damit als eine Obermenge (strict superset) von JavaScript bzw. ECMAScript 2015 be-
trachten und jeder JavaScript-Code ist gültiger TypeScript-Code. Damit können alle
gängigen JavaScript-Bibliotheken in TypeScript verwendet werden. Die Umkehrung gilt
jedoch nicht. Um TypeScript wirklich in der Praxis verwenden zu können, muss dar-
aus JavaScript gemacht werden. Der von Microsoft bereitgestellte TypeScript-Compiler
transkompiliert TypeScript-Code nach ECMAScript 5 (ES5) oder ECMAScript 3 (ES3).
Neue Versionen von JavaScript haben diverse Features von TypeScript mittlerweile auch
optional verfügbar, sodass sich die Sprachen stark angeglichen haben. Nur wird Java-
Script universell unterstützt, während TypeScript weitgehend auf das Habitat von Micro-
soft eingeschränkt ist oder eben transkompiliert werden muss.

JavaScript verwendet den Unicode-Zeichensatz. Unicode ist ein internationaler Standard, der
eine einheitliche Codierung, Darstellung und Verarbeitung von Texten in verschiedenen Schrift-
systemen und Zeichenkodierungen ermöglicht. Unicode bietet eine breite Palette von Zeichen,
die verschiedene Sprachen, Symbole, Emojis und andere Zeichen umfassen. JavaScript kann
somit Zeichen aus verschiedenen Sprachen und Zeichenkodierungen verarbeiten, solange sie im
Unicode-Zeichensatz definiert sind. Aber das war nicht immer so. In den frühen Tagen von Java-
Script, als die Sprache in den 1990er Jahren entwickelt wurde, war die Unterstützung für Zeichen-
sätze weniger standardisiert. JavaScript wurde ursprünglich für die Verwendung in Webbrowsern
entwickelt, und zu dieser Zeit waren Zeichencodierungen wie ASCII und ISO-8859–1 (auch be-
kannt als Latin-1) weit verbreitet. Unicode wurde in JavaScript jedoch prinzipiell dennoch von
Anfang an unterstützt. 1995 war Unicode war bereits als aufstrebender Standard für die Zeichen-
codierung bekannt und daher von Netscape bereits in den Grundlagen der Sprache berücksichtigt.
Die offizielle Spezifikation für JavaScript über ECMAScript-Spezifikation hat die Unterstützung
für Unicode kontinuierlich verbessert und erweitert. In den modernen Versionen von JavaScript,
insbesondere in ECMAScript 6 und den nachfolgenden Versionen, ist Unicode fest im Standard
verankert. Dies ermöglicht die Verwendung von Unicode-Zeichen in Variablennamen, String-­
Literalen, Funktionsnamen und anderen Teilen des Codes.

3.2 JavaScript in Webseiten

Wir haben schon zwei Wege gesehen, wie man JavaScript in einer Webseite verwendet.
Das wollen wir vertiefen. JavaScript wird im Browser in einer sogenannten Sand-
box ausgeführt. Dies soll bewirken, dass man in JavaScript nur Zugriff auf die Objekte
hat, die der Browser zur Verfügung stellt. Insbesondere wird der direkte Zugriff auf
das Dateisystem unterbunden. Eine Ausnahme stellt der Lesezugriff auf eine Datei dar,
die per Dateiauswahl-Dialog über das HTML-Element <input type = "file"> vom Be-
nutzer ausgewählt wurde. Ebenso sind lesende und schreibende Zugriffe auf dafür vor-
gesehene Bereiche erlaubt, was etwa Cookie, Local Storage oder lokale Datenbanken
ermöglicht. Die Sandbox unterbindet zudem Sicherheitsprobleme wie das sogenannte
26 3 Versionen von JavaScript und Einbindung in Webseiten

Cross-Site-Scripting. Dabei jede Webseite oder Webanwendung innerhalb des Browsers


isoliert ausgeführt und ein Datenaustausch zwischen den verschiedenen Applikationen
­unterbunden. Auch bestimmte sicherheitsrelevante Browserfunktionen wie das Schlie-
ßen des Browserfensters, das Aus- und Einblenden von Symbolleisten, das Ändern der
Browserstartseite, der Zugriff auf die Zwischenablage oder das Auslesen der zuletzt be-
suchten Webseiten des Anwenders werden geschützt. Der Einsatz von JavaScript in Web-
seiten stellt also kein oder nur ein geringes Sicherheitsrisiko dar und hat sich auch des-
halb als die einzig relevante Möglichkeit etabliert, um innerhalb eines Browsers aktive
Inhalte auszuführen. Kommen wir nun dazu, wie Sie eine Verbindung zwischen HTML
und der Skript-Funktionalität schaffen. Dazu gibt es verschiedene Möglichkeiten.

3.2.1 Die Inline-Referenz

Nur noch historisch interessant ist der Aufruf einer JavaScript-Anweisung, die direkt in
eine HTML-Referenz geschrieben wird. Dies ist die sogenannte Inline-Referenz. Dabei
wird JavaScript-Code bei einem sensitiven Element wie einem Hyperlink statt eines
URL auf eine andere Ressource als Verweisziel definiert, indem dem Attribut href in An-
führungszeichen eine oder mehrere JavaScript-Anweisungen zugewiesen werden. Dazu
müssen Sie in einer entsprechenden HTML-Anweisung das Schlüsselwort javascript (als
Protokoll zu verstehen), gefolgt von einem Doppelpunkt, als Attribut angeben. Die Syntax
sieht also so aus:

Beispiel

<a href = "javascript:[JavaScript-Anweisung]"> ◄

Hier ist Beispiel einer Inline-Referenz zum Aufruf eines Dialogfensters bei einem Klick
auf einen Hyperlink:

<a href="javascript:alert(1 + 1)">Berechnung</a>

Wenn der Anwender auf den Hyperlink klickt, wird in dem Beispiel mit alert ein kleines
Dialogfenster eingeblendet, in dem der als Parameter angegebene Text, der sich durch die
Berechnung ergibt, angezeigt wird. Dieses System der Inline-Referenz hat erhebliche Nach-
teile und wird so gut wie gar nicht mehr verwendet. Wir verfolgen das Konzept nicht weiter.

3.2.2 Notation eines Skript-Containers in der Webseite

JavaScript-Anweisungen können in eine Webseite integriert werden, indem sie einfach in


die entsprechende HTML-Datei als Klartext hineingeschrieben werden. Allerdings muss der
Skriptbereich klar von der „normalen“ Webseite getrennt werden. Der Beginn eines Skripts
3.2 JavaScript in Webseiten 27

wird durch eine HTML-Steueranweisung realisiert, die mit ihrem zugehörigen Abschluss-
Tag einen Container für die Skriptanweisungen bildet. Es handelt sich bei dieser Steuer-
anweisung um das Tag <script>. Diese Anweisung haben Sie in den ersten Beispielen im
Buch auch schon gesehen. Das Tag gehört explizit zu HTML, aber all das, was in dem ein-
geschlossenen Bereich notiert wird, wird vom Browser als ein Skript interpretiert.

<script> … </script>

Das Tag verfügt über einige optionale Parameter, die aber in der Regel veraltet sind und
heute nicht mehr verwendet werden.

• Mit language können Sie angeben, um welche Skriptsprache es sich im Inneren des
Containers handelt. Das geht auch heute noch, aber der Parameter wurde schon lange
durch das Attribut type ersetzt. Damit geben Sie den MIME-Type an (für JavaScript
ist das der Wert "text/javascript"). Mit diesem Parameter können Sie also ebenfalls
angeben, dass in dem Skriptbereich mit JavaScript gearbeitet wird. In manchen älte-
ren Quellen zu JavaScript sieht man auch den MIME-Typ "application/javascript" als
Alternative. Ich rate von der Verwendung ab. Es gibt diverse Browser, die damit Pro-
bleme machen. HTML5 hat sogar den Verzicht auf die Angabe der konkreten Skript-
sprache als Vorgabe genommen.

 Definition Die Angabe eines Dokumententyps im Internet erfolgt in der Regel über
einen Standard mit Namen MIME (Multipurpose Internet Mail Extensions). Dieser wird
im Wesentlichen zur Spezifizierung von Dateitypen bei der Kommunikation zwischen
Server und Browser verwendet. Sowohl Server als auch Browser kennen bestimmte
Dateitypen. Beim Übertragen vom Server zum Browser wird über das http-Protokoll der
MIME-Typ versteckt mit übertragen. Der MIME-Typ text/html steht zum Beispiel für ein
HTML-Dokument und XHTML-Dokumente gibt es die MIME-Typen text/xml oder ap-
plication/xml. Aufgrund seiner Liste mit bekannten MIME-Typen kann ein Server eine
bestimmte Dateiart auf spezielle Weise versenden bzw. markieren, der Browser aber vor
allen Dingen eine Datei eines bekannten Typs korrekt behandeln. Werden unbekannte
Dateitypen geladen, kann ein Browser nicht direkt damit umgehen. Dann kann er an-
bieten, die Datei zu speichern oder über zusätzliche Informationsquellen (etwa einen
Benutzerdialog) nach der Behandlungsweise zu forschen. Die Verwendung von MIME-
Typen umgeht zugleich das Problem, dass viele Dateierweiterungen von mehreren Pro-
grammen bearbeitet werden können (etwa .bmp, was unter Windows von zahlreichen
Grafikprogrammen bearbeitet werden kann). MIME-Typen geben eindeutig das zu ver-
wendende Programm an. MIME-Typen werden nach folgendem Schema angegeben:

Hauptkategorie/Unterkategorie.
Hauptkategorien sind etwa text, image oder audio. Unterkategorien von text sind bei-
spielsweise plain (eine reine Textdatei), javascript (beim Einbinden von JavaScripts)
28 3 Versionen von JavaScript und Einbindung in Webseiten

oder html (eine HTML-Datei). Unterkategorien von image sind beispielsweise gif oder
jpeg.

• Durch die Angabe des optionalen Attributs defer (ohne Wert) teilen Sie dem Brow-
ser mit, dass der JavaScript-Code keine Ausgabe im HTML-Dokument erzeugt. Diese
Angabe ist rein informell und dient dazu, dass der Browser dadurch das HTML-Do-
kument schneller darstellen kann, da der JavaScript-Code nicht sofort analysiert (ge-
parst) werden muss.
• Bei Bedarf können Sie im <script>-Tag das Attribut lang setzen, um eine Sprache an-
zugeben, die in dem Quellcode verwendet wird.

Wir werden im Buch in den meisten Fällen auf optionale Attribute verzichten.

In JavaScript wurden über die verschiedenen Versionen natürlich gewisse Techniken


erst sukzessive eingeführt. Wenn Sie also solche Techniken einsetzen, sollten Sie sicher-
stellen, dass sich auch nur Browser daran versuchen, die einen benötigten Sprach-
standard unterstützen. Bei der Einbindung eines JavaScripts in eine Webseite hat man
lange Zeit eine JavaScript-Version angegeben. Das erfolgte einfach, indem Sie beim
Wert des language-Attributs ohne Leerzeichen (!) die Versionsnummer anfügen. Beispiel
einer Angabe der JavaScript-Version 1.5

<script language="javascript1.5">

Das macht man aber seit einigen Jahren gar nicht mehr.

[Link] Position des Skript-Containers


Ein <script>-Tag kann im Grunde an jeder beliebigen Stelle innerhalb einer Webseite
platziert werden. Das gilt auch für die nachfolgend noch vertiefte Einbindung von ex-
ternen JavaScript-Dateien. Die Skriptanweisungen werden einfach geladen und aus-
geführt, wo sie notiert sind, wenn die Webseite von oben nach unten im Browser ab-
gearbeitet wird. Wenn der Browser eine auszuführende Anweisung lädt, wird sie aus-
geführt. Entsprechend ist klar, dass unten in einer Webseite notierte Skriptanweisungen
auch erst dann zur Verfügung stehen, wenn die Seite bis dahin geladen ist. Das ist dann
kein Problem, wenn das Skript genau da notiert wird, wo es benötigt wird und es beim
Laden automatisch ausgeführt werden soll. So künstlich oder antik die Situation Ihnen
vielleicht erscheinen mag – es gibt auch heute noch einige Anwendungen, bei denen man
einen Skriptcontainer weit unten in der Webseite (sogar nach dem Body) notiert. Es wird
dann gemacht, wenn man in einem Skript auf Bestandteile der Webseite zugreifen will
und gewährleisten muss, dass diese bereits im Rahmen des DOM-Konzepts als Objekte
zur Verfügung stehen. Zwar gibt es die Möglichkeit, mit Eventhandlern zu arbeiten, die
erst nach dem Laden der Webseite ausgeführt werden. Die Erfahrung zeigt jedoch, dass es
einige Situationen gibt, in denen dies unzuverlässig funktionieren kann. Das Spiel mit der
3.2 JavaScript in Webseiten 29

richtigen Position eines Skriptcontainers oder der Einbindung einer externen JavaScript-
Datei kann massive Auswirkungen auf die Effizienz und Qualität der Webseite haben.

Wenn Sie sich ganz alte Webseiten mit JavaScripts ansehen, erkennen Sie dort oft einen HTML-
Kommentar im Skriptbereich. Direkt nach dem einleitenden <script>-Tag öffnet sich ein HTML-
Kommentar (<!––). Dieser wird unmittelbar vor dem abschließenden Tag </script> mit dem ent-
sprechenden HTML-Kommentar-Endezeichen (––>) wieder geschlossen. Dadurch steht der ge-
samte JavaScript-Code innerhalb eines HTML-Kommentars. Dieses Verfahren zur Absicherung
von alten Browsern ist heute überflüssig. Es ist nur dafür da, dass ein Browser ohne JavaScript-
Kenntnisse den Quelltext nicht in der Seite anzeigt. Aber diese Browser stammen aus den frühen
neunziger Jahren und sind ausgestorben. Beachten Sie, dass hier nicht von Browsern die Rede ist,
bei denen der Anwender JavaScript deaktiviert hat oder JavaScript blockiert wird. Diese verstehen
das <script>-Tag und führen einfach die darin enthaltenen Anweisungen nicht aus.

3.2.3 Verwendung von externen JavaScript-Dateien

Fast immer ist es sinnvoll, JavaScripts in eine oder mehrere externe Datei(en) auszu-
lagern. Diese Vorgehensweise trägt der Tatsache Rechnung, dass man ab einer gewissen
Größe Projekte strukturieren muss. Zudem können gemeinsame Funktionalitäten in einer
Datei bereitgestellt werden und nicht jede Webseite muss deren Implementierung ent-
halten. Natürlich werden die Wartung und Anpassung erheblich erleichtert und auch
sonst gibt es weitere Vorteile. Die Auslagerung in eine externe Datei verwendet wieder
das <script>-Tag, das dann aber über das Attribut src die externe Datei angibt. Optional
ist – wie oben angesprochen – das zusätzliche type-Attribut oder früher auch language.
Die Syntax einer Referenz auf eine externe JavaScript-Datei sieht schematisch so aus:

Beispiel

<script type = "text/javascript" src = "[externe JavaScript-Datei]"></script> ◄

In Anführungszeichen wird hinter dem Attribut src der Name bzw. vollständige URL der
separaten Datei angegeben. Dabei gelten beim Referenzieren von separaten JavaScript-
Dateien die üblichen Regeln für URLs. Entweder geben Sie einen relativen Pfad an oder
aber einen vollständigen URL, der mit dem Protokoll (in der Regel http oder https) be-
ginnt. Die Datei mit dem Quellcode muss – wie HTML-Dateien – eine reine Klartextdatei
sein und ausschließlich JavaScript-Code enthalten. Eine solche JavaScript-Datei enthält
explizit kein Grundgerüst. Wie schon erwähnt, ist die [Link] üblich, aber das
ist nicht zwingend. Hier sind ein paar konkrete Beispiele mit und ohne die type-Angabe:

<script type="text/javascript" src="[Link]"></script>


<script src="[Link]
<script src="lib/js/[Link]"></script>
<script src="../../[Link]"></script>
30 3 Versionen von JavaScript und Einbindung in Webseiten

Eine externe JavaScript-Datei enthält wie ein in einer Webseite notierter <script>-Con-
tainer einfach JavaScript-Code in Form von Klartext. Allerdings wird bei externen Java-
Script-Dateien fast immer mit Funktionen oder Klassen gearbeitet, da sonst alle in der
externen Datei notierten Anweisungen beim Laden einer Webseite mit einer Referenz
auf die externe Datei abgearbeitet werden. Das ist in der Regel nicht gewünscht, denn
meist finden sich mehrere Funktionalitäten in einer einzigen Datei und Funktionen oder
Klassen geben die Möglichkeit, gezielt eine der Schrittfolgen aufzurufen. Sie müssen
natürlich nicht alle in einer Datei oder einem Container deklarierten Funktionen auch
wirklich nutzen. Ansonsten gelten die gleichen Ausführungen wie beim internen Skript-
container.

 Sie können JavaScript-Dateien von beliebigen Webservern oder Verzeichnissen


nachladen, auf die Sie Zugriff haben. Das ist kein (!) Cross-Site-Scripting oder
eine Verletzung der Same Origin Policy (SOP. Dieses Sicherheitskonzept legt für
clientseitige Skriptsprachen oder CSS fest, dass diese auf Objekte zugreifen, die
von einer anderen Webseite stammen oder deren Speicherort nicht der Origin (Her-
kunft) entspricht. Als Herkunft wird dabei die Kombination aus Protokoll (zum
Beispiel HTTP oder HTTPS), Domain und Port in der URL definiert. Nur wenn
alle drei gleich sind, gilt die SOP als erfüllt und der Skript-Zugriff ist möglich. Die
SOP stellt ein wesentliches Sicherheitselement in allen modernen Browsern und
Webanwendungen zum Schutz vor Angriffen dar.

 Bei der Referenz auf externe JavaScript-Dateien muss der <script>-Container


leer bleiben. Die JavaScript-Anweisungen befinden sich alle in der referen-
zierten externen Datei. Sie können keine JavaScript-Anweisungen in den
Container schreiben. Genau genommen muss man diese Aussage präzisieren.
Der Skriptcontainer ist im Sinne von XML ein leeres Element, wenn man ihn
zum Referenzieren einer externen JavaScript-Datei verwendet. Deshalb darf
im Inneren streng genommen auch kein Whitespace-Zeichen (Leerzeichen,
Tabulatoren, Zeilenumbrüche etc.) stehen. Durch das Prinzip der Fehler-
toleranz verhalten sich allerdings die meisten Browser so, dass sie einen
Zeilenumbruch oder ein Leerzeichen im Inneren des Elements nicht als Fehler
interpretieren. Vermeiden Sie diese „Verschmutzung“ dennoch – es gibt ja kei-
nen Grund dazu. Aber ein anderes lästiges Problem tritt bei einigen Browsern
auf. Wenn Sie die vollkommen korrekte XML-Syntax <script type = "text/
javascript" src = "…" /> zur Angabe eines leeren Elements verwenden, melden
einige Browser einen Fehler bzw. sie werden die Verwendung der externen
JavaScript-Datei verweigern. Sie sollten auf diese elegante und wie gesagt
vollkommen korrekte Syntax verzichten und stattdessen die oben gezeigte
Schreibweise wählen, denn mittlerweile verweigern fast alle modernen Brow-
ser diese Notation als leeres Element.
3.3 Der gemeinsame Namensraum 31

3.3 Der gemeinsame Namensraum

Viele Programmiersprachen stellen sogenannte Namensräume zur Verfügung. Ein Namens­


raumkonzept organisiert die Eindeutigkeit von Bezeichnern in einem gemeinsamen
Kontext und regelt damit, wie etwa Variablen, Funktionen und Objekte organisiert und
gruppiert werden können, um Kollisionen und Konflikte zwischen verschiedenen Teilen
einer Anwendung zu vermeiden. Im Wesentlichen ermöglicht es das Namensraumkonzept,
den Code besser zu strukturieren und zu organisieren, was die Lesbarkeit, Wartbarkeit und
Skalierbarkeit verbessert. In JavaScript gibt es jedoch keine eingebaute Unterstützung für
Namensräume im klassischen Sinn. Stattdessen werden Namensräume normalerweise
durch Objekte realisiert und dieser Ansatz ist seit den frühen Versionen von JavaScript ver-
fügbar. Das ist bei Erfahrung in Programmiersprachen wie C# oder Java wahrscheinlich
ungewohnt, aber extrem effizient und elegant, wenn man sich daran gewöhnt hat. Ein ge-
bräuchliches Muster zur Erstellung von Namensräumen besteht darin, ein globales Objekt
zu verwenden und dann alle Variablen und Funktionen innerhalb dieses Objekts zu defi-
nieren. So gehen auch die meisten populäre JavaScript-Frameworks vor. Sie bilden ein
globales Objekt und verlagern sämtliche Features des Frameworks in dieses Objekt.
Das Konzept von Namensräumen wurde jedoch insbesondere mit der Einführung
von ECMAScript 6 (ES6) erweitert, da ES6 Module als eine native Unterstützung für
Namensräume bietet. Mit ES6 können Sie Module definieren, die ihre eigenen Namens-
räume haben und explizit exportierte Elemente angeben, die von anderen Modulen im-
portiert werden können. Diese Module bieten eine robuste und standardisierte Möglich-
keit, Namensräume in JavaScript-Anwendungen zu organisieren und zu verwalten. In
„normalem“ JavaScript müssen Sie jedoch immer beachten, dass es einen gemeinsamen
Namensraum gibt. Das hat Konsequenzen, die leicht zu Problemen führen können, aber,
wenn man das Konzept verinnerlicht hat, eine äußerst elegante und effiziente Program-
mierung gestattet. Aber was bedeutet der gemeinsame Namensraum nun genau? Im
Konfliktfall (identische Bezeichner mehrerer Elemente) wird bei den meisten Browsern
bzw. JavaScript-Engines das zuletzt definierte Element (im Sinn der Abarbeitung der
Webseite oder von Skripten von oben nach unten) verwendet. Das soll ein einfaches Ja-
vaScript demonstrieren ([Link]).

var a = 1;
var a = "Text";
[Link](a);

Ich greife in dem Beispiel etwas vor, da die Deklaration von Variablen noch nicht be-
sprochen wurde. Aber ich gehe davon aus, dass das Skript dennoch verständlich ist.

 Unter einer Deklaration (in einigen Programmiersprachen bzw. Sonderfällen auch De-
finition) versteht man die Festlegung von Dimension, Bezeichner, Datentyp und weiteren
32 3 Versionen von JavaScript und Einbindung in Webseiten

Abb. 3.1 Der gemeinsame Namensraum

Aspekten einer Variable oder eines Unterprogramms. Erst durch eine Deklaration wird
dem Compiler oder Interpreter ein solches Element bekannt gemacht; es ist damit mög-
lich, diese an anderen Stellen im selben Quelltext zu verwenden.

• In der ersten Zeile des Skripts wird eine Variable a deklariert und der Wert 1 zu-
gewiesen. Damit beinhaltet die Variable eine Zahl (numerischer Datentyp).
• In der zweiten Zeile des Skripts wird erneut eine Variable deklariert, die den glei-
chen Bezeichner an erhält. Dieser wird ein Text zugewiesen.
• Die Ausgabe in der dritten Zeile zeigt, dass die zuerst deklarierte Variable mit der Zahl
nicht mehr da ist, sondern nur die nachfolgend deklarierte Textvariable ( Abb. 3.1).

Nun haben Sie in diesem einfachen Beispiel ein Verdecken des zuerst deklarierten Ele-
ments durch ein identisch benanntes nachfolgendes Element in der gleichen JavaScript-
Datei. Sie können in einer Webseite aber auch mehrere verschiedene externe JavaScript-
Dateien und interne Skripts sowie Inline-Referenzen gemeinsam verwenden. Auch das
neue Modulsystem gestattet die Verwendung mehrerer JavaScript-Ressourcen. Alle Ele-
mente landen im gemeinsamen Namensraum und dann können diese Effekte über meh-
rere Ressourcen und Bereiche hin auftreten. Also sollten Sie beachten, dass es in den
verschiedenen Dateien und Bereichen keine Funktionen, Variablen oder andere Elemente
mit gleichem Namen gibt. Wenn alle Elemente zugänglich bleiben sollen!
Aber wenn der gegenteilige Wunsch besteht, braucht man in JavaScript nicht einen
immensen Overhead wie beim Überschreiben (Override) in schwergewichtigeren Spra-
chen wie Java oder C# betreiben. Ich möchte an der Stelle nicht zu weit in die Grund-
lagen der OOP (Objektorientierte Programmierung) einsteigen und wir reden hier auch
nicht von Vererbung im Sinn der OOP, aber der gemeinsame Namensraum und das be-
schriebene Verhalten bei Identität von Bezeichnern kann als Schwäche von JavaScript,
aber auch als große Stärke ausgelegt werden. Ich stehe auf der Seite derer, die darin
einen riesigen Vorteil von JavaScript gegenüber Sprachen wie Java oder gar C# sehen1.

1 Und das, obwohl ich vermutlich in den letzten Jahren viel mehr mit C# und Java programmiere
als mit JavaScript.
3.4 Kann man testen, ob bei einem Browser JavaScript aktiviert ist? 33

Gerade die Verwendung von fremden Ressourcen ist damit erheblich erleichtert. Ent-
weder haben diese ein zentrales Objekt, in dem sie alle Funktionalitäten kapseln. Oder
das ist nicht der Fall und selbst wenn in Bibliotheken oder anderen JavaScript-Dateien
Bezeichner bereits verwendet werden, die man selbst im Code deklarieren möchte, gibt
es keine Fehler oder Beschränkungen. Sie müssen nur Ihre eigenen Codes immer als
Letztes einbinden bzw. aufrufen. Maximal sind dann diese verdeckten Elemente der vor-
her referenzierten Codes eben nicht mehr verfügbar und das kann in vielen Fällen un-
problematisch sein.
Das Verhalten von JavaScript kann man also ausdrücklich nutzen, um ein elegan-
tes und effizientes Überschreiben bzw. Verdecken von Elementen durchzuführen, aber
es kann auch ungewollte Effekte haben. Sie sollten deshalb eine Konfliktsituation ver-
meiden, wenn Sie diesen Vorteil des Überschreibens nicht nutzen wollen.

Wenn Sie in einer Webseite ein Skript verwenden, konnte früher dem <script>-Element in dem
Körper der Webseite ein <noscript>-Container folgen. In diesem konnten Informationen unter-
gebracht werden, die nur bei deaktiviertem JavaScript im Browser angezeigt wurden. Dieser Con-
tainer wird in der Praxis schon lange nicht mehr verwendet, denn JavaScript ist – wie schon er-
wähnt –so gut wie immer aktiviert und in HTML5 wird der Tag sowieso als deprecated betrachtet.

3.4 Kann man testen, ob bei einem Browser JavaScript


aktiviert ist?

JavaScript wird in allen relevanten modernen Webclients unterstützt. Wenn JavaScript


aber deaktiviert ist, kann ein Test auf der Einstiegsseite einer Website interessant sein.
Wie können Sie nun testen, ob bei einem Client JavaScripts überhaupt ausgeführt werden
können oder nicht? Mit JavaScript selbst? Eine Abfrage per JavaScript, ob JavaScript
aktiviert ist, ist ja nicht möglich, wenn es deaktiviert ist. Oder doch? Es geht wirklich,
indirekt. Man nutzt explizit die Tatsache, dass eine JavaScript-Aktion bei deaktiviertem
JavaScript überhaupt nicht funktioniert.

3.4.1 Eine Browserweiche

Das geht beispielsweise so. Mit JavaScript führen Sie eine Weiterleitung zu einem anderen
URL aus. Funktioniert der, muss auch JavaScript aktiviert sein. Wenn Sie den Anwender
nun komfortabel führen wollen, erstellen Sie eine Einstiegsseite in Ihr Webprojekt, die im
Wesentlichen eine solche Weiterleitung enthält und ihn für den Fall von deaktiviertem Ja-
vaScript automatisch zu einem Projekt führt, das ohne JavaScript auskommt. Eine solche
Weiterleitung funktioniert auch ohne JavaScript. Mit HTML sind automatische Weiter-
leitungen mit dem Tag <meta http-equiv = "refresh" content = "[Zeit];URL = [Ziel]"> mög-
lich. Beim Attribut content geben Sie zuerst die Zeitspanne in Sekunden an, nach der die
Weiterleitung erfolgen soll, und dann das Ziel der Weiterleitung. Der entscheidende Trick
34 3 Versionen von JavaScript und Einbindung in Webseiten

beruht nur darauf, die Weiterleitung per HTML erst nach der Weiterleitung per JavaScript
auszuführen. Das nachfolgende Beispiel zeigt so eine automatische Trennung nach Java-
Script und deaktiviertem JavaScript. Beispiel (kap 3_1.html):

<html lang="de"><head>
<meta http-equiv="refresh" content="2;URL=[Link]
    <title>Browser-Weiche für JavaScript</title>
    <script>[Link] = "[Link]
    <a href="[Link] Webprojekt ohne JavaScript</a><br />
    <a href="[Link] Webprojekt mit JavaScript</a>
  </body></html>

Wenn man diese Webseite lädt, erfolgt eine automatische Weiterleitung per JavaScript
mit dem location-Objekt ([Link]), das von der DOM-Schnittstelle des Browsers
bereitgestellt wird. Nun wird diese Anweisung bei deaktiviertem JavaScript aber nicht
ausgeführt. Im Header finden Sie jedoch ein HTML-<meta>-Tag, das aber erst nach
einem Zeitintervall (hier zwei Sekunden und im Allgemeinen unbedingt länger als das
Weiterleitungsintervall des JavaScripts) automatisch auf ein Teilprojekt ohne JavaScript
weiterleitet. Zur Sicherheit notieren Sie noch zwei Hyperlinks, womit die Weiterleitung
auch vom Besucher manuell ausgelöst werden kann. Sie können in jedem Fall davon
ausgehen, dass JavaScript aktiviert ist, wenn die Weiterleitung oder auch das Öffnen
eines neuen Fensters funktioniert hat. Der Umkehrschluss ist jedoch nicht zwingend. In
einigen neueren Browsern können Anwender sehr differenziert angeben, welche Java-
Script-Aktionen sie tolerieren.
Elementare JavaScript-Grundstrukturen
4

Übersicht
In den folgenden Abschnitten lernen Sie elementare Grundstrukturen von Ja-
vaScript kennen. Dies sind logische Strukturen, die Sie in fast jeder Skript- und
Programmiersprache so finden und grundlegend für das Verständnis einer Sprache
sind. Im Einzelnen umfasst das Variablen, Datentypen, Operatoren, Ausdrücke,
Anweisungen und Kommentare in JavaScript. Dabei werden wir nahezu komplett
auf den Browser verzichten, sondern uns rein auf JavaScript konzentrieren und
deshalb mit [Link] arbeiten. Oft ist auch erfahrenen Webentwicklern nicht klar,
wann Sie mit purem JavaScript arbeiten und wann sie Objekte verwenden, die vom
Browser bereitgestellt werden. Durch die Ausführung in [Link] können wir sicher
sein, dass bis auf das console-Objekt nur reines JavaScript verwendet wird, sofern
wir nicht explizit ergänzende Module verwenden.

4.1 Token und Parser

Token bedeutet übersetzt Zeichen oder Merkmal. Wenn ein JavaScript-Interpreter Quell-
code in Maschinenanweisungen übersetzen will, muss er vorher zunächst herausfinden
(oder mitgeteilt bekommen), welche Token oder Symbole im Code benutzt werden und
sie in sinnvolle Zusammenhänge setzen. Einen Token kann man deshalb auch als Sinn-
zusammenhang verstehen. So ist etwa in der menschlichen Sprache ein Wort nicht
nur die Summe seiner Buchstaben oder Zeichen beziehungsweise akustisch ein Klang,
sondern besitzt einen konkreten Sinnzusammenhang, den das interpretierende System
(der menschliche Geist) mit einer bestimmten Bedeutung assoziiert. Allerdings muss
das interpretierende System sowohl die Sprache als auch den konkreten Begriff ver-
stehen, sonst bleibt ein Token einfach nur die Summe seiner Buchstaben oder Zeichen

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 35


Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
36 4 Elementare JavaScript-Grundstrukturen

beziehungsweise im akustischen Fall ein Lautgebilde ohne Bedeutung. Stellen Sie sich
einfach vor, dass Sie ein Wort in einer Ihnen fremden Sprache hören. Für Sie ist das dann
kein Token, aber für jemanden, der diese Sprache versteht, schon. Aber wenn Sie sich
das Wort übersetzen lassen, dann wird es auch für Sie in Zukunft ein Token sein.

4.1.1 Zerlegen von Quelltext

Der gesamte Teil der Assoziation mit gültigen Token bei einer Programmiersprache wird
bei der Übersetzung eines Quelltextes vom sogenannten Parser übernommen. Dieser
kann ein eigenständiges Programm sein, aber oft ist er auch in einen Interpreter oder
Compiler bereits integriert. Allgemein versteht man in der Informatik unter einem Par-
ser (engl. to parse – analysieren) ein Computerprogramm (oder einen Teil eines Pro-
gramms), das für die Zerlegung und Umwandlung einer beliebigen Eingabe in ein für die
Weiterverarbeitung brauchbares Format zuständig ist.
In unserem Zusammenhang zerlegt ein Parser einen HTML-Code oder auch einen
JavaScript-Quelltext in Token und bringt diese dann in eine Reihenfolge, sodass ein
Compiler oder Interpreter Token richtig verarbeiten kann. Der gesamte Quelltext muss
sich also vor einer konkreten Übersetzung respektive Ausführung von Anweisungen erst
einmal in logisch sinnvolle Einheiten zerlegen und in gültige Arten von Token einordnen
lassen. Sprachelemente werden dabei auf ihre Richtigkeit geprüft. Alle Operationen mit
einem Token werden mit dem spezifischen Wert des Tokens durchgeführt, also nicht mit
den Buchstaben und Zeichen des Tokens selbst, sondern mit dem, was in unserem Fall
JavaScript mit dem Token assoziiert. Es gibt in JavaScript wie in den meisten anderen
Computersprachen fünf Arten von Token, wobei die letztgenannten Kommentare, Trenn-
zeichen und Whitespaces als auch die zuerst genannten Bezeichner eine Sonderstellung
einnehmen:

1. Bezeichner oder Identifier


2. Literale
3. Operatoren
4. Kommentare
5. Trennzeichen und Whitespaces

JavaScript ist bezüglich seiner Syntax der Programmiersprache C angelehnt. Sie werden zwar
nicht alle Elemente und Grundstrukturen in JavaScript wiederfinden, die es in C gibt. Aber sehr
viele syntaktischen Elemente und Grundstrukturen, die Sie in JavaScript vorfinden, stammen aus
C. Deshalb werden JavaScript-Einsteiger mit einem C-Background oder Kenntnis einer syntak-
tisch verwandten Sprache wie Java, C#, Perl oder PHP sich in der Syntax gleich zurechtfinden.
Das Problem ist aber, dass die syntaktische Ähnlichkeit zwischen den Sprachen insbesondere
bei JavaScript den Blick darauf verstellen kann, dass die konzeptionellen Gemeinsamkeiten oft
gering sind und die Funktion von JavaScripts von zig Dingen (das diffizile Verhalten der Browser,
verschiedene automatische Verhaltensweisen des Interpreters, die Stelle der Einbindung eines
4.1 Token und Parser 37

Skripts in eine Webseite, die Art des Aufrufs einer Funktion, der unterschiedliche Aufbau des
DOM in verschiedenen Browsern etc.) beeinflusst werden kann, die in vielen anderen Sprachen
keine Rolle spielen. Dazu kommt der Umgang mit Datentypen.

[Link] Kommentare, Leerzeichen und Trennzeichen


Beginnen wir die Erklärungen zunächst von hinten. Denn die da genannten Trenn-
zeichen wie Klammern oder Kommata als auch Leerzeichen sind wichtige Möglich-
keiten, um Token zu trennen. Das ist an vielen Stellen notwendig, kann aber auch an
anderen Stellen rein zur Übersicht des Quellcodes verwendet werden. Damit sind solche
Zeichen in den meisten Programmiersprachen einerseits wie Kommentare (Abschn. 4.2)
zu sehen1 und keine klassische Token, obwohl sie auf Quelltextebene wie Token notiert
werden. Aber sie verändern die Bedeutung von Token. Ein Leerzeichen macht etwa aus
einem Bezeichner zwei Bezeichner oder Klammern können die Art ändern, wie eine
Berechnung durchgeführt wird. Kommentare hingegen können durchaus Token sein.
Und zwar für ein anderes interpretierendes System. Darauf kommen wir gleich zurück
(Abschn. 4.2).

[Link] Operatoren
Operatoren sind Token, die mit ihren Operanden Operationen ausführen, etwa mathe-
matische Berechnungen, Wertzuweisungen, Vergleiche etc. Wir gehen detailliert auf die
Operatoren unter JavaScript ein (Abschn. 4.7).

[Link] Literale
Literale sind Werte. Etwa eine Zahl oder ein Text. Aber man muss sich klarmachen, dass
es auch explizit Token (sinnbehaftete Ausdrücke) sind. Es gibt in JavaScript vordefinierte
Literale und verschiedene Schreibweise für die unterschiedlichen Arten an Literalen.

[Link] Schlüsselwörter
Unter dem Begriff Schlüsselwörter fasst man alle Wörter zusammen, die ein essenziel-
ler Teil der Sprachdefinition sind und die in JavaScript eine besondere Bedeutung haben.
Auch die Behandlung von Schlüsselwörtern wird auf verschiedene Abschnitte des Bu-
ches verteilt und an den passenden Stellen genauer abgehandelt.

[Link] Bezeichner
Mit dem Token-Typ Bezeichner oder Identifier berühren wir ein Thema, das auf ver-
schiedene Abschnitte des Buches verteilt wird. Es handelt sich hier um die Namen für
Klassen, Objekte, Variablen, Funktionen, Konstanten etc. Wir gehen an den passenden Stel-
len genauer darauf ein. Aber in Hinsicht auf einen Sinnzusammenhang gilt es deutlich zu
machen, dass ein Bezeichner Dingen einen Sinnzusammenhang zuordnet, den ein Parser

1 Also außer einer Trennung von Token keine „Bedeutung“.


38 4 Elementare JavaScript-Grundstrukturen

nicht erkennen kann. Der Name einer Variablen kann sprechend sein (etwa vname oder vor-
name, wenn der Vorname einer Person gemeint ist), aber aus Sicht eines Parsers ist dieser
Sinnzusammenhang nicht erkennbar.

4.2 Kommentare

Ein Kommentar wird in jeder Dokumentenbeschreibungs-, Skript- oder Programmier-


sprache mit spezifischen Token gekennzeichnet. Er kann in der Regel an jeder Stelle
innerhalb eines Quelltexts stehen, denn Kommentare haben keinerlei Auswirkung auf die
konkrete Abarbeitung eines Quelltexts. Kommentare sorgen aber für eine bessere Les-
barkeit des Quelltexts und ermöglichen das Einfügen von Hintergrundinformationen in
einen Quelltext. Wichtig ist ebenso die Möglichkeit, zeitweise bestimmte Teile eines
Programms oder Dokuments über Kommentare ausblenden zu lassen. Jeder Parser er-
kennt seine sprachspezifischen Kommentare anhand bestimmter Zeichenfolgen im
Quellcode (Token). In JavaScript gibt es drei Varianten von Kommentaren.

• Ein einzeiliger Kommentar in JavaScript beginnt mit einem Doppel-Slash (//) und
endet mit dem Zeilenende. Das bedeutet, alle Zeichen in einer Zeile hinter dem Dop-
pel-Slash werden vom JavaScript-Interpreter ignoriert.
• Ein mehrzeiliger Kommentar beginnt mit einem Slash, gefolgt von einem Stern (/*),
schließt beliebigen Text ein und endet mit den gleichen Zeichen in umgekehrter Reihen-
folge – einem Stern gefolgt von einem Slash (*/). Diese Form von Kommentar kann über-
all beginnen und aufhören. Man nutzt diese Form eines Kommentars gerne, um ganze
Teile eines Skripts auszukommentieren und sie bei Bedarf wieder zur Verfügung zu
haben, indem man einfach Anfang- und Endezeichen des Kommentars wieder beseitigt.
• Es gibt nun in JavaScript eine Spezialversion von einem mehrzeiligen Kommentar,
der zu Dokumentationszwecken verwendet werden kann. Dabei wird der mehrzeilige
Kommentar mit /** begonnen (also zwei Sternchen). Aus Sicht von JavaScript ist das
dann nur ein gewöhnlicher mehrzeiliger Kommentar. Aber es gibt Dokumentations-
tools, die dies als Token verstehen und nachfolgenden Kommentar verarbeiten kön-
nen, um eine Dokumentation zu erstellen. Wir werden das noch genauer besprechen.

Bei der Zerlegung von Quellcode in Token werden Kommentare also aus dem Text ent-
fernt und damit keine Token im Sinn der Skript- bzw. Programmiersprache. Sie passen
dennoch in das Gesamtkonzept der Token, weil sie auf Quelltextebene wie Token notiert
werden und von spezifische Kennzeichen/Token von den Passagen im Text abgetrennt
werden, die letztendlich übersetzt werden. Zudem kann ein Kommentar zwar für das eine
interpretierende System kein Token sein, für ein zweites System jedoch schon, was schon
angedeutet wurde. Gerade bei Webseiten ist das keinesfalls abstrakt, denn man hat da
oft gemeinsam HTML- und JavaScript-Bereiche in einer Datei. Ein HTML-Kommentar
ist aus Sicht von JavaScript kein Kommentar-Token und umgekehrt ist ein JavaScript-
Kommentar-Token für HTML ohne Bedeutung. Ein HTML-Kommentar sieht etwa so aus:
4.2 Kommentare 39

Beispiel

<!-- … --> ◄

Aber das doppelte Minuszeichen (--) ist in JavaScript ein Operator (Abschn. 4.7.2). Tat-
sächlich gab es früher Browser, die deshalb beim sequenziellen Analysieren von Quell-
code Probleme hatten. In ganz alten Quellcodes findet man deshalb für interne Java-
Script-Container manchmal folgende Syntax:

Beispiel

<script language = "javascript">


<!--

//-->
</script> ◄

Bewusst wird hier das veraltete Attribut language verwendet, weil dieses zu dieser Zeit
üblich war. Wobei das Attribut in unserem Zusammenhang nebensächlich ist. Es geht um
die zwei Arten von Kommentar-Token. Mit den HTML-Kommentaren wurden früher oft
die JavaScript-Anweisungen vor Browsern versteckt, die kein JavaScript kannten. Java-
Script-fähige Browser kannten hingegen auch HTML-Kommentare und haben dann trotz-
dem die enthaltenen Anweisungen ausgeführt. Der Token // hingegen hat den Operator –
als JavaScript-Kommentar versteckt. Das war die Reaktion auf den angedeuteten Fehler in
einigen alten Browsern. Diese waren so mit der Abarbeitung von JavaScript „beschäftigt“,
dass sie nicht „bemerkt“ haben, dass diese zwei Minuszeichen gar kein JavaScript mehr
waren, sondern Teil des abschließenden HTML-Kommentar-Tokens. Diese Probleme und
Details sind mittlerweile obsolet, machen aber aus meiner Sicht sehr gut deutlich, dass
Token bei einer gemeinsamen Analyse des gleichen Quellcodes durch verschiedene Parser
nur für ein System gelten können, für ein anderes nicht unbedingt. Das Verhalten haben
Sie manchmal auch bei Tools, die aus dem Quellcode eine Dokumentation erstellen. Der
oben beschriebene Dokumentationskommentar ist aus Sicht von JavaScript nur ein mehr-
zeiliger Kommentar, manche Dokumentationstools „sehen dahinter einen tieferen Sinn“.

4.3 Schlüsselwörter und definierte Literale in JavaScript

Jede Programmier- und Skriptsprache besitzt bestimmte Zeichenkombinationen (Token),


die ein essenzieller Teil der Sprachdefinition sind. Diese lassen sich in verschiedene
Kategorien unterteilen. Das sind einmal Wörter, die bereits eine feste Bedeutung haben,
sogenannte Schlüsselwörter.

 JavaScript unterscheidet grundsätzlich Groß- und Kleinschreibung (case-sensitiv).


Alle Schlüsselwörter in JavaScript werden vollständig kleingeschrieben.
40 4 Elementare JavaScript-Grundstrukturen

Wenn ein Schlüsselwort im Quellcode auftaucht, wird automatisch die dafür vor-
gegebene Aktion ausgeführt, sofern es in syntaktisch korrekter Form verwendet wurde.
Andere Token sind definierte Werte für manche Situationen. Solche Wörter dürfen auf
der anderen Seite nicht als Bezeichner für Variablen oder Funktionen verwendet werden,
denn dann könnte der JavaScript-Interpreter etwa nicht entscheiden, ob ein Bezeichner
vorliegt oder ein Schlüsselwort, das eine vorgegebene Reaktion auslösen soll.
JavaScript hat aktuell benutzte Schlüsselwörter reserviert, aber auch bestimmte noch
nicht verwendete Zeichenkombinationen für zukünftige Sprachvarianten. Die nach-
folgende Tabelle enthält alphabetisch die Schlüsselwörter von ECMAScript 2021 (ES12)
bzw. den aktuell modernen JavaScript-Varianten sowie Token mit definierten Werten,
wobei Sie beachten müssen, dass in früheren Versionen manche Token u. U. keine Be-
deutung haben oder nicht vorhanden sind. Dennoch dürfen auch Token ohne Bedeutung
in diesen Versionen nicht für eigene Bezeichner verwendet werden (Tab. 4.1).
Es gibt noch weitere reservierte Token, die auch unter ES12 bzw. derzeit aktuellen
Versionen von JavaScript keine Bedeutung haben, aber teils in darauf aufbauenden Spra-
chen oder vielleicht in Zukunft. Beispielsweise sind das short, static, synchronized, tran-
sient oder volatile. Darüber hinaus gibt auch noch einige Token wie NaN, die hier nicht
aufgelistet sind, aber dennoch eine reservierte Bedeutung haben und nicht für eigene Be-
zeichner verwendet werden dürfen. Darauf kommen wir gleich zurück (Abschn. [Link]).

4.4 Bezeichner und Namenskonventionen

Bezeichner benennen unter anderem Konstanten, Variablen und Funktionen in Java-


Script-Programmen. Man spricht auch von einem „Namen“, aber „Bezeichner“ ist üb-
licher und mehr technisch. Sie vergeben Bezeichner bei der Deklaration von Elementen
und können über einen Bezeichner auf diese Elemente zugreifen.

4.4.1 Regeln für Bezeichner in JavaScript

Bei der Deklaration von Elementen gelten in JavaScript folgende Regeln:

• Bezeichner müssen mit einem Buchstaben, dem Zeichen $ oder einem Unterstrich _
beginnen.
• Bezeichner dürfen nur Buchstaben, Ziffern und die beiden Sonderzeichen $ und _
enthalten. Deutsche Umlaute wie ä, ö, ü sind zwar möglich, sollten jedoch nicht ein-
gesetzt werden.
• Groß- und Kleinschreibung ist von Bedeutung, da JavaScript case-sensitive ist, d. h.,
zwischen Groß- und Kleinschreibung unterscheidet.
• Bezeichner dürfen keine Leerzeichen enthalten.
• Reservierte Wörter dürfen nicht als Bezeichner verwendet werden.
4.4 Bezeichner und Namenskonventionen 41

Tab. 4.1  Token mit definierter Bedeutung


Token Bedeutung
boolean Rückgabe des Operators typeof beim Datentyp Boolean
break Abbruch in Schleifen und der switch-case-Anweisung – eine Sprunganweisung
case Bezeichnung des konkreten Falls in der switch-case-Fallunterscheidung
catch Definiert einen Block von Anweisungen, der bei der Ausnahmebehandlung aus-
geführt wird, wenn ein Fehler in einem try-Block auftritt
class Definiert eine Klasse
const Deklariert eine konstante Variable mit Gültigkeitsbereich im umgebenden Block
continue Beendet die aktuelle Iteration einer Schleife und startet die nächste Iteration. Also
die Fortsetzung des nächsten Schleifendurchlaufs – eine Sprunganweisung
debugger Unterbricht die Ausführung des Skripts und öffnet den Debugger
default Der Vorgabefall bei switch-case-Fallunterscheidungen
delete Ein Operator zum Löschen eines Datenfeldelements oder einer selbst definierten
Objekteigenschaft
do Startet eine annehmende Schleife
else Definiert die Anweisung oder einen Block von Anweisungen, die ausgeführt wer-
den soll(en), wenn die Bedingung einer if-Anweisung falsch ist
export Exportiert Funktionen, Variablen oder Klassen aus einem Modul
extends Erweitert eine Klasse um eine andere Klasse (Angabe der Superklasse)
false Der Token repräsentiert den Wert falsch
finally Definiert im Rahmen des Ausnahmebehandlungskonzepts über try-catch einen
Block von Anweisungen, der nach der Ausführung eines try-Blocks oder eines
catch-Blocks immer ausgeführt wird
for Startet eine abweisende Schleife
function Ein Token zur Einleitung der Deklaration von Funktionen
if Definiert eine Bedingung, unter der eine Gruppe von Anweisungen ausgeführt
wird
import Importiert Funktionen, Variablen oder Klassen aus einem Modul
in Bestimmt, ob ein bestimmtes Attribut in einem Objekt enthalten ist. Wird auch bei
einem for…in-Iterator eingesetzt
instanceof Bestimmt, ob ein Objekt eine Instanz einer bestimmten Klasse ist
let Deklaration einer blocklokalen Variablen
new Ein Token zur Verwendung bei der Erzeugung von Objekten mit einem Konstruktor
null Der Token repräsentiert ein nicht initialisiertes Objekt
number Rückgabe des Operators typeof beim Datentyp Number
object Rückgabe des Operators typeof beim Datentyp Object
return Beendet die Ausführung einer Funktion und gibt optional einen Wert zurück
string Rückgabe des Operators typeof beim Datentyp String
(Fortsetzung)
42 4 Elementare JavaScript-Grundstrukturen

Tab. 4.1 (Fortsetzung)


Token Bedeutung
super Bei Vererbung Zugang zur Methode eines übergeordneten Objekts
switch Definiert eine bedingte Anweisung mit mehreren möglichen Ausführungspfaden in
der der switch-case-Fallunterscheidung
this Verweis auf das aktuelle Objekt in der Ausführung
throw Auswerfen einer Ausnahme
true Der Token repräsentiert den Wert wahr
try Beginn des Umschließens einer kritischen Anweisung im Rahmen des Ausnahme-
behandlungskonzepts von JavaScript
typeof Ein Operator zum Bestimmen des Datentyps einer Variable oder eines Ausdrucks
undefined Der Token repräsentiert den Wert von nicht initialisierten Variablen und Objekt-
eigenschaften
var Deklariert eine Variable mit Funktionsscope
void Gibt an, dass eine Funktion keinen Rückgabewert hat – der leere Funktionstyp
while Startet eine abweisende Schleife
with Führt eine Anweisung mit einem bestimmten Objekt als Kontext aus. Dieser Token
erlaubt es, mehrere Anweisungen mit einem Objekt durchzuführen
yield Wird innerhalb eines Generators verwendet, um einen Wert zurückzugeben

4.4.2 Namenskonventionen

Neben den verbindlichen Regeln für Bezeichner gibt es in JavaScript (wie eigentlich
in jeder Programmiersprache) Konventionen, die man bei Bezeichnern einhalten sollte.
Die Einhaltung solcher Konventionen ist technisch nicht zwingend, jedoch gerade in
JavaScript ist die Bedeutung dieser Namenskonventionen sogar exorbitant. Dabei es ist
durchaus von Bedeutung, ob Sie in großen Projekten oder mit mehreren Personen zu-
sammenarbeiten oder nur für sich alleine programmieren. Während eine Verletzung der
Namenskonventionen im letzteren Fall nur geringe Probleme macht und im Zweifels-
fall nur Sie selbst Ihre eigenen Codes nicht mehr durchblicken, kann die Verletzung von
Namenskonventionen in großen Projekten Zeitabläufe verzögern, unnötige Kosten ver-
ursachen oder Projekte gar ganz zum Scheitern bringen. Deshalb die dringende Empfeh-
lung, dass Sie Namenskonventionen zu 100 % einhalten sollten. Gerade in JavaScript,
das durch seine Freiheiten zwar sehr schlank und flexibel ist, aber ohne strenge Ein-
haltung dieser Konventionen – gerade für Einsteiger – keinerlei Halt und Orientierung
gibt. Die Beachtung der Namenskonventionen kann hingegen Syntaxstrukturen in Java-
Script durch Metainformationen eindeutig machen.
Die folgenden Konventionen haben sich in JavaScript etabliert (die genannten
Syntaxstrukturen werden in der Folge noch erläutert und an den jeweiligen Stellen
4.4 Bezeichner und Namenskonventionen 43

werden die Konventionen noch einmal rekapituliert). Beachten Sie, dass es in einigen
Zusammenhängen andere Regeln geben kann und insbesondere Microsoft die üblichen
JavaScript-Konventionen für sein Habitat an einigen Stellen verändert hat:

• Bezeichner von Eigenschaften, Variablen und Funktionen/Methoden in JavaScript be-


ginnen immer mit einem Kleinbuchstaben. Allerdings kann es begründete Ausnahme-
situationen geben, wo man explizit durch ein abweichendes Großschreiben bestimmte
Metainformationen vermitteln will. Abweichungen sollten aber sparsam eingesetzt
und gut gerechtfertigt sein.
• Bei längeren, zusammengesetzten Bezeichnern verwendet man die Camelnotation
(auch Höckernotation genannt), bei der jedes neue Teilwort mit einem Großbuch-
staben an das vorherige Teilwort angefügt wird. Etwa datenPerson für eine Variable.
Alternativ kann man auch den Unterstrich zum Zusammenfügen verwenden, aber die
Schreibweise ist in JavaScript eher unüblich. Nur bei Konstanten ist sie üblich, denn
diese werden vollständig großgeschrieben und damit kann man nicht mit Groß- und
Kleinschreibung logische Trennungen vornehmen.
• Deutsche Umlaute, im Prinzip erlaubte Sonderzeichen außer dem Unterstrich etc.
unterbleiben beim Bezeichner.
• Klassen/Prototypen werden immer mit einem Großbuchstaben begonnen und dann
kleingeschrieben. Es gilt weiter die Camelnotation.
• Klassen/Prototypen werden in Singularform notiert.
• Konstanten (und nur diese Elemente) werden vollständig großgeschrieben.
• Ein Konstruktor hat in JavaScript immer den gleichen Bezeichner wie die zugehörende
Klasse bzw. der Prototyp. Daher muss ein Konstruktor immer großgeschrieben wer-
den. Da ein Konstruktor aber in JavaScript eine Funktion ist, widerspricht das der
Konvention, dass Funktionen mit einem Kleinbuchstaben beginnen. Die Folge ist fast
zwangsläufig und verdeutlicht die Bedeutung der strengen Einhaltung von Namenskon-
ventionen. Wenn Sie diese zu 100 % einhalten, ist die einzige Funktion, die mit einem
Großbuchstaben beginnt, ein Konstruktor und umgekehrt muss jede Funktion, die mit
einem Großbuchstaben beginnt, eine Konstruktorfunktion sein.

Sie werden die Einhaltung dieser Konventionen in den vorgefertigten Schlüsselworten, Funktio-
nen, Methoden, Klassen/Prototypen oder Konstanten von JavaScript konsequent wiederfinden.
Aber auch in zusätzlichen Programmierschnittstellen wie DOM.

4.5 Anweisungen

Eine Anweisung ist eine Quellcodezeile, die bestimme Befehle enthält. JavaScript-
Anweisungen können über mehrere Zeilen im Editor gehen, denn sie enden immer (bis
auf Ausnahmen, die Sie aber grundsätzlich nie ausnutzen sollten) erst mit einem Semi-
kolon. Dieses Semikolon teilt dem JavaScript-Interpreter des Browsers mit, dass das
44 4 Elementare JavaScript-Grundstrukturen

Ende der Anweisung erreicht ist und die Befehle nun ausgeführt werden können. An-
weisungen können nach Typen unterschieden und auch zusammengefasst werden.

4.5.1 Ausdrücke

Bei einem Ausdruck beziehungsweise einer Ausdrucksanweisung geht es um die Mani-


pulation von Daten, welche in Variablen abgelegt sind, beziehungsweise die Verbindung
von Zeichen, die selbst bereits einen Wert darstellen (also Zahlen- oder Textliterale). Ein
Ausdruck in JavaScript ist also ein Vorgang, mit dem der Wert einer Variablen verändert
oder bereitgestellt wird. Dabei steht in der ausgeschriebenen Form immer auf der lin-
ken Seite eines Ausdrucks der Name der Variablen, in welcher der neue Wert gespeichert
werden soll. Beispiele:

Beispiel

gewicht = 86 + 15;
kontostand = 1000–150;
pi = 3.14; ◄

Es gibt in JavaScript zwei Typen von Ausdrücken:

• Numerische Ausdrücke. Dies sind mathematische Operationen, etwa die Addition oder
Subtraktion von zwei Werten. Um diese Arithmetik durchführen zu können, benötigen
Sie Operatoren (Abschn. 4.7). Im Fall von numerischen Ausdrücken sind das numeri-
sche Operatoren, die auch gelegentlich arithmetische Operatoren genannt werden.
• Logische (boolesche) Ausdrücke. Damit werden Vergleiche durchgeführt. Solche Aus-
drücke gehören zu den Kerntechniken jeder Programmiersprache. Erst darüber können
Kontrollstrukturen realisiert werden. Logische Ausdrücke werden also zur Steuerung
von Programmabläufen verwendet. Bei logischen Ausdrücken wird nur nachgeschaut,
ob ein Vergleich richtig oder falsch ist. Das Ergebnis wird der Wert true sein, wenn der
Vergleich richtig war, und false, wenn der Vergleich falsch ist (Abschn. [Link]).

4.5.2 Blöcke

Blöcke sind im eigentlichen Sinn keine Anweisungen (obwohl sie auch Block-
anweisungen genannt werden), sondern die Zusammenfassung mehrerer Anweisungen
zu einer Gruppe. Diese werden in geschweifte Klammern eingeschlossen ({}). Diese
markieren Anfang und Ende eines Blocks. Der JavaScript-Interpreter des Browsers wird
einen solchen Block als Einheit behandeln und ihn als Ganzes abarbeiten. Ein Block
kann überall da stehen, wo auch eine einzelne Anweisung stehen kann. Insbesondere
4.6 Datentypen, Variablen und Literale 45

steht hinter der schließenden Klammer eines Blocks in der Regel kein (!) Semikolon.
Das Beendigungszeichen einer Anweisung muss innerhalb des Blocks nach jeder einzel-
nen Anweisung notiert werden.

 Besonders interessant ist, dass in JavaScript jeder Block seinen eigenen Gültig-
keitsbereich (scope) hat. Das ist vor allen Dingen in Verbindung mit var und let
sehr interessant (Abschn. [Link]).

4.6 Datentypen, Variablen und Literale

Variablen und Literale wurden bereits in den bisherigen Skripten verwendet. Jetzt gehen
wir die Details an. Variablen benannte Stellen im Hauptspeicher eines Computers, denen
zur Laufzeit eines Programms/Skripts temporär Werte zugeordnet werden können. Dies
erfolgt in JavaScript über ein Gleichheitszeichen (eine Wertzuweisung). Angesprochen
werden diese Variablen über Bezeichner (Namen), die sie vorher zugewiesen bekommen
haben (Deklaration). Und neben dem Bezeichner dieses Hauptspeicherbereichs ist die
Art des Inhalts von Bedeutung – der Datentyp.

4.6.1 Datentypen und die JavaScript Global Properties

Es ist bei einer Programmierung elementar zu wissen, von was für einem Typ eine Variable
ist. Die Wahl des Typs einer Variablen (auch genannt) hängt unter a Datentyp nderem
davon ab, was man mit der Variablen machen möchte. Umgekehrt legt die Art des Daten-
typs fest, was man dann mit der Variablen machen kann. Eine Variable mit Text als In-
halt ist beispielsweise von einem ganz bestimmten Datentyp. Sie werden mir sicher Recht
geben, dass die Division von zwei Texten ziemlich sinnlos ist. Es gibt aber auch Daten-
typen, in denen man Zahlen darstellen kann. Damit würde eine Division schon eher Sinn
machen. Auf der technischen Seite gibt es ebenfalls diverse Gründe, warum eine Variable
einen Datentyp benötigt. Im Wesentlichen muss das System wissen, wie viel Speicher für
eine Variable oder eine direkte Information wie eine Zahl eingeplant werden muss.

[Link] Lose Typisierung und Duck-Typing in JavaScript


Es ist nun sowohl Segen als auch Fluch von JavaScript, dass man Details zu Datentypen
gar nicht so genau wissen muss/kann, denn JavaScript verfolgt bei der Zuweisung von
Werten an Variablen das Konzept der sogenannten losen Typisierung (loose typing).
Dies bedeutet, dass man in JavaScript bei einer Deklaration zwar die Namen von Va-
riablen festlegt, dabei jedoch die Datentypen nicht deklariert werden (können!). Dadurch
bekommt eine Variable erst dann einen bestimmten Datentyp zugewiesen, wenn ihr der
Wert eines bestimmten Typs zugewiesen wurde. Diese implizite Typfestlegung erfolgt
46 4 Elementare JavaScript-Grundstrukturen

vollkommen automatisch und ohne explizites Zutun des Programmierers. Man nennt das
Verhalten auch „Duck-Typing“.

 Definition Duck-Typing ist ein Konzept, das überwiegend bei der objektorientierten
Programmierung, zum Einsatz kommt, aber allgemein eine indirekte Typbestimmung
beschreibt. Der Typ eines Objektes oder einer Variablen allgemein wird durch das Vor-
handensein bestimmter Merkmale festgelegt. Der Name ergibt sich in Anlehnung an ein
Gedicht von James Whitcomb Rileys: „When I see a bird that walks like a duck and
swims like a duck and quacks like a duck, I Call that bird a duck.“ „Wenn ich einen
Vogel sehe, der wie eine Ente läuft, wie eine Ente schwimmt und wie eine Ente schnat-
tert, dann nenne ich diesen Vogel eine Ente.“
Beim Duck-Typing wird der Typ zur Laufzeit des Programms also indirekt geprüft.

Ein wesentlicher Vorteil der losen Typisierung ist neben der einfachen Einführung einer
Variablen, dass Sie den Datentyp jederzeit ohne Aufwand ändern können. Eine explizite
Typumwandelung (Casting) ist nicht notwendig. Wenn Sie beispielsweise einer Variab-
len den Wert einer Zeichenkette zuweisen, können Sie ihr später eine Zahl zuweisen und
verändern damit automatisch ihren Typ. Welcher Wert auch immer in der Variablen ent-
halten ist, er definiert den Datentyp. Dies ist bei vielen konventionellen Programmier-
sprachen (wie C und C +  + und Java) anders. Dort muss der Programmierer auf jeden
Fall den Datentyp festlegen, bevor in einer Variablen ein Wert gespeichert werden kann.
In der so festgelegten Variablen kann dann auch nur ein Wert des einmal fixierten Typs
gespeichert werden. Jede Wertzuweisung eines anderen Datentyps wird einen Fehler er-
zeugen. Man nennt so etwas dann statische Typisierung von Variablen oder auch streng
typisiert.

Lose Typisierung macht den Einstieg in eine Sprache wie JavaScript (oder auch PHP) für Ein-
steiger einfach, hat aber nicht nur Vorteile. Sonst würden die mächtigeren Programmiersprachen
nicht den Aufwand mit der statischen Typisierung betreiben und auch neuere Versionen von Java-
Script und insbesondere TypeScript bieten die Möglichkeit für eine statische Typisierung. Lose Ty-
pisierung erhöht die Fehlerwahrscheinlichkeit in komplexeren Quellcodes und macht die Wartung
unter Umständen aufwendig. Merken Sie sich schon an der Stelle – obwohl JavaScript es zulässt,
sollten Sie eine Variable nicht unnötig im Typ verändern, indem Sie Literale oder Rückgabewerte
von Funktionen unterschiedlichen Typs zuweisen. Das ist schlechter Programmierstil und kann
Verständlichkeit und Wartbarkeit von Quellcode reduzieren.

JavaScript ist zwar lose typisiert und hält den Programmierer von der konkreten An-
gabe und dem Zugriff auf Datentypen fern. Das bedeutet aber nicht, dass nicht intern mit
Datentypen gearbeitet wird und man keine Informationen zu Datentypen erhalten kann
(Abschn. 4.7.1).

Hintergrundinformation
Die explizite Deklaration von Datentypen für Variablen, aber Rückgabewerte und Parameter von Funk-
tionen stehen schon lange auf der der Wunschliste mancher JavaScript-Programmierer. TypeScript
stellt solche Möglichkeiten bereit wie die Typfestlegung einer Variablen über eine Syntax der Art:
4.6 Datentypen, Variablen und Literale 47

var a:Integer; //Variable a vom Typ Integer.


Auch bei Funktionen gibt es da die Möglichkeit zur Definition des Rückgabe- und Parameter-
typs, indem die Typen wie bei Variablen durch einen Doppelpunkt getrennt an den jeweiligen Para-
meter bzw. an die Funktion geheftet werden. Also so etwas:
function f(a:Integer, b:Integer, c:Integer):Integer.
Das aktuelle ECMAScript bzw. JavaScript lässt das bisher aber nicht zu.

[Link] Datentypen, besondere Token und globale Eigenschaften


JavaScript besitzt einige Haupt- und einige Sonderdatentypen, die aber eben in Java-
Script implizit verwaltet werden.

 Beachten Sie die Großschreibung der Typen. Hier wird eine Notation gewählt,
die den Konventionen von Klassen entspricht. Bei internen Abfragen des Typs
durch JavaScript etwa mit dem vorangestellten Operator typeof (Abschn. 4.7.1)
liefert der Operator den Datentyp jedoch kleingeschrieben und auch wenn Sie
Token mit den entsprechenden Werten im Quellcode schreiben, werden Sie
diese kleinschreiben müssen. Also etwa null oder undefined.

[Link] Grundtypen
JavaScript unterstützt folgende Grundtypen (Tab. 4.2).

[Link] Casting
Die genannten Datentypen kann man zu einer Typumwandlung (Casting) verwenden,
wenn das gewünscht ist. So kann man zum Beispiel aus einem String, der eine Zahl
beinhaltet, einen Number-Typ machen, indem man den String als Parameter an die
Funktion2 Number übergibt. Das kann man mit einem kleinen Script sehen, wobei wir
etwas vorgreifen und neben der Verwendung der Technik von Funktionen den Operator
typeof verwenden, mit dem der Datentyp eines Operant bestimmt werden kann ([Link]).

[Link](typeof "1");
[Link](typeof Number("1"));

Die Ausgabe wird string und number sein, was – wie oben erwähnt – den groß-
geschriebenen Datentypen entspricht und von typeof in Kleinschreibung geliefert wird.

[Link] Zusammengesetzte Datentypen
Auch drei zusammengesetzte Datentypen existieren in JavaScript. Meist werden die aber
gar nicht als explizite Datentypen wahrgenommen (Tab. 4.3).

2 Neben der Tatsache, dass bisher Funktionen noch nicht explizit erläutert wurden, stellt JavaScript
Funktionen sehr „speziell“ zur Verfügung. Das Konzept, wie Funktionen in JavaScript imple-
mentiert werden, ist genial und einfach, aber teils etwas ungewohnt. Wir kommen ausführlich im
nächsten Kapitel dazu.
48 4 Elementare JavaScript-Grundstrukturen

Tab. 4.2  Primitive Datentypen von JavaScript


Typ Beschreibung
Boolean Werte von diesem Typ (auch boolesche Werte genannt) sind Wahrheitswerte, die
nur einen der beiden Werte true oder false (wahr oder falsch) annehmen. Solche
Datentypen werden im Rahmen von Vergleichen genutzt
Number Dieser Datentyp kann in JavaScript sowohl Ganzzahl- als auch Gleitkommawerte
enthalten. Dies ist ungewöhnlich, denn in den meisten Programmiersprachen
werden diese Typen getrennt. Meist gibt es dort sogar mehrere Typen für ganze
Zahlen und auch Kommazahlen. Dabei muss man mit dem Begriff »Kommazahl«
aufpassen, denn diese verwenden in JavaScript anstelle eines Kommas einen Punkt,
um den Nachkommateil abzutrennen (der englische Bezeichner Floatingpoint
macht das auch deutlich). Neben der normalen Zahlenschreibweise gibt es weitere
Schreibweisen, die wir gleich behandeln (Abschn. [Link]). Ebenso den Typ BigInt
Null Zur Darstellung der Abwesenheit eines Werts oder einer leeren Referenz. Das ist
durchaus ein sinnvoller Wert, der sich von einer leeren Zeichenkette oder der Zahl
0 unterscheidet. Sie erhalten null beispielsweise dann, wenn in einem Dialogfenster
mit einer Abbrechen-Schaltfläche diese betätigt wird
String Dieser Datentyp steht für eine Zeichenkette und kann eine Reihe von alpha-
numerischen Zeichen enthalten. Die Anzahl der maximal in einem String erlaubten
Zeichen ist in ECMAScript nicht explizit spezifiziert und implementations-
abhängig. Auf jeden Fall sind in JavaScript mindestens 256 Zeichen in einer
Zeichenkette erlaubt. Damit sind Sie auf jeden Fall auf der sicheren Seite und
bei Bedarf können mehrere Zeichenketten mit dem Operator + verknüpft werden
(Zeichenverkettungsoperator). Eine Zeichenkette in JavaScript kann wie bei HTML
sowohl in doppelte als auch in einfache Hochkommata eingeschlossen werden.
Beide Varianten sind vollkommen gleichwertig. Dies muss aber immer paarweise
passend erfolgen. Viele Programmiersprachen unterscheiden die beiden Notationen,
aber durch die beiden gleichwertigen Möglichkeiten bei JavaScript als auch HTML
kann man auch Verschachtelungen vornehmen, was im Web sehr oft vorkommt.
Einen speziellen Typ für ein einzelnes Zeichen (Charakter) gibt es in JavaScript
nicht
Symbol Ein neu eingeführter Datentyp in ECMAScript 6 zur Erstellung eindeutiger Identi-
fikatoren. In der Praxis ist dieser derzeit nur selten relevant
Undefined Der Sonderdatentyp beschreibt eine nichtdefinierte Situation. Eine Variable besitzt
so lange diesen Wert, bis ihr nach dem Anlegen explizit ein Wert zugewiesen wor-
den ist

[Link] NaN, null und undefined


Eine besondere Erwähnung verdient der Token NaN. Dieser steht für „Not a Number“ und
wird von JavaScript bei einer ungültigen mathematischen Operation geliefert. Das kann
etwa bei der Multiplikation von zwei Texten passieren. Einmal ist seine Groß- und Klein-
schreibung in JavaScript einzigartig und NaN ist wie undefined und null als eine gewisse
Sondersituation zu sehen, die im Zusammenhang mit Datentypen zu berücksichtigen ist.
Der Token NaN ist in dem Sinn auch kein Datentyp, aber er hat einen Datentyp. Genauer
4.6 Datentypen, Variablen und Literale 49

Tab. 4.3  Zusammengesetzte Datentypen


Typ Beschreibung
Array Ein Array bezeichnet eine spezielle Art von Objekt, das eine geordnete Sammlung
von Werten enthält, die über Indizes zugänglich sind
Function Eine spezielle Art von Objekt, die ausführbaren Code repräsentiert und über einen
Namen oder eine anonyme Deklaration definiert werden kann
Object Der allgemeinste Datentyp in JavaScript wird für das Speichern von sämtlichen
Objektreferenzen verwendet. Er ist ein Container für Datenwerte, die als Eigen-
schaften und Methoden organisiert sind

gilt, dass NaN wie undefined zu den sogenannten JavaScript Global Properties (globa-
len Eigenschaften) zählt. Wirklich „eigenartig“ ist, das NaN vom Datentyp Number ist,
was von der Aussagelogik ein Widerspruch zu „Not a Number“ darstellt. Aber auch die
anderen beiden Token null und undefined sollte man vom Datentyp betrachten. Während
null als Object gewertet wird, hat der Token undefined auch tatsächlich den Datentyp Un-
defined. Auch das kann man wieder mit einem kleinen Script sehen, wobei wir wieder den
Operator typeof verwenden, mit dem der Datentyp eines Operant bestimmt werden kann
([Link]).

[Link](typeof undefined);
[Link](typeof null);
[Link](typeof NaN);

Die Ausgabe wird undefined, object und number sein, denn die großgeschriebenen
Datentypen werden von typeof ja in Kleinschreibung geliefert.

 Gerade bei undefined kann diese Situation etwas irritieren, weil Token und
Datentyp scheinbar keine Unterschiede bieten. Aber tatsächlich gibt es die in
der Groß- und Kleinschreibung, was typeof ein bisschen verschleiert.

4.6.2 JavaScript Global Properties und das globale Objekt

Kommen wir noch einmal auf die JavaScript Global Properties zurück. JavaScript bietet
eine Reihe von Eigenschaften, die im sogenannten globalen Objekt (global object) verfüg-
bar sind. Damit sind wir beim Thema der objektorientierten Programmierung, was einen
weiteren kleinen Vorgriff sinnvoll macht, ohne an der Stelle schon zu tief einzusteigen.

Hintergrundinformation
JavaScript folgt von der Art der Erstellung von Strukturen auf den ersten Blick der funktionalen
Programmierung:
50 4 Elementare JavaScript-Grundstrukturen

• Funktionale Programmierung basiert auf der Verwendung von Funktionen als grundlegende
Bausteine. Dabei werden Funktionen als „First-Class Citizens“ betrachtet3. Sie können wie
Daten behandelt werden und beispielsweise als Argumente an andere Funktionen übergeben
oder von Funktionen zurückgegeben werden.
• Objektorientierte Programmierung (OOP) verfolgt einen anderen Ansatz zur Strukturierung
und Organisation von Code. Hier dreht sich alles um Objekte, die Daten (in Form von Eigen-
schaften oder Attributen) und Verhalten (in Form von Methoden) kapseln. Klassen sind Bau-
pläne für die Erstellung von Objekten und Vererbung ermöglicht es, Eigenschaften und Metho-
den von einer Klasse zu einer anderen zu übernehmen.

Doch die Art, wie man in JavaScript von Anfang an schon programmiert hat, verschleiert die Tat-
sache, dass Funktionen und Variablen zwar auf globaler Ebene deklariert werden können, aber dass
es ein globales Objekt gibt, zu dem sie automatisch dynamisch hinzugefügt werden. Das Konzept
des globalen Objekts existiert seit den frühen Versionen von JavaScript und war von Anfang an
Teil der Sprachspezifikation, ohne dass dies vielen Programmierern klar war. In den meisten Skrip-
ten musste man dies auch gar nicht bewusst beachten. Dieses Verhalten unterscheidet sich von
den meisten strengeren objektorientierten Programmiersprachen, aber über diese Strategie schafft
JavaScript einen Spagat zwischen einfacher Programmierung und der Umsetzung wichtiger OO-
Konzepte im Hintergrund, die ein Anwender/Programmierer teils gar nicht bemerkt. Das globale
Objekt dient als Container für verschiedene globale Variablen und Funktionen, die in einer Java-
Script-Anwendung verwendet werden können, und setzt auch globale Variablen und Funktionen in
einen logischen Objektkontext. In Browserumgebungen ist das globale Objekt window, während in
[Link]-Umgebungen das globale Objekt global ist. Dies ermöglichte den Zugriff auf Dinge wie
den DOM (Document Object Model) in Browsern oder Systemfunktionen in [Link].

Das globale Objekt wird von einer JavaScript-Engine vor jeder Ausführung irgendeines
Kontextes erstellt wird und integriert die build-in-Elemente von JavaScript in den Kon-
text. Tatsächlich sind auch Datentypen wie Number oder Boolean Eigenschaften des glo-
balen Objekts mit Funktionsreferenzen als Werten. Deshalb kann man auch das eben ge-
sehene Casting darüber vornehmen. Die tiefergehenden Details behandeln wir bei den
Funktionen und der OOP.
Neben den Sondertoken, den genannten zusammengesetzten Datentypen sowie String
gibt es mit Math und Date zwei eingebaute Objekte, die wir in der Folge auch noch aus-
führlich behandeln. Diese Objekte als auch die JavaScript Global Properties sind überall
im JavaScript-Code zugänglich und können direkt verwendet werden, um auf wichtige

3 Wenn Sie Erfahrung in der OOP haben, können Sie sich First-Class Citizens ein bisschen wie sta-
tische Member vorstellen, wobei der Vergleich „gewagt“ ist. Aber man kann argumentieren, dass
statische Member in bestimmten Sprachen als eine Form von First-Class Citizens betrachtet wer-
den können, da statische Methoden in Klassen ähnlich wie normale Funktionen verwendet werden.
Dennoch sind sie konzeptionell im Grunde verschieden und bedienen unterschiedliche Zwecke in
ihren jeweiligen Programmierparadigmen.
4.6 Datentypen, Variablen und Literale 51

Funktionen und Informationen zuzugreifen. Es sind also Token, die überall im Skript
automatisch verfügbar sind.
Viele gibt es in JavaScript nicht, denn mit Infinity und globalThis ist die Aufzählung
bereits vollständig.

• Die Token Infinity und –Infinity repräsentieren einen numerischen Wert für die posi-
tive oder negative Unendlichkeit.
• Mit globalThis erhalten Sie eine Referenz auf das globale Objekt, unabhängig vom
aktuellen Ausführungskontext. Es bietet eine plattformübergreifende Möglichkeit, auf
das globale Objekt zuzugreifen, unabhängig davon, ob der Code in einem Browser,
auf dem Server oder anderswo ausgeführt wird. Allerdings wird der Token nicht in
allen Systemen unterstützt.

 Auf Token wie undefined, Infinity und null können Sie in JavaScript direkt mit
Operatoren (Abschn. 4.7.3) prüfen, auf NaN hingegen nicht. Dazu müssen Sie
eine spezielle Standardfunktion von JavaScript verwenden. Darauf gehen wir
genauer ein.

Auch wenn man ohne weitere Syntaxvorbereitungen nur eingeschränkte Beispiele erstellen
kann, wollen wir uns dennoch etwas Quellcode ansehen (javascript_global_properties.js).

var a;
[Link](1 / 0);
[Link](globalThis);
[Link](NaN);
[Link](a);

• Zuerst deklarieren wir eine Variable, die keinen Wert zugewiesen bekommt. Ein Zu-
griff auf die Variable mündet in dem wohldefinierten Wert undefined, den wir in der
letzten Zeile des Quellcodes ausgeben. Es ist kein Widerspruch, hier von „wohl-
definiert“ zu sprechen. Ganz und gar nicht. Denn das Skript hat bei einem Zugriff kei-
nen Fehler. Stattdessen gibt es einen Token mit einem eben wohldefinierten Zustand,
mit dem man weiterarbeiten kann. Es ist nur eine Frage der Logik, ob dieser undefi-
ned-Zustand sinnvoll ist oder nicht.
• In Zeile 2 führen wir eine Division durch 0 durch. Resultat ist der Wert „Unendlich“.
Auch das wird in JavaScript als ein wohldefinierter Zustand gewertet und das Skript
wird ohne Fehler fortgesetzt.
• In Zeile 3 greifen wir auf den globalen Kontext zu. Beachten Sie die unterschiedlichen
Eigenschaften unter Windows 11 (Abb. 4.1) und einem Linux-System (Abb. 4.2).
• Die letzte Ausgabe zeigt auf triviale Weise, dass NaN im Quellcode als Token verfüg-
bar ist.
52 4 Elementare JavaScript-Grundstrukturen

Abb. 4.1 Zugriff auf globale Eigenschaften unter [Link] auf einem Windows 11-System

4.6.3 Variablen

Variablen können während der Programmausführung unterschiedliche, veränderbare


Werte annehmen. Für jede Variable wird ein Speicherplatz im Arbeitsspeicher Ihres
Computers reserviert. Im Programm greifen Sie auf diesen Bereich über den Variablen-
namen zu. Variablen haben die folgenden Eigenschaften:

• Variablen können an einer beliebigen Stelle in einem JavaScript-Quelltext durch die


Angabe des Variablennamens deklariert werden. Dabei hängt es an der Anweisung
"use strict", ob moderne Browser bzw. JavaScript-Engines die Verwendung von var
oder let bei der Deklaration einer Variablen erzwingen. Ohne diese Einstellung genügt
die erstmalige Zuweisung eines Werts zu einem neuen Bezeichner, um daraus eine
Variable zu machen. Dies zu verhindern ist eine sehr sinnvolle Sicherheitsstrategie.
Beachten Sie, dass die Anweisung "use strict" in alten Browsern oder Engines keine
Probleme macht, auch wenn sie dort eventuell nicht beachtet wird. Da es sich nur um
einen String handelt, ist die Option auch für alte Systeme unproblematisch. In den
Unterlagen wird diese Einstellung aus didaktischen Gründen teils verwendet, teils
4.6 Datentypen, Variablen und Literale 53

Abb. 4.2 Zugriff auf globale Eigenschaften unter Linux bei [Link]

aber auch weggelassen, um besondere Techniken und Effekte zu zeigen. In der Pra-
xis sollten Sie diese Einstellung aber immer verwenden. Allgemein wird man also
(globale) Variablen mit dem einleitenden Schlüsselwort var oder let deklarieren. Dies
dient es unter anderem der besseren Lesbarkeit, hat aber noch andere Konsequenzen.
Darauf gehen wir gleich ein (Abschn. [Link]).
• Variablen, die Sie über die Angabe var bzw. let deklarieren und denen Sie keinen
Wert zuweisen, haben nach der Deklaration den Zustand bzw. Wert undefined. Das
ist der oben schon beschriebene wohldefinierter Zustand, auch wenn der Bezeichner
möglicherweise etwas Anderes suggeriert.
• Variablen, die weder über var oder let noch durch eine Wertzuweisung deklariert wur-
den, erzeugen bei der Verwendung einen Laufzeitfehler. So können Sie beispielsweise
nicht den Wert einer Variablen auslesen, wenn diese nicht deklariert wurde. Die Inter-
pretation des JavaScript-Codes wird abgebrochen.
• Mehrere Variablen können in einem Schritt durch Kommata getrennt deklariert wer-
den. Diese Anweisung kann über mehrere Zeilen verteilt werden. Einzelne Variablen
können dabei initialisiert werden oder auch nicht.
• Einer Variablen können in JavaScript im Programmverlauf Werte verschiedener
Datentypen zugewiesen werden (lose Typisierung).

Im Folgenden werden unterschiedliche Möglichkeiten der Deklaration und Initialisieren


einer Variablen gezeigt. Dabei wird hier bewusst auf "use strict" verzichtet, um das Ver-
halten zu zeigen (variablen_deklaration1.js). Beachten Sie die Kommentare im Quell-
text, welche die Anweisungen ausführlich erläutern.

 Initialisieren bedeutet, dass eine Variable mit einem Anfangswert belegt wird.
54 4 Elementare JavaScript-Grundstrukturen

/* Variable k bekommt Wert 10 zugewiesen. Gab es die Variable k schon


vorher, ist das die Zuweisung eines neuen Werts. Gab es k noch nicht
ist das die Deklaration einer Variablen k mit gleichzeitiger Initiali-
sierung */
k = 10;
[Link](k);
var j; // Variable j wird deklariert, ist vom Wert aber noch undefined
[Link](j);
let l = 0; // Variable l wird deklariert und mit Wert 0 initialisiert
[Link](l);
var m, n = 10; // Variable m ist nach der Deklaration undefined
                                // und n bekommt Wert 10 zugewiesen
[Link](m);
[Link](n);
var u = 11, v = "Text1", w; // mehrere Variablen mit Wertzuweisung
      // in einer Deklaration in einer Zeile mit und ohne Wertzuweisung
[Link](u);
[Link](v);
[Link](w);
var x = 12,         // mehrere Variablen in einer Deklaration, aber
                   y = "Text2";             // auf mehrere Zeilen aufgeteilt
[Link](x);
[Link](y);

 Es wurde schon thematisiert, aber da es extrem wichtig ist, soll noch einmal
darauf hingewiesen werden. Da JavaScript case-sensitive ist, verweisen z. B.
die Variablen autofarbe und autoFarbe auf unterschiedliche Speicherbereiche.

Ein kleines Beispiel soll jetzt deutlich machen, was "use strict" bewirkt (use_strict.js):

"use strict";
k = 10;
[Link](k);

Wenn Sie das Programm ausführen, bekommen Sie die Fehlermeldung Uncaught Refe-
renceError ReferenceError: k is not defined. In Zeile 2 wird versucht, dem noch nicht
vorhandenen Bezeichner k einen Wert zuweisen. Ohne "use strict" würde damit ein-
fach eine neue Variable angelegt. Mit "use strict" wird das unterbunden. Sie sollten sich
­unbedingt klarmachen, dass diese Unterbindung einen Quantensprung in der Software-
qualität darstellt. Denn sonst können ungewollt durch Schreibfehler oder Unachtsam-
keit neue Variablen entstehen. Dabei ist deren Entstehung meist das kleinere Problem.
Schlimmer ist, dass man bei so einem Fehler normalerweise einer vorhandenen Variablen
einen Wert zuweisen wollte und das damit schiefgegangen ist. Das Skript bzw. Pro-
4.6 Datentypen, Variablen und Literale 55

gramm läuft damit in einem inkonsistenten, fehlerhaften Zustand weiter, der oft nicht
bemerkt wird.

[Link] Deklaration mit let vs var


Betrachten wir nun die Wirkung von var im Vergleich zu let. Zum Teil sehen Sie die ge-
meinsame Wirkung von var und let erst, wenn wir zu Funktionen kommen, da damit glo-
bale und lokale Variablen unterschieden werden. Aber auch ohne Funktionen kann man
die unterschiedliche Wirkung deutlich machen. Die Unterschiede zwischen var und dem
relativ neu eingeführten let in JavaScript sind dies:

• Sichtbarkeit und Gültigkeitsbereich: Eine mit var eingeführte Variable ist überall im
Code des aktuellen Gültigkeitsbereichs (engl. Scope) (global oder lokal innerhalb
einer Funktion oder eines Blocks), aber auch teils übergeordnet und untergeordnet gül-
tig, während eine mit let deklarierte Variable innerhalb des Scopes, in dem sie definiert
wurde, gültig ist, aber nicht oberhalb und auch von untergeordneten Scopes verdeckt
werden kann. Mit let deklarierte Variablen haben eine explizite Block-Scope-Sichtbar-
keit. Das bedeutet, dass sie nur innerhalb des Blocks, in dem sie deklariert wurden, sicht-
bar sind, sei es innerhalb einer Funktion, Schleife oder eines anderen Blockstrukturen.
• Hoisting: Variablen, die mit var deklariert werden, werden gehoistet, was bedeutet,
dass die Deklaration an den Anfang ihres Scope-Bereichs verschoben wird. Die Zu-
weisung bleibt jedoch an ihrer ursprünglichen Stelle. Das bedeutet, dass Variablen-
und Funktionsdeklarationen unabhängig davon, wo sie innerhalb eines Gültigkeits-
bereichs deklariert sind, so behandelt werden, als ob sie am Anfang des Gültigkeits-
bereichs stehen. Dies kann manchmal zu unerwartetem Verhalten führen. Mit let
deklarierte Variablen werden ebenfalls gehoistet, aber im Gegensatz zu var werden sie
nicht initialisiert, bevor der Code ausgeführt wird. Zugriffe auf let-Variablen vor ihrer
Deklaration führen zu einem Referenzfehler (Uncaught ReferenceError).
• Wiederverwendung von Variablenbezeichnern: Mit var können Sie denselben
Variablennamen mehrmals innerhalb desselben Scope deklarieren. Dies kann zu un-
erwarteten Fehlern oder Konflikten führen. Bei Verwendung von let innerhalb des-
selben Blocks mit demselben Bezeichner bei der Deklaration verschiedener Variablen
führt dies zu einem Syntaxfehler. Es sollte klar werden, dass das dann nicht ein Prob-
lem ist, sondern Lösung von einem Problem.

Allgemein ist die Verwendung von let also moderner und sicherer, aber let wird nicht in
alten Browsern unterstützt. Schauen wir uns einige Beispiele an.

"use strict";
// Beispiel mit var - Hoisting
[Link](a); // Ausgabe: undefined
var a = 5;
// Beispiel mit let - Hoisting
56 4 Elementare JavaScript-Grundstrukturen

[Link](b); // Fehler: b is not defined


let b = 10;

Für die Variable a in dem Quellcode let_vs_var_hoisting.js erhalten Sie den Zustand
undefined bei der Ausgabe, obwohl die Deklaration im Scope erst nach dem Zugriff er-
folgt. Für die Variable b hingegen die Fehlermeldung Uncaught ReferenceError Referen-
ceError: Cannot access 'b' before initialization und das Skript wird beendet. Damit wird
ein inkonsistenter Zustand verhindert. Hier folgt ein Beispiel zur Wiederverwendung
von den gleichen Bezeichnern für Variablen bei mehrfachen Deklarationen (let_vs_var_
[Link]).

"use strict";
// Beispiel mit var - Wiederverwendung von Variablennamen
var c = 1;
var c = 2; // Kein Fehler, die Variable c wird überschrieben
// Beispiel mit let - Wiederverwendung von Variablennamen
let d = 3;
let d = 4; // Fehler: Identifier 'd' has already been declared

Und jetzt sehen Sie noch die unterschiedliche Sichtbarkeit in verschiedenen Scopes.
JavaScript definiert neben dem globalen Scope das Innere von Funktionen als eigenen
Scope, aber Funktionen haben wir noch nicht behandelt. Aber JavaScript gestattet auch
die Angabe von getrennten Gültigkeitsbereichen über Blöcke. Das sind Bereiche, die mit
geschweiften Klammern begrenzt werden (let_vs_var.js).

"use strict";
// Beispiel mit var
{
  {
    var x = 10;
  }
  [Link](x); // Ausgabe: 10, da var Funktions-Scope
}
// Beispiel mit let
{
  {
    let y = 20;
  }
  [Link](y); // Fehler: y ist nicht definiert, da let Block-Scope
}

Obwohl die mit var deklarierte Variablen in einem untergeordneten Block deklariert ist,
kann man im übergeordneten Block darauf zugreifen. Bei der Variable, die mit let de-
klariert wurde, geht das nicht.
4.6 Datentypen, Variablen und Literale 57

4.6.4 Konstanten

JavaScript besitzt vorgegebene Konstanten. Man kann in neuen Versionen von JavaScript
aber auch mit const eigene Konstanten deklarieren, wobei man zwingend den Wert zu-
weisen muss. Konstanten haben während der Programmausführung immer einen unver-
änderlichen Wert. Sie können also keine zweite Wertzuweisung an eine Konstante vor-
nehmen, nachdem sie initialisiert wurde. Im Kern unterscheiden sich ansonsten Variablen
und Konstanten nicht sonderlich.

• Genau wie Variablen werden Konstanten mit einem Namen und einem Datentyp ver-
einbart. Der Datentyp ergibt sich wie bei normalen Variablen automatisch aus der An-
gabe des Wertes über Duck-Typing.
• Konstanten werden per Konvention vollständig großgeschrieben. Wobei dies nicht
immer beachtet wird.

4.6.5 Spezielle Schreibweisen für Literale

Kommen wir noch einmal auf Literale zurück, die es in unterschiedlichen Versionen in
JavaScript ging. Man kann zu deren Notation einige Schreibweisen verwenden, die Sie
kennen sollten.

[Link] Ganze Zahlen
Einfache Literale für ganze Zahlen sind trivial. Also Wertdarstellungen wie 1,2, 3 etc.
Ganze Zahlen besitzen keine Nachkommastellen und können negative sowie positive
Werte und den Wert null annehmen. In JavaScript können ganze Zahlen in einer von drei
möglichen Darstellungen eingesetzt werden:

• Dezimaldarstellung: Dies ist die besagte triviale und gebräuchlichste Darstellung von
Zahlen. Die Zahl wird im Dezimalsystem (Basis 10) dargestellt, d. h., dass die Ziffern
0.9 verwendet werden.
• Hexadezimaldarstellung: Die Zahlen werden im Hexadezimalsystem (Basis 16) dar-
gestellt. Die verwendbaren Ziffern sind 0.9, A.F (A = 10, B = 11 … F = 15). Hexa-
dezimale Zahlen werden durch ein einleitendes 0x (bzw. 0X) gekennzeichnet und die
Buchstaben können auch kleingeschrieben werden. Beispiele wären 0XA oder 0Xc.
• Die Oktaldarstellung (zur Basis 8) ist seit JavaScript 1.5 nicht mehr Bestandteil
von JavaScript und wird teils gar nicht, teils nur noch aus Gründen der Abwärts-
kompatibilität unterstützt. Oktalzahlen werden durch eine vorangestellte 0 (Null) ge-
kennzeichnet, wobei man im strikten Modus ein O oder o ergänzen muss. Nur ohne
"use strict" ist ein oktales Literal wie 0755 für den dezimalen Wert 493 noch gültig.

"use strict";
let k = 10, l = 0xA, m = 0o111;
[Link](k, l, m);
58 4 Elementare JavaScript-Grundstrukturen

Bei l sehen Sie eine hexadezimale und bei m eine oktale Darstellung ([Link]).
In JavaScript können Sie den Maximalwert von ganzen Zahlen mithilfe der Kons-
tanten Number.MAX_SAFE_INTEGER bestimmen, wobei wir hier etwas auf OOP vor-
greifen. Diese Konstante repräsentiert den größten Wert, der sicher als ganze Zahl dar-
gestellt werden kann, ohne dass es zu einem Rundungsfehler oder einem anderen Rand-
effekt kommt. Hier ist ein Beispiel, wie Sie den Maximalwert von ganzen Zahlen in
JavaScript bestimmen können (max_ganzezahlen.js):

"use strict";
[Link](Number.MAX_SAFE_INTEGER); // Ausgabe: 9007199254740991

Dieser Wert entspricht 2^53–1, was die maximale Anzahl darstellt, die sicher als ganze
Zahl in JavaScript verwendet werden kann. Beachten Sie, dass man auf manchen Platt-
formen auch größere ganze Zahlen darstellen kann. Es gibt auch eine Number.MIN_SAFE_
INTEGER-Konstante, die den minimalen sicheren Wert für ganze Zahlen repräsentiert.
In neueren Versionen von JavaScript gibt es zudem mit BigInt einem eingebauten
Datentyp samt einem eigenen Literal, der es ermöglicht, ganze Zahlen beliebiger Größe
darzustellen, ohne das Risiko von Überlauf oder Verlust von Genauigkeit bei gro-
ßen Zahlen. Damit kann der begrenzte Wertebereich von herkömmlichen JavaScript-
Nummern gesprengt werden. Sie können BigInt-Literale erstellen, indem Sie einer gan-
zen Zahl das Suffix "n" anhängen oder indem Sie den BigInt()-Konstruktor verwenden.
Hier sind einige Beispiele:

Beispiel

"use strict";
let bigIntLiteral = 1234567890123456789012345678901234567890n;
let bigIntConstructed = BigInt("[Link].[Link].[Link].
567.890");
[Link](bigIntLiteral);
[Link](bigIntConstructed); ◄

BigInt-Werte können jedoch nicht direkt mit anderen Datentypen gemischt werden.
Diese müssen explizit in BigInt-Typen umgewandelt werden, um Operationen zwischen
ihnen durchführen zu können. Sie können auch nicht mit BigInt-Werten mathematische
Operationen wie [Link]() durchführen. BigInt-Werte können auch nicht mit allen Ver-
gleichsoperatoren mit regulären numerischen Werten verglichen werden.

[Link] Gleitkommazahlen und Fixkommazahlen


Gleitkommazahlen können Nachkommastellen besitzen. Das Kennzeichen in der trivia-
len Variante ist in der Darstellung entweder ein Dezimalpunkt. Aber auch wenn ich das
4.6 Datentypen, Variablen und Literale 59

„trivial“ nenne, muss eine Sache beachtet werden. In JavaScript wird die amerikanische
Notation, also ein Punkt (.), verwendet, um die Dezimalstellen einer Zahl kenntlich zu
machen. Die deutschen Bezeichner sind also irreführend und „Floatingpoint“ drückt viel
deutlicher aus, dass der Punkt zur Trennung verwendet wird. Man kann aber auch einen
Exponenten oder beides angeben. Dazu erfolgt eine Kennzeichnung mit E oder e.

"use strict";
let num = 3.14e2; // 3.14 * 10^2 = 314
[Link](num); // Ausgabe: 314
let num2 = 1.5E-3; // 1.5 * 10^(-3) = 0.0015
[Link](num2); // Ausgabe: 0.0015

In diesem Beispiel ([Link]) wird 3.14e2 als 3.14 multipliziert mit 10^2
interpretiert, was 314 ergibt. Ähnlich wird 1.5E-3 als 1.5 multipliziert mit 10^(-3) inter-
pretiert, was 0.0015 ergibt. Die Exponentialnotation wird häufig verwendet, um sehr
große oder sehr kleine Zahlen in einer kompakten Form darzustellen. Bei einer Fix-
kommazahl ist die Position des Kommas vorgegeben. Allerdings kennt JavaScript kein
explizites Literal, sondern wandelt eine Gleitkommazahl mit einer Methode um.

"use strict";
let num = 123.456;
[Link]([Link](2)); // Ausgabe: 123.46

In dem Beispiel ([Link]) wird die toFixed-Methode verwendet, um die


Anzahl der Dezimalstellen auf zwei zu begrenzen. Die Methode rundet die Zahl ent-
sprechend und gibt eine Zeichenkette zurück, die die Zahl im Fixkommazahlenformat
darstellt. Die größte und kleinste speicherbare Gleitkommazahl können Sie durch die
Konstanten Number.MAX_VALUE und Number.MIN_VALUE nutzen, wobei wir hier
wieder etwas auf OOP vorgreifen (min_max_zahlen.js):

"use strict";
[Link]("Max: ", Number.MAX_VALUE);
[Link]("Min: ", Number.MIN_VALUE);

Wenn Sie in große Zahlenbereiche vorstoßen, gibt es ein paar Dinge in JavaScript zu be-
achten. Beim Überschreiten des Wertebereichs der ganzen Zahlen wird teils automatisch
eine Konvertierung in Gleitkommazahlen erfolgt. Aber das ist von der jeweiligen Platt-
form abhängig. Gleitkommazahlen unterliegen aber dabei auch immer potenziellen
Rundungsfehlern. Beachten Sie dies bei Berechnungen mit großen Zahlen. Irgendwann ist
aber auch der Wertebereich der ganzen Zahlen als auch der Gleitkommazahlen erschöpft
und das Ergebnis wird der wohldefinierte Wert Infinity (ueberlauf_ganzezahlen.js).
60 4 Elementare JavaScript-Grundstrukturen

"use strict";
let z1 = 5000, z2 = 2.1, z3 = z1 ** Number.MAX_SAFE_INTEGER,
z4 = z2 * Number.MAX_VALUE;
[Link](z1, z2, z3, z4);
let z5 = 12345678901234567890;
// liefert 12345678901234567000, da die Zahl zuerst in eine
// Gleitkommazahl konvertiert und danach gerundet wird !
[Link]("z5 hat den Wert:", z5);

Das ist die Ausgabe:


5000 2.1 Infinity Infinity
z5 hat den Wert: [Link].234.567.000.

[Link] Zeichenketten (Strings) und Maskierungen


Eine Zeichenkette (String) ist eine Folge von Zeichen, die in JavaScript in einfache oder
doppelte Anführungszeichen eingeschlossen ist. Wir haben schon thematisiert, dass es
somit zwei gleichwertige Formen von Anführungszeichen gibt. Nun gibt es aber auch
die Möglichkeit Steuerzeichen in Zeichenketten zu verwenden, um damit Zeichen zu
maskieren. Beispielsweise einen Zeilenumbruch oder einen Tabulator. Diese maskierten
Zeichen werden mit einem Backslash eingeleitet und als Escape-Sequenzen oder Steuer-
zeichen bezeichnet.

 Die Wirkung der Steuerzeichen ist meist nicht in der Ausgabe in einer Webseite
zu sehen. Bei der Ausgabe von Text im Browser müssen Sie stattdessen für ei-
nige Steuerzeichen HTML-Tags verwenden, da der Browser etwa Tabulatoren
und Zeilenumbrüche im HTML-Code als Leerzeichen interpretiert (Tab. 4.4).

Tab. 4.4  Vordefinierte Escape-Sequenzen


Steuerzeichen Bedeutung
\n new line: Zeilenumbruch (neue Zeile)
\r return: „Wagenrücklauf“: Der Cursor steht in der nächsten Zeile wieder an
Position 1. In JavaScript hat dies keine weitere Bedeutung
\t Tabulator
\f form feed: Seitenvorschub. In JavaScript hat dies keine weitere Bedeutung
\b backspace: ein Zeichen zurück
\" doppeltes Anführungszeichen, auch innerhalb von doppelten Anführungs-
zeichen, z. B. alert("\"");
\' einfaches Anführungszeichen, auch innerhalb von einfachen Anführungs-
zeichen, z. B. alert('\'');
\\ Backslash
\n new line: Zeilenumbruch (neue Zeile)
4.6 Datentypen, Variablen und Literale 61

Beispiel (string_maskierungen.js):

"use strict";
// Escape-Sequenzen in einem String mit einfachen Anführungszeichen
let string1 = 'Dies ist ein String mit Escape-Sequenzen: \n\t - Neue
Zeile \n\t - Tabulator \n\t - Einzelnes Anführungszeichen (\') \n\t -
Doppeltes Anführungszeichen (")';
[Link](string1);
// Escape-Sequenzen in einem String mit doppelten Anführungszeichen
let string2 = "Dies ist ein String mit Escape-Sequenzen: \n\t - Neue
Zeile \n\t - Tabulator \n\t - Einzelnes Anführungszeichen (') \n\t -
Doppeltes Anführungszeichen (\")";
[Link](string2);

Das ist die Ausgabe, die 2 × auftaucht:


Dies ist ein String mit Escape-Sequenzen:
– Neue Zeile
– Tabulator
– Einzelnes Anführungszeichen (')
– Doppeltes Anführungszeichen (")

[Link] Boolesche Werte (Wahrheitswerte)


Die booleschen Werte sind true (wahr) und false (falsch). Wahrheitswerte werden ein-
gesetzt, wenn ein Wert nur zwei Zustände annehmen kann, z. B. Licht an oder Licht
aus. Ausdrücke geben häufig einen booleschen Wert zurück, z. B. beim Vergleichen von
Zahlen. Der Vergleich 2 > 3 liefert das Ergebnis false, weil die Zahl 2 nicht größer ist
als 3.

 Vorsicht Beachten Sie, dass auch andere Typen an Literalen in JavaScript für
boolesche Ausdrücke stehen. Das unterscheidet JavaScript von Sprachen wie
Java oder C#, wohingegen sich alte Sprachen wie C oder Perl genauso ver-
halten. In JavaScript werden folgende Werte als false interpretiert, wenn sie in
einem booleschen Kontext verwendet werden:

• false
• 0 (als Zahl)
• –0 (als Zahl)
• 0n (als BigInt)
• "" oder (der leere String)
• null
• undefined
• NaN (Not-a-Number)
62 4 Elementare JavaScript-Grundstrukturen

JavaScript interpretiert diese Werte allerdings nicht direkt als boolesch. Wenn
Sie jedoch einen dieser Werte in einem booleschen Kontext verwenden, wird
eine implizite Umwandlung durchgeführt. So ein Kontext entsteht beim Ver-
wenden in einer Bedingung bei Entscheidungsstrukturen oder Schleifen oder
aber durch eine explizite Umwandlung, was man mit Boolean() machen kann.
Alle anderen Werte, einschließlich aller anderen numerischen Werte, werden
als true interpretiert, wenn sie in einem booleschen Kontext verwendet
werden.

Hier ist ein Beispiel ([Link]), das zeigt, wie Werte in einem booleschen Kon-
text interpretiert werden:

"use strict";
let a = 0, b = 10, c = -5, d = "", e = 0n, f = "t";
[Link](Boolean(a), Boolean(b), Boolean(c), Boolean(d), Boolean(e),
Boolean(f));

Hintergrundinformation
Wenn Sie Variablen deklarieren, reservieren Sie dafür Speicherplatz. Vollkommen unabhängig
davon, welche Inhalte sie diesen zuweisen. Grundsätzlich wird Speicherplatz benötigt. Auch beim
Erzeugen von Objekten. Dieser muss irgendwann wieder freigegeben werden, wenn diese Struk-
turen nicht mehr benötigt werden. Garbage Collection (kurz GC) in JavaScript ist der Prozess,
bei dem nicht mehr benötigte Speicherbereiche (wie nicht referenzierte Objekte und Variablen)
automatisch erkannt und freigegeben werden, um Ressourcen zu sparen und Speicherlecks zu ver-
meiden. Das funktioniert in JavaScript im Wesentlichen nach der Methode Mark-and-Sweep (Mar-
kieren und Aufräumen). Der Algorithmus besteht aus zwei Phasen:

• Beim Markieren durchläuft der GC alle erreichbaren Objekte, die von Wurzelobjekten aus er-
reichbar sind (zum Beispiel globale Objekte, lokale Variablen im aktuellen Ausführungskontext,
usw.). Alle erreichbaren Objekte werden markiert.
• Beim Aufräumen durchläuft der GC den gesamten Speicherbereich und entfernt alle nicht mar-
kierten Objekte, da sie nicht mehr erreichbar sind und somit nicht mehr benötigt werden. Dieser
Schritt stellt sicher, dass nur noch Objekte im Speicher verbleiben, die tatsächlich verwendet
werden.

Ein weiterer Ansatz für die GC ist die sogenannte Referenzzählung (Reference Counting). In Py-
thon ist das beispielsweise der Kern von dessen GC-Konzept, in JavaScript hingegen eher selten.
Bei der Referenzzählung wird jeder Wert im Speicher verfolgt, und die Anzahl der Referenzen
auf jedes Objekt wird gezählt. Wenn die Anzahl der Referenzen auf null sinkt, wird das Objekt
als nicht mehr benötigt angesehen und der Speicher wird freigegeben. Dieser Ansatz hat jedoch
den Nachteil, dass er Zyklen nicht effektiv behandeln kann und daher nicht so robust ist wie der
Mark-and-Sweep-Algorithmus. Die GC in JavaScript ist eine automatische und transparente Funk-
tion, die in modernen JavaScript-Engines implementiert ist. Entwickler müssen sich normaler-
weise nicht direkt um die GC kümmern, aber es ist wichtig zu verstehen, wie sie funktioniert, um
Leistungsprobleme zu vermeiden und den Speicherverbrauch zu optimieren.
4.7 Operatoren 63

4.7 Operatoren

Operatoren sind Token, die verwendet werden, um Berechnungen durchzuführen oder


Verknüpfungen und Vergleiche zwischen Variablen oder Literalen herzustellen. Man
nennt das dann auch Operationen zwischen oder mit Operanden. Auch wenn diese Be-
griffe bisher nicht explizit erläutert wurden, haben wir an diversen Stellen Operatoren
verwenden. Der Operator typeof wurde sogar schon ausführlich verwendet.
Eine Operation arbeitet in der Regel mit einem oder zwei Operanden. Entsprechend
wird von unären oder binären Operatoren gesprochen. Es gibt in JavaScript auch einen
Operator mit drei Operanden, der triadischer oder ternärer Operator genannt wird. Ein
Operator erzeugt immer einen Ergebniswert. Manche Operatoren können nur in Ver-
bindung mit Variablen oder Literalen eines bestimmten Datentyps (sinnvoll) eingesetzt
werden. Man kann Operatoren nach ihrer Art einteilen. Das wollen wir tun, wobei diese
Einteilung logisch, aber nicht zwingend ist.

4.7.1 Die Operatoren typeof und delete

Über den Operator typeof, den wir schon intensiv verwendet haben, haben Sie die Möglich-
keit, den Typ eines nachgestellten Operanden zu bestimmen. Sie werden entweder einen
der Standardtypen von JavaScript erhalten (kleingeschrieben) und auf diese Token auch in
Bedingungen vergleichen können oder aber Sie erhalten den Wert null oder undefined.
Mit delete haben Sie hingegen einen Operator zum Löschen von Objekten bzw.
Objekteigenschaften zur Verfügung. Dazu geben Sie diese als nachgestellte Operanden
an. Der Operator liefert im Erfolgsfall den Wert true und bei Misserfolg den Wert false.

 Beachten Sie, dass Sie mit delete keine vordefinierten Standardobjekte und
-objekteigenschaften von JavaScript löschen können. Ebenso können in den
meisten aktuellen Browsern nur Variablen und Objekte mit delete gelöscht
werden, die nicht mit dem Schlüsselwort var bzw. let deklariert wurden. Aus
dem Grund ist der Einsatz von delete zum Löschen von Variablen selten. Statt-
dessen setzt man besser eine Variable explizit auf null oder undefined, wenn
man diese nicht mehr benötigt und freigeben will.

Um delete in der Praxis zu sehen, benötigen wir explizit OOP oder zumindest Arrays.
Deshalb wird erst da ein Beispiel gezeigt.

4.7.2 Arithmetische Operatoren und der Verknüpfungsoperator

Arithmetische Operatoren dienen zur Berechnung eines Wertes. Dazu wird eine Opera-
tion auf einen oder mehrere Operanden angewendet (Tab. 4.5).
64 4 Elementare JavaScript-Grundstrukturen

Tab. 4.5  Arithmetische Operatoren


Name Operator Syntax Beispiel
Addition + Summand1 + Summand2 7+4
Subtraktion – Minuend – Subtrahend 7–4
Multiplikation * Faktor1 * Faktor2 7*4
Division / Dividend / Divisor 7/4
Modulo % Dividend % Divisor 7%4
Potenz ** Basis ** Exponent 2 ** 3
Negation – –Operand –(2 + 5)
Inkrement +  + +  + Variable; x = 10;
Variable +  + +  + x;
y = 135;
y +  +
Dekrement –– ––Variable; x = 10;
Variable–– ––x;
y = 135;
y––

Der Operator für die Potenz ist ziemlich neu und steht in alten Versionen von Java-
Script nicht zur Verfügung. Für die vier Grundrechenarten Addition, Subtraktion, Multi-
plikation und Division verwendet JavaScript die üblichen Zeichen. Bei den zugehörigen
Operatoren handelt es sich um binäre Operatoren, die eine Zahl als Ergebniswert liefern.
Der Modulo-Operator berechnet den Rest einer ganzzahligen Division. Bei der ganz-
zahligen Division der Zahl 23 mit der Zahl 5 bleibt ein Rest von 3 (23 = 4 * 5 + 3), d. h.,
die Zahl 5 ist viermal in der Zahl 23 enthalten. Der Rest von 3 ist nicht mehr ganzzahlig
durch 5 teilbar. Modulo ist in JavaScript auch auf Gleitkommazahlen anwendbar. Die
folgenden Anweisungen sind also gültig:

Beispiel

7.1 % 5
7.1 % 3.4 ◄

Sie sollten allerdings dennoch niemals Modulo mit Gleitkommazahlen ausführen, da es


zu Rundungsproblemen kommen kann, die an der internen Darstellung der Gleitkomma-
zahlen hängen und nicht zu verhindern sind ([Link]).

"use strict";
[Link](17 % 3);
[Link](7.1 % 3.4);
4.7 Operatoren 65

Die Ausgabe ist das:


2
0.2999999999999998
Bei der Modulo-Operation mit Gleitkommazahlen sollte 0.3 das Ergebnis sein, aber
die Rundungsprobleme sind deutlich. Das ist übrigens kein Problem, das auf JavaScript
beschränkt ist. Auch andere Programmiersprachen rechnen nach dem gleichen Verfahren
und haben diese Effekte.
Der Inkrement- sowie der Dekrementoperator sind unäre Operatoren, d. h., sie wer-
den nur auf einen Operanden angewendet. Das Inkrement +  + bewirkt, dass der Wert des
Operanden um 1 erhöht wird. Das Dekrement – bewirkt, dass der Wert um 1 reduziert
wird. Die Operatoren können dabei vor oder nach dem Operanden gesetzt werden. Bei
letzterer Variante besitzt der Operand während der Operation den bisherigen Wert und
wird erst danach erhöht, in komplexen Ausdrücken kann sich dies jedoch auf das zurück-
gegebene Ergebnis auswirken. Die Angabe vor dem Operanden wird als Präfix-, die nach
dem Operanden als Postfix-Notation bezeichnet. Im ersten Fall wird die Operation vor
jeder weiteren Berechnung ausgeführt, in der Postfix-Notation erst nach der Berechnung
des Ausdrucks. Bei komplexen Ausdrücken kann es sehr schwer nachvollziehbar sein,
wie die Erhöhung erfolgt.
In dem Beispiel ([Link]) werden Variablen mit denselben Werten unterschiedlich
inkrementiert und dekrementiert. Beachten Sie die daraus resultierenden unterschied-
lichen Ergebnisse.

"use strict";
var x = 5; var y = 1.5;
[Link]("x = " + x + "; y = " + y);
[Link]("x++ + y ergibt: " + (x++ + y));
x = 5; y = 1.5;
[Link]("x = " + x + "; y = " + y);
[Link]("x + ++y ergibt: " + (x + ++y));
x = 5; y = 1.5;
[Link]("x = " + x + "; y = " + y);
[Link]("x-- + y ergibt: " + (x-- + y));
x = 5; y = 1.5;
[Link]("x = " + x + "; y = " + y);
[Link]("x + --y ergibt: " + (x + --y));

• Die Variablen x und y werden mit den Werten 5 und 1.5 initialisiert.
• Zuerst werden die Werte von x und y addiert und ausgegeben, erst danach wird x
automatisch um den Wert 1 erhöht.
• Die Variable y wird schon vor der Addition um 1 erhöht und dann erst mit dem Wert
von x addiert.
66 4 Elementare JavaScript-Grundstrukturen

• Die Werte von x und y werden addiert und ausgegeben. Dann erst wird x um 1 ver-
ringert.
• Erst nachdem y um 1 verringert wurde, werden beide Variablen addiert.

Um deutlich zu machen, wie man diese Operatoren jedoch für ganz schlechten
Programmierstil missbrauchen kann, versuchen Sie das Ergebnis nachzuvollziehen (ink-
[Link]).

"use strict";
var x = 1;
[Link](x++ + ++x + ++x + x++ + x + ++x);

Das ist legitimer Code. Aber vollkommen unlesbar. Nutzen Sie In- und Dekrement nur
als einzelne Anweisungen. Alles andere ist aus meiner Sicht Sabotage.
Aber grundsätzlich sind arithmetische Operatoren eher einfach und problemlos. Nur
ein Token ist mehrdeutig – das Pluszeichen. Dieser Operator kann auch als Konkatena-
tionsoperator (engl.: concatenate = verknüpfen) oder Stringverkettungsoperator dienen
und verknüpft zwei Zeichenketten mit der zusammengesetzten Zeichenkette als Ergeb-
niswert.

[Link] Verbindung verschiedener Typen


JavaScript zeigt bei Operatoren ein polymorphes (vielgestaltiges) Verhalten. Das Plus-
zeichen kann ohne den Kontext nicht zwischen Berechnungsoperator für eine Addition
und dem Verknüpfungsoperator unterschieden werden. Es macht einen wichtigen Unter-
schied, ob Sie Operatoren wie + oder auch * oder – auf Zeichenketten oder andere Daten-
typen wie numerische Werte anwenden und manchmal auch, welche Inhalte in einer
Zeichenkette stehen. Bei ganzen Zahlen und Gleitkommazahlen entspricht das Zei-
chen + der mathematischen Addition. Werden zwei Zeichenketten mit dem Zeichen + ver-
knüpft, werden sie zu einer Zeichenkette zusammengefasst (Stringverkettung).

 In neuen Versionen von JavaScript gibt eine sogenannte Template-Literal-


Interpolation, womit man Stringverkettung vermeiden kann, wenn man mit
Variablen und Strings arbeitet. Vereinfach gesagt, wird für eine Variable ein
Platzhalter in einem String notiert und dann durch den Wert der Variable er-
setzt. Wenn etwas ${name} innerhalb des Strings "Hallo ${name}!" auftaucht,
wird in diesem speziellen Fall der Wert der Variablen name direkt in den String
eingefügt. Das macht den Code lesbarer und erleichtert die Arbeit mit Strings.
Template-Literals können auch mehrere Variablen oder sogar JavaScript-Aus-
drücke enthalten, die innerhalb der `${}`-Syntax eingefügt werden.

Elemente einer Zeichenkette können auf der anderen Seite zwar im Grunde nicht arith-
metisch verwertet werden. Aber was ist, wenn darin nur Zahlen stehen? Interessant wird
4.7 Operatoren 67

es ebenso, wenn verschiedene Datentypen an diesen Operationen zur Verknüpfung von


zwei Operanden beteiligt sind. Denn dann greifen automatische Typumwandlungen im
Hintergrund.

• '1' + 1 wird zu '11'. Die Zahl 1 wird vor der Verbindung zu einem String konvertiert
und dann ist das eine Stringverkettung. Immer (!) wenn ein String an einer Opera-
tion mit dem Plusoperator beteiligt ist, wird der andere Operand zu einem String kon-
vertiert und dann eine Stringverkettung vorgenommen.
• '1' * 1 wird zu 1. Der String mit der Zahl 1 als Inhalt wird vor der Verbindung zu
einem Number konvertiert und dann ist das eine einfache Multiplikation. Immer (!)
wenn eine Multiplikation, Division, Subtraktion, Modulo und De- sowie Inkrement
ausgeführt wird, wird eine Typkonvertierung der Operanden auf Number versucht,
wenn sie nicht sowieso vom Typ Number sind. Geht das schief, liefert die Berechnung
den Wert NaN (not a number = keine Zahl).
• Bei einem Ausdruck mit mehreren gleichwertigen Operationen wird von links nach
rechts ausgewertet und gegebenfalls mehrfach der Datentyp von Operanten ge-
wandelt. Jede einzelne Teiloperation wird unabhängig bewertet.

Das folgende Beispiel verdeutlich das (operationen_verschiedene_datentypen.js).

"use strict";
var zahl = 1, zeichen = '1';
var erg = zahl + zahl; // 2;
[Link](erg, typeof(erg));
erg = zeichen + zeichen; // '11';
[Link](erg, typeof(erg));
erg = zeichen - zeichen; // 0;
[Link](erg, typeof(erg));
erg = '1a' * zahl; // NaN
[Link](erg, typeof(erg));
erg = zahl + zahl + zeichen; // '21' - 1 + 1 ist 2, dann 2 + '1'
[Link](erg, typeof(erg));
erg = zeichen + zahl + zahl; // '111' - '1' + 1 ist '11', dann '11' +
1
[Link](erg, typeof(erg));
erg = "5" % 2; // ergibt 1 - '5' wird zu 5 gecastet
[Link](erg, typeof(erg));
erg = ++zeichen; // ergibt 2 - '1' wird vor der Erhöhung zu 1 ge-
castet
[Link](erg, typeof(erg));

Die Ausgabe ist das:


2 number
68 4 Elementare JavaScript-Grundstrukturen

11 string
0 number
NaN number
string
111 string
1 number
2 number

Tab. 4.6  Vergleichsoperatoren
Operator Bedeutung Beschreibung Beispiele Ergebnis
!= Ungleichheit Vergleich, ob zwei Operanden im Wert 8 ! = 4 + 4 false
nicht identisch sind. Der Vergleich lie-
fert true, wenn zwei Ausdrücke rein im
Wert nicht gleich sind
! =  = Typungleichheit Durch lose Typisierung von JavaScript 7 ! = 4 + 4 true
hat man als wenig Kontrolle darüber,
von welchem Datentyp eine Variable
ist. Dennoch kann es von Bedeutung
sein, einen bestimmten Datentyp zur
Verfügung zu haben. Neben der Gleich-
heit der reinen Werte zweier Operanden
muss auch der Datentyp im Vergleich
berücksichtigt werden. Mit diesem
Operator können Sie einen Vergleich
durchführen, ob zwei Operanden ent-
weder im Wert oder im Datentyp oder
beidem nicht übereinstimmen
< Kleiner-als Vergleich, ob der erste Operand kleiner "4" ! =  = 4 true
als der zweite Operand ist
<= Kleiner-als-oder- Vergleich, ob der erste Operand kleiner 4 ! =  = 2 + 1 true
gleich oder gleich dem zweiten Operanden ist
=  = Gleichheit Vergleich, ob zwei Operanden im Wert 5<6 true
identisch sind. Das doppelte Gleich-
heitszeichen darf nicht mit dem Zu-
weisungsoperator (=) verwechselt
werden!
=  =  = Identität Vergleich, ob zwei Operanden sowohl 6 <=6 true
im Wert als auch Datentyp überein-
stimmen
> Größer-als Vergleich, ob der erste Operand größer 4 =  = 5 false
als der zweite Operand ist
>= Größer-als-oder- Vergleich, ob der erste Operand größer "4" =  = 4 true
gleich oder gleich dem zweiten Operanden ist
4.7 Operatoren 69

4.7.3 Vergleichsoperatoren

Vergleichsoperatoren werden benutzt, um die Werte von zwei Operanden miteinander zu


vergleichen. Folgende Vergleichsoperatoren werden unterschieden (Tab. 4.6):

 Vorsicht Wenn Sie zwei Operanden auf strikte Gleichheit prüfen, müssen
diese den gleichen Wert und den gleichen Datentyp besitzen. Wird auf ein-
fache Gleichheit geprüft, können auch Typumwandlungen zu einer Aus-
wertung true führen. Im Falle von strikter Gleichheit ist dies nicht möglich. Das
wird auch Identität genannt.
[Link](2 =  = "2"); // liefert true, da nur Werte geprüft werden.
[Link] (2 =  =  = "2"); // liefert false, da unterschiedliche Typen vorliegen.

Werden Vergleichsoperatoren auf Zeichenketten angewendet, ist die Reihenfolge der Zei-
chen im String von Bedeutung. Es wird Zeichen für Zeichen geprüft. Also das erste Zei-
chen des einen Operanten mit dem ersten Zeichen des zweiten Operanten usw.

[Link] Wertigkeiten beim Vergleich


Bei den Vergleichen mit Zahlen ist klar, was kleiner oder größer ist. Bei Inhalten von
Zeichenketten gibt es Wertigkeiten der einzelnen enthaltenen Zeichen, die sich aufgrund
der Kodierung im Zeichensatz ergeben. Ziffern werden z. B. niedriger eingestuft als
Buchstaben und Großbuchstaben niedriger als Kleinbuchstaben. Wenn das klar ist, kann
man einfach die numerischen Vergleiche übernehmen.
Nachfolgend sind die einzelnen Zeichen von niedrig nach hoch nach der Wertigkeit
sortiert.

1. !"#$%&'()* + ,-./
2. 0123456789
3. :;< = >?@
4. ABCDEFGHIJKLMNOPQRSTUVWXYZ
5. [\]^_`
6. abcdefghijklmnopqrstuvwxyz
7. {|} ~

Betrachten wir ein Beispiel, das mit Kommentaren das Verhalten erläutert ([Link]).

"use strict";
[Link](2 == "2"); // liefert true, da nur Werte geprüft werden
[Link] (2 === "2"); // liefert false, da unterschiedliche Typen
[Link]("a" < "A"); // liefert false, da a höhere Wertigkeit als A
[Link] (1 < "a"); // liefert false, da 1 höhere Wertigkeit als a
70 4 Elementare JavaScript-Grundstrukturen

[Link]("Abc" > "abc"); // liefert false, da a höhere Wertigkeit


als A
[Link]("ABc" > "ABd"); // liefert false, da c vor d kommt

4.7.4 Logische Operatoren

Im Gegensatz zu den Vergleichsoperatoren werden mit den logischen Operatoren die


booleschen Wahrheitswerte true und false miteinander verknüpft. Der Ergebniswert
eines logischen Ausdrucks besteht wieder aus einem booleschen Wert (Tab. 4.7).

Beispiel (logische_op.js).

"use strict";
let a = 2, b = 3, c = 4, d = 17;
// liefert false, da bereits Vergleich 1 false ergibt
[Link]((d % b < 2) && (d - a * b > 100) && (c + d > a));
// liefert true, da Vergleich 3 true ergibt
[Link]((d % b < 2) || (d - a * b > 100) || (c + d > a));
// liefert ja, da Vergleich true ergibt
[Link]((a < b) ? "ja" : "nein");

Bei den Vergleichen wurde ein spezifisches Verhalten im Hintergrund genutzt. Ausdrücke
werden einzeln von links nach rechts ausgewertet. Jeder einzelne bewertete Ausdruck
liefert true oder false. Wenn sich an der Auswertung des Gesamtausdrucks durch die
Auswertung weiter rechts stehende Teilausdrücke nichts mehr ändern kann oder bereits
das Kriterium für die Gesamtbewertung erfüllt ist, wird die Auswertung abgebrochen.

 Diese Funktionalität wird als Short-Circuit-Auswertung oder Lazy Evaluation be-


zeichnet. Sie gewährleistet oft bessere Performance.

Beispiel (lazy_evaluation.js):

"use strict";
[Link]((4 > 3) || (5 > 6)); // true, da Vergleich 1 bereits true
[Link]((4 > 3) && (5 > 6)); // false, da Vergleich 1 bereits false

4.7.5 Bit-Operatoren

Bit-Operatoren werden auf Zahlen und boolesche Werte angewendet, die entsprechend ihrer
binären Darstellung verknüpft werden (die Bits können z. B. für bestimmte Eigenschaften
stehen). Die booleschen Werte true und false entsprechen dabei den Werten 1 und 0.
4.7 Operatoren 71

Tab. 4.7  Logische Operatoren


Operator Bedeutung Beschreibung Beispiele Ergebnis
&& Logisches Und Vergleich zweier Ausdrücke mit (4 + 4 =  = 8) && true
(And) dann und nur dann true, wenn (2 + 3 =  = 5) false
beide verglichenen Ausdrücke (4 + 4 =  = 7) &&
bzw. Operanden wahre Ergebnisse (2 + 3 =  = 5)
liefern. Sonst erhalten Sie false
|| Logisches Oder Vergleich zwei Ausdrücke und (4 + 4 =  = 8) true
(Or) bereits dann true, wenn einer der || (2 + 3 =  = 5) false
beiden verglichenen Ausdrücke (4 + 4 =  = 7) || true
ein wahres Ergebnis liefert. (2 + 3 =  = 5)
Ebenso liefert der Operator true, (4 + 4 =  = 7) ||
wenn beide wahr sind. Dies ist (2 + 3 =  = 6)
also kein ausschließendes Oder
(XOR). Nur wenn beide Operan-
den unwahr sind, liefert der Ope-
rator false
! Logisches Nicht Umdrehen eines logischen Werts !(4 + 4 =  = 8) false
(Not) (meist Ergebnis einer nach-
gestellten booleschen Operation)
um. Aus true wird false und aus
false wird true
?: Entweder-Oder- Der Operator ist eine sehr kom- (3 < 4) ? "ja": "ja"
Bedingung pakt zu notierende Abkürzung für "nein"
eine if-else-Entscheidung. Man
nennt diesen Operator den tria-
dischen oder ternären Operator,
denn er ist der einzige Operator
mit drei Operanden. Diese Ope-
ration benötigt einen booleschen
Ausdruck vor dem Fragezeichen.
Wenn diese true liefert, wird
der Wert vor dem Doppelpunkt
zurückgegeben, ansonsten der
Wert hinter dem Doppelpunkt

Bit-Operatoren haben die unterschiedlichsten Einsatzgebiete. Es gibt diverse interes-


sante Operationen auf Bit-Ebene. Verschlüsselung, Komprimierung oder Steigerung von
Performance wären Beispiele. Aber kann auch trivialer herangehen. Jedes Zeichen auf
dem Computer wird bekanntlich intern in Form des Binärsystems aus Nullen und Einsen
dargestellt, was dem Wert in einer numerischen Zeichenkodierung wie ASCII oder Uni-
code entspricht. Deren Darstellung kann man etwa manipulieren. JavaScript kennt wie
viele andere Programmiersprachen Operatoren, welche eine Manipulation von Werten
auf Bitebene erlauben.
72 4 Elementare JavaScript-Grundstrukturen

[Link] Die bitweisen Verschiebungsoperatoren


In Tab. 4.8 finden Sie die bitweisen Verschiebungsoperatoren von JavaScript.
Um die Wirkung deutlich zu machen, betrachten wir die Binärdarstellung eines Zei-
chens. Nehmen wir die Zahl 42 als Beispiel. Deren Binärdarstellung ist 00.101.010. Nun
bedeutet die Anwendung der Operation << 1, dass alle Bits um eine Stelle nach links
verschoben werden. Von rechts kommt eine 0 dazu. Das führt zu der Binärdarstellung
01.010.100 und das ist dezimal 84.
Das bedeutet nun, dass << 1 (eine Verschiebung der Bits um eine Stelle nach links)
nichts weiter als die Verdoppelung des Werts darstellt. Das ist auch aufgrund der Basis 2
des Binärsystems klar. Zwangsläufig führt eine Verschiebung um zwei Stellen nach links
(<< 2) zu einer Vervierfachung.
Wie geht es nun in die andere Richtung? Der Ausdruck >> 1 verschiebt die Bits um eine
Stelle nach rechts. Das bedeutet, dass bei der Binärdarstellung der Zahl 42 (00.101.010)
das am weitesten rechts stehende Bit verschwindet und links eine binäre 0 dazukommt.
Das führt in der Binärdarstellung zum Wert 00.010.101. Dezimal ist das die Zahl 21.
Offensichtlich steht der Ausdruck >> 1 für eine Halbierung des Werts. Wenn wir jetzt bei
der Binärdarstellung der Zahl 42 (00.101.010) zwei Stellen nach rechts schieben (>> 2),
wird geviertelt. Beachten Sie, dass hier aber eine Ganzzahldivision durchgeführt wird!
Nun gibt es aber zwei bitweiser Linksverschiebungsoperator (<< und <<<). Im Detail
gibt es da einen Unterschied. Der Operator << führt eine bitweise Linksverschiebung der
binären Darstellung des Operanden um eine bestimmte Anzahl von Stellen durch. Für
positive Zahlen führt << eine normale bitweise Linksverschiebung durch. Für negative
Zahlen führt << eine Linksverschiebung durch, die die Vorzeichenbits beeinflussen kann.
Beispiel:

Beispiel

let x = 5; // Binäre: [Link].[Link].000.000.101.


let y = x << 1; // Ergebnis: 10 (Binäre: [Link].[Link].000.001.0
10). ◄

Der bitweise Null-Füllung-Linksverschiebungsoperator (<<<) führt eine bitweise Links-


verschiebung der binären Darstellung des Operanden durch, füllt jedoch die dabei

Tab. 4.8  Die bitweisen Verschiebungsoperatoren


Operator Beschreibung
<< Operator für bitweise Verschiebung nach links
>> Operator für bitweise Verschiebung nach rechts
>>> Operator für bitweise Verschiebung nach rechts explizit mit binären Füllnullen von
links
4.7 Operatoren 73

entstandenen Leerstellen immer mit Nullen auf, unabhängig vom Vorzeichen des Ope-
rands. Dieser Operator wird hauptsächlich für die Behandlung von großen positiven
Ganzzahlen oder für die Erstellung von Hashing-Funktionen verwendet, bei denen das
Vorzeichen nicht berücksichtigt werden soll. Beispiel:

Beispiel

let x = -5; // Binär: [Link].[Link].111.111.011.


let y = x >>> 1; // Ergebnis: [Link] (Binäre: [Link].[Link]
[Link]). ◄

In der Regel wird der <<<-Operator nicht so häufig verwendet wie der <<-Operator, da
die Notwendigkeit, Vorzeichenbits zu ignorieren, relativ selten ist. Der <<<-Operator
ist jedoch nützlich, wenn eine bitweise Verschiebung ohne Berücksichtigung des Vor-
zeichens erforderlich ist.

[Link] Die bitweisen arithmetischen Operatoren


Betrachten wir nun die bitweisen arithmetischen Operatoren. Diese dienen zur Ver-
knüpfung von zwei Operanden auf Bitebene. Das bedeutet, dass das Bit im einen
Operanden mit dem Bit an der gleichen Stelle des zweiten Operanden verknüpft wird
(Tab. 4.9).
Um die Sache zu verstehen, schauen wir uns an, was auf Binärebene passiert, wenn
zwei Bits aufeinandertreffen. Das bitweise UND funktioniert wie folgt:

• 0 & 0=0
• 0 & 1=0
• 1 & 0=0
• 1 & 1=1

Das Ergebnis zweier mit UND verknüpfter binärer Zahlen führt also dazu, dass nur bei
den Bits, an denen in beiden Operanden übereinstimmend eine Eins steht, nach der Ope-
ration wieder eine Eins zu finden ist. Beispiel:

Tab. 4.9  Die bitweisen arithmetischen Operatoren


Operator Beschreibung
& Bitweiser AND-Operator
| Bitweiser OR-Operator
^ Bitweiser XOR-Operator – das ausschließende Oder (englisch: exclusive OR)
~ Bitweises NICHT – auch bitweiser Komplementoperator genannt
74 4 Elementare JavaScript-Grundstrukturen

Das bitweise ODER folgt nachstehendem Schema:

• 0 | 0=0
• 0 | 1=1
• 1 | 0=1
• 1 | 1=1

Hier ergibt sich nur dann 0, wenn die verglichenen Bits in den zwei Operanden beide
eine Null aufweisen. Beispiel:

Beispiel

Operation Dezimaldarstellung Binärdarstellung


10 0000 1010
& 12 0000 1100
Ergebnis 8 0000 1000

Betrachten wir an einem Beispiel eine konkrete binäre ODER-Verknüpfung:

Beispiel

Operation Dezimaldarstellung Binärdarstellung


10 0000 1010
| 12 0000 1100
Ergebnis 14 0000 1000

XOR hat das folgende Rechenschema:

• 0 ^ 0=0
• 0 ^ 1=1
• 1 ^ 0=1
• 1 ^ 1=0

Es wird eine Eins im Ergebnis gesetzt, wenn die Bits sich unterscheiden. Andernfalls ist
das Resultat Null. Betrachten wir an einem Beispiel eine konkrete binäre XOR-Verknüp-
fung.
4.7 Operatoren 75

Beispiel

Operation Dezimaldarstellung Binärdarstellung


10 0000 1010
^ 12 0000 1100
Ergebnis 6 0000 0110

Das bitweise NICHT (~) ist die Negation eines jeden Bits. Es wird in der Bitdarstellung
eines Operanden jede Null durch eine Eins und jede Eins durch eine Null ersetzt. Die
Wirkung dieses Vorgangs auf die Zahl besteht darin, dass ihr Vorzeichen umgedreht (aus
einer positiven Zahl wird also eine negative) und der Wert 1 subtrahiert wird. Dieser bit-
weise Komplementoperator unterscheidet sich deutlich von den anderen bitweisen Ope-
ratoren. Er legt eine einstellige bitweise Operation fest. Das bedeutet, er hat nur einen –
nachgestellten – Operanden, genau wie ein negatives Vorzeichen bei einer normalen
Zahl. Beispiel für die binäre Nichtoperation:

Beispiel

Operation Dezimaldarstellung Binärdarstellung


~ 10 0000 1010
Ergebnis −11 1111 0101

Schauen wir uns die bitweisen Operationen in einem gemeinsamen Beispiel an (bit-
weise_operationen.js).

"use strict";
var zahl = 42;
var a = zahl << 1;
var b = zahl >> 1;
var c = zahl << 2;
var d = zahl << 3;
var e = zahl >>> 3;
var f = zahl >>> 2;
[Link]("Der Wert von 42 << 1: " + a );
[Link]("Der Wert von 42 >> 1: " + b );
[Link]("Der Wert von 42 << 2: " + c );
[Link]("Der Wert von 42 << 3: " + d );
[Link]("Der Wert von 42 >>> 3: " + e );
76 4 Elementare JavaScript-Grundstrukturen

[Link]("Der Wert von 42 >>> 2: " + f );


var zahl1 = 10;
var zahl2 = 12;
var a = zahl1 & zahl2;
var b = zahl1 | zahl2;
var c = zahl1 ^ zahl2;
var d = ~zahl1;
[Link]("Der Wert von 10 & 12: " + a );
[Link]("Der Wert von 10 | 12: " + b );
[Link]("Der Wert von 10 ^ 12: " + c );
[Link]("Der Wert von ~ 10: " + d );

Zu dem Listing sind aufgrund der vorbereitenden Argumente keine weiteren Erklärungen
notwendig, aber Sie sollten die Ausgabe ansehen, die die obigen Ausführungen beweist.
Wenn man die bitweisen Verknüpfungsoperatoren auf boolesche Ausdrücke an-
wendet, erhält man im Grunde ein gleiches Verhalten wie bei „normalen“ Verknüpfungs-
operatoren. Zumindest bei den Operatoren für UND bzw. ODER. XOR und den Komple-
mentoperator gibt es ja nur als bitweise Operatoren. Dennoch lohnt es sich, das Ver-
halten genauer anzusehen. Denn es gibt interessante Situationen, wobei Sie – wie oben
erwähnt – zu beachten haben, dass bei diesen Operationen false für 0 und true für 1 steht.
Weiter gilt zu beachten, dass bei diesen Operationen keine Lazy Evaluation benutzt wird.
Das logische Schema von XOR liefert das:

false ^ true true


true ^ false true
false ^ false false
true ^ true false

Schauen wir uns ein vollständiges Beispiel an (bitweise_operationen_bool.js).

"use strict";
[Link]("XOR");
[Link](false ^ true);
[Link](false ^ false);
[Link](true ^ true);
[Link](false ^ false);
[Link]("Komplement");
[Link](~false, ~true);
[Link]("Keine Lazy Evaluation");
[Link](false & true);

Das führt zu diesen Resultaten:


XOR
1
4.7 Operatoren 77

0
0
0
Komplement
-1 -2
Keine Lazy Evaluation
0

4.7.6 Zuweisungsoperatoren

Es gibt neben dem direkten Zuweisungsoperatorin JavaScript noch die Zuweisungs-


operatoren, die mit einer Operation verknüpft sind. Diese sind hauptsächlich als Ab-
kürzung für arithmetische Operationen (dies bedeutet mathematische Berechnungen) mit
einer nachfolgenden Zuweisung zu verstehen und bauen auf dem Gleichheitsoperator mit
dem vorangestellten Token für die arithmetische Operation auf. Dazu gibt es noch den
Zuweisungsoperator mit gleichzeitiger String-Verkettung.

Beispiel

let x = 2;
x ** = 3; // Jetzt ist x gleich 8 (2^3).
let str = "Hello";
str +  = " World"; // Jetzt ist str gleich "Hello World". ◄

Hier sind noch ein paar Beispiele:

• Bei dem Modulo- und Zuweisungsoperator würde a % = 5 den Wert von a Modulo 5
nehmen. Die alternative Schreibweise wäre a = a % 5;.
• Beim Multiplikations- und Zuweisungsoperator multipliziert die Anweisung a * = 5
den Wert der Variablen a mit dem Wert 5. Die alternative Schreibweise wäre a = a * 5;.

Zuweisungsoperatoren gibt es auch für bitweise Operationen. Der bitweise XOR-Zuwei-


sungsoperator (^ = ) führt eine bitweise XOR-Operation zwischen dem aktuellen Wert
der Variablen und dem Wert auf der rechten Seite des Operators aus und weist das Ergeb-
nis der Variablen zu. Beispiel:

Beispiel

let x = 0b1010; // Binäre Darstellung von 10.


x ^ = 0b1100; // Führt eine XOR-Operation zwischen 10 und 12 aus.
// Jetzt ist x gleich 0b0110 (Binäre Darstellung von 6). ◄
78 4 Elementare JavaScript-Grundstrukturen

 Wenn Ihnen diese erweiterten Zuweisungsoperatoren suspekt erscheinen,


brauchen Sie diese in der Anfangsphase einfach nicht zu verwenden. Sie sind
nichts als Abkürzungen im Quelltext für mathematische Berechnungen bzw.
eine String-Verkettung oder Bitoperationen. Allerdings schätzen viele Java-
Script-Programmierer die Kurzschreibweise. Insbesondere die Erweiterung
eines Strings mit +  = zählt zu den elegantesten und kompaktesten Möglich-
keiten, einen String sukzessive aufzubauen.

4.7.7 Die Operatorenrangfolge

Die Operatoren in JavaScript haben eine festgelegte Rangordnung (Priorität), die immer
dann angewandt wird, wenn in einem Ausdruck mehrere Operatoren verwendet wer-
den und keine Klammern gesetzt werden (Klammern sind in der Rangfolge mit auf-
genommen und haben die höchste Priorität). Als Beispiel kennen Sie die Punkt-vor-
Strich-Regel. Die Prioritäten sind von der höchsten Wertigkeit bis zur niedrigsten ab-
wärts angegeben.
1. () [].
2. ! ~ - +  + –
3. * / %
4.  + -
5. << >> >>>
6. < < = > > =
7.  =  = ! =  =  =  = ! =  =
8. &
9. ^
10. |
11. &&
12. ||
13. ?:
14.   =  +  = - = << = >> = & = ^ = |=
15. ,
Kontrollflussanweisungen
5

Übersicht
Kontrollstrukturen sind klassische Anwendungen von logischen oder ver-
gleichenden Operatoren und spezielle Anweisungsschritte, mit denen man Ent-
scheidungen über den weiteren Ablauf eines Programms oder Skripts vorgeben
kann, wenn bestimmte Bedingungen eintreten. Kontrollstrukturen gehören in
einer Programmiersprache zu den einfachen syntaktischen Strukturen. In Java-
Script haben Sie die gleichen Kontrollstrukturen zur Verfügung, die es in fast allen
Programmiersprachen in gleicher oder ähnlicher Form gibt. Dementsprechend
wird man sich mit bereits elementaren Grundlagen in irgendeiner Programmier-
sprache damit vermutlich sehr leichttun. Es gibt neben der trivialen Sequenz (ein-
fache Abarbeitung der Reihe nach) und der noch trivialeren leeren Anweisung (in
JavaScript ein Semikolon) drei Arten von Kontrollflussanweisungen:
• Entscheidungsanweisungen. Diese suchen aufgrund einer Bedingung einen
Programmfluss heraus.
• Schleifen bzw. Iterationsanweisungen. Diese wiederholen eine bestimmte An-
zahl an Anweisungen.
• Sprunganweisungen. Diese verlassen eine Struktur.

5.1 Entscheidungsanweisungen

Betrachten wir zuerst die Entscheidungsanweisungen.

 Die auszuführenden Anweisungen bei if-Entscheidungsanweisungen no-


tiert man in der Regel als Block. Gibt es nur eine Anweisung, kann die Block-
notation entfallen. Das gilt auch für den else-Zweig. Aber das sollte man nur
sparsam ausnutzen.

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 79


Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
80 5 Kontrollflussanweisungen

5.1.1 Die if-Entscheidungsanweisungen

Über die if-Kontrollstruktur entscheiden Sie, ob eine nachfolgende Anweisung oder ein
Block auszuführen ist. Allgemeine Syntax ist folgende:

Beispiel

if (Bedingung) { …Anweisungen A} else {..alternative Anweisungen} ◄

Wenn eine Bedingung wahr ist, werden die Anweisungen A (entweder eine einzelne
Anweisung oder eine Blockanweisung) ausgeführt, ansonsten die alternativen An-
weisungen, wobei die else-Anweisung optional ist. Unter Bedingung versteht man einen
Vergleich, der entweder wahr (true) oder falsch (false) sein kann oder den booleschen
Wert selbst.

[Link] Benutzereingaben bei [Link]


Da Kontrollstrukturen an sich ziemlich trivial sind, nutzen wir diese Stelle und füh-
ren eine Technik ein, mit der man bei [Link] Benutzereingaben von der Konsole ent-
gegennehmen kann. Also das, was man in einem Browser mit prompt machen kann. Das
Thema an sich gehört eigentlich nicht hierher und greift gleich in mehrere Bereiche vor,
aber damit können die nachfolgenden Beispiele ein bisschen interessanter gestaltet wer-
den. Zudem lernen Sie dabei bereits ein mehr Details zu [Link], aber ebenso Modulen
und auch JavaScript. Hier ist ein einfaches Beispiel für ein JavaScript-Programm, das
Benutzereingaben über die Befehlszeile in [Link] entgegennimmt ([Link]):

"use strict";
// Einbinden des 'readline' Moduls von [Link]
const readline = require('readline');
// Konfigurieren des readline interfaces
const rl = [Link]({
  input: [Link], output: [Link]
});
// Frage stellen und auf Benutzereingabe warten
[Link]('Wie ist dein Name? ', (name) => {
  // Benutzereingabe verarbeiten
  [Link](`Hallo ${name}!`);
  // readline interface schließen
  [Link]();
});

Das Programm wird eine Frage nach dem Namen des Benutzers stellen und nach Ein-
gabe des Namens eine Begrüßung ausgeben. Das Konzept, das hier verwendet wird,
nennt sich „Benutzereingabe über die Befehlszeile“ oder „Befehlszeileninteraktion“. Es
5.1 Entscheidungsanweisungen 81

ermöglicht einem [Link]-Programm, mit dem Benutzer über die Befehlszeile zu inter-
agieren, indem es auf Eingaben wartet und dann entsprechend reagiert. Hier ist eine de-
tailliertere Erklärung des Codes, die – wie erwähnt – einige neue Techniken verwendet,
die später noch vertieft werden und nur Basis für etwas interessantere Beispiele mit
Kontrollstrukturen bieten soll:

1. Um eine Befehlszeileninteraktion bei [Link] vornehmen zu können, benötigt man


ein zusätzliches Modul. Hier sehen Sie zum ersten Mal, wie man mit Modulen arbei-
tet. Zuerst importieren wir das readline-Modul von [Link] mit der require-Anwei-
sung. Dieses Modul stellt eine Schnittstelle zum Lesen von Daten aus einem Readable
Stream (wie z. B. der Befehlszeile) bereit.
2. Dann erstellen wir eine Instanz von [Link], die für die Kommunikation mit
der Befehlszeile verwendet wird. Wir konfigurieren die Schnittstelle mit [Link]
für die Eingabe und [Link] für die Ausgabe. Beides interagiert mit der Kon-
sole.
3. Wir verwenden dann die question-Methode des [Link]-Objekts, um eine
Frage an den Benutzer zu stellen. Die Frage wird als erstes Argument der question-
Methode übergeben. Die Methode akzeptiert auch eine Callback-Funktion als zweites
Argument, die aufgerufen wird, wenn der Benutzer eine Antwort eingibt.
4. Innerhalb der Callback-Funktion, die aufgerufen wird, wenn der Benutzer eine Ant-
wort eingibt, wird die eingegebene Antwort (der Name des Benutzers) verwendet, um
eine Begrüßungsnachricht zu generieren, die dann auf der Befehlszeile ausgegeben
wird. Sie sehen hier das erste Mal eine sogenannte Lambda-Notation.
5. Schließlich wird die close-Methode des [Link]-Objekts aufgerufen, um
das Interface zu schließen und das Programm zu beenden.

Hintergrundinformation
Die Ausgabe in einer Konsole über [Link] ist in der Grundversion sehr einfach. Die Ver-
wendung von dem readline-Module für die Benutzereingabe über die Befehlszeile bietet jedoch
mehr Kontrolle und Funktionalität im Vergleich zur einfachen Verwendung von [Link]. Hier
sind einige Gründe, warum das Modul verwendet wird:

• Interaktive Eingabe: Mit der Verwendung des Moduls kann das Programm auf Benutzerein-
gaben warten und dann entsprechend reagieren. Dies ermöglicht eine interaktive Erfahrung, bei
der das Programm auf Eingaben des Benutzers reagiert, anstatt einfach statische Ausgaben zu
generieren.
• Mehrere Eingaben: Mit dem Modul können mehrere Eingaben vom Benutzer in einer einzigen
Ausführung des Programms verarbeitet werden. Das bedeutet, dass das Programm nicht nur
einmalig etwas ausgeben kann, sondern auch auf mehrere Eingaben des Benutzers reagieren
kann.
• Benutzerfreundlichkeit: Durch die Verwendung von dem Modul können Programme Benutzern
Fragen stellen und auf ihre Antworten warten. Dies macht Programme benutzerfreundlicher und
82 5 Kontrollflussanweisungen

ermöglicht es Entwicklern, interaktive Anwendungen zu erstellen, die auf die Bedürfnisse ihrer
Benutzer eingehen können.
• Validierung von Eingaben: Das Modul bietet Mechanismen zur Validierung von Benutzerein-
gaben. Bevor eine Eingabe verarbeitet wird, kann das Programm überprüfen, ob sie den er-
warteten Anforderungen entspricht.

Kommen wir nun zum eigentlichen Thema zurück und testen in einem Beispiel die if-
Anweisung (if_anweisung.js), wobei wir am Beginn die Eingabe über [Link] wie oben
beschrieben ermöglichen (das wird auch in der Folge aus Platzgründen nicht mehr ab-
gedruckt).


[Link]("Geben Sie eine Zahl zwischen 0 und 10 ein: ", (erg) => {
  [Link]("Sie haben " + erg + " eingeben!");
  if (erg > 7) {
    [Link]("Der Wert ist zu groß");
  } else {
    [Link]("Alles klar");
  }
  [Link]();
});

Die Benutzereingabe über die Konsole erfolgt wie oben beschrieben. Je nach ein-
gegebenem Wert wird dann der eine oder der andere Programmfluss ausgewählt.

 Eine Kurzform dieser Entscheidungsstruktur haben Sie mit dem Operator ?:


schon kennengelernt.

Sie können auch im else-Zweig direkt eine if-Anweisung notieren und damit mehrere
Bedingungen prüfen sowie aus mehreren Programmflüssen auswählen. Etwa so (zur
Demonstration dieses Mal ohne Blockanweisungen):

Beispiel

let zahl = 10;


if (zahl > 10) [Link]("Die Zahl ist größer als 10");
else if (zahl =  =  = 10) [Link]("Die Zahl ist genau 10");
else [Link]("Die Zahl ist kleiner als 10"); ◄

In diesem Beispiel wird der Wert der Variablen zahl überprüft. Je nachdem, ob die Be-
dingungen erfüllt sind oder nicht, wird eine entsprechende Ausgabe generiert:

• Wenn zahl größer als 10 ist, wird „Die Zahl ist größer als 10“ ausgegeben.
• Wenn zahl genau 10 ist, wird „Die Zahl ist genau 10“ ausgegeben.
5.1 Entscheidungsanweisungen 83

• Wenn zahl kleiner als 10 ist (und nicht gleich 10), wird „Die Zahl ist kleiner als 10“
ausgegeben.

 Man kann if-Anweisungen auch verschachteln.

5.1.2 Die switch-case-Fallunterscheidung

In JavaScript gibt es auch die Fallunterscheidung über switch-case. Ein Vorteil gegen-
über if-else ist, dass Sie auf einfache und übersichtliche Weise mehrere Fälle unter-
scheiden können. Das ist zwar auch mit if-else möglich, indem Sie – wie gesehen – meh-
rere if-Abfragen hintereinanderschreiben und ggf. mit vorangestelltem else verknüpfen
oder verschachteln. Jedoch erscheint die Fallunterscheidung mit switch-case vielen
Programmierern als eleganter. Die Syntax einer switch-case-Anweisung sieht formal
folgendermaßen aus:

Beispiel

switch([auswahl]){
  case [Fall 1]: break;
  case [Fall 2]: break;

  default:
}

Mit dem Schlüsselwort switch leiten Sie die Fallunterscheidung ein. Als Argument wird
in runden Klammern eingeschlossen eine Testvariable oder ein Ausdruck angegeben,
für dessen aktuellen Wert Sie die Fallunterscheidung durchführen. Dies ist der Testwert
und es kann ein beliebiger Datentyp sein, der unter JavaScript erlaubt ist (beispiels-
weise eine Zahl, jedoch auch ein Text oder Objekt). Die einzelnen Fälle, zwischen denen
unterschieden werden soll, werden untereinander aufgelistet und innerhalb geschweifter
Klammern als Blöcke notiert. Jeden einzelnen Fall leiten Sie mit dem Schlüsselwort case
ein, gefolgt von der konkreten Angabe des Werts, auf den Sie prüfen wollen.

 Die Anweisungen bei einem case-Block können Sie zwar in geschweifte Klam-
mern notieren, in der Praxis verzichtet man jedoch in der Regel darauf.

Die Anweisung break, die am Ende jedes Blocks notiert ist, ist eine – in JavaScript op-
tionale – Sprunganweisung. Damit stellen Sie sicher, dass nach einem Treffer die nach-
folgend notierten Fälle nicht ebenso ausgeführt werden. Bei dem switch-case-Konstrukt
handelt es sich um eine Fall-Through-Anweisung. Fehlt eine Sprunganweisung am Ende
jedes Blocks, werden ab einem Treffer alle folgenden Anweisungen ausgeführt, was in
84 5 Kontrollflussanweisungen

der Praxis oft nicht gewünscht ist. Beim letzten Block in der switch-case-Anweisung ist
das break immer überflüssig.

In der Tat wird die switch-case-Anweisung fast immer in Verbindung mit break erläutert und auch
so eingesetzt. Ich persönlich bin aber der Meinung, dass ein Hauptvorteil dieses Konstrukts gerade
der „durchfallende“ Charakter ohne break ist. Dieses Verhalten kann man mit der if-else-Struktur
nur mit Mühe erreichen. Beachten Sie die nachfolgenden Beispiele, in denen wir die switch-case-
Anweisung sowohl mit als auch ohne break einsetzen.

Der Fall default ist dafür da, eine Standardreaktion für den Fall zu definieren, dass kei-
ner der von Ihnen definierten Fälle zutrifft. Dieser Zweig ist optional und kann entfallen,
wenn es keinen Vorgabefall geben soll. Er entspricht dem else-Teil bei einer if-else-
Konstruktion.

 Der gravierende Nachteil der Programmflusssteuerung über switch und case


ist, dass nur diskrete Fälle unterschieden werden können. Also ist nur der Ver-
gleich auf exakte Gleichheit mit einem Wert möglich. Es gibt keinen Vergleich
auf größer oder kleiner, wie es bei einer if-else-Konstruktion möglich ist.

Zu der switch-case-Anweisung sollten ein paar Bemerkungen über Besonderheiten nicht


fehlen:

• Die Überprüfung des Testwerts erfolgt auf Identität. Das bedeutet, dass sowohl der
Wert als auch der Datentyp übereinstimmen müssen. Das ist deshalb bemerkenswert,
weil in JavaScript in fast allen anderen Fällen bei einem Vergleich nur die Wertgleich-
heit überprüft und bei unterschiedlichen Datentypen im Hintergrund automatisch ge-
castet wird. Das ist hier explizit nicht der Fall.
• Wenn Sie bei mehreren hintereinander angeordneten Fällen alle Anweisungen und
insbesondere das break weglassen, können Sie eine Art diskreten Wertebereich für
Treffer angeben. Ganz ohne Anweisungen werden für alle diskreten Treffer die glei-
chen Anweisungen ausgeführt, andernfalls immer ein Teil (ab dem Treffer).
• Die Testfälle müssen nicht in irgendeiner Reihenfolge notiert werden, obwohl man in
der Praxis meist eine chronologische Reihenfolge notieren wird, sofern dies sinnvoll
ist (vor allen Dingen bei Zahlen – bei Texten oder Objekten ist das natürlich nur be-
grenzt zweckmäßig).

Schauen wir ein praktisches Beispiel an (switch_anweisung1.js):


[Link]("Geben Sie eine Zahl zwischen 0 und 10 ein: ", (erg) => {
    [Link]("Sie haben " + erg + " eingeben!");
    switch(erg * 1) {
        case 1: [Link]("Das führt zur Auswahl 1."); break;
5.1 Entscheidungsanweisungen 85

        case 2: [Link]("Das führt zur Auswahl 2."); break;


        case 3: [Link]("Das führt zur Auswahl 3."); break;
        case 4: [Link]("Das führt zur Auswahl 4."); break;
      default: [Link]("Der Vorgabefall wird genommen.");
    }
    [Link]("Nach der switch-case-Anweisung.");
  [Link]();
});

In dem Beispiel wird wie in dem vorangegangenen Beispiel mit vom Anwender eine
Zahl entgegengenommen. Beachten Sie hier aber den kleinen Trick der Multiplikation
mit dem Wert 1 – (erg *1). Der Rückgabewert der beim Einlesen eine Benutzereingabe
ist immer ein String, im nachfolgenden Test wollen wir aber mit numerischen Werten
vergleichen. Die Multiplikation eines Strings, der nur numerische Werte enthält, mit der
Zahl 1 verändert den Wert nicht, konvertiert aber den Datentyp des Ergebnisses der Ope-
ration in einen numerischen Typ und damit haben wir gegebenenfalls auch die Identität
des Datentyps. Mit der nächsten Zeile wird der Wert der eingegebenen Zahl dann wieder
in die Webseite geschrieben ([Link]("Sie haben" + erg + "eingeben!");). Dann wird
der Wert der Variablen erg getestet (switch(erg * 1)). Je nach Eingabe des Anwenders
wird ein Fall ausgewählt und die Anweisung break unterbricht die weitere Abarbeitung
dieser Fallunterscheidung und springt hinter den switch-Block. Die nachfolgende Va-
riante des Beispiels verzichtet nun auf das break. Zudem wollen wir nun bei dem switch-
case auf Zeichenketten überprüfen (switch_anweisung2.js):


[Link]("Geben Sie eine Zahl zwischen 0 und 10 ein: ", (erg) => {
    [Link]("Sie haben " + erg + " eingeben!");
    switch(erg) {
       case "1": [Link]("Das führt zur Auswahl 1.");
        case "2": [Link]("Das führt zur Auswahl 2.");
        case "3": [Link]("Das führt zur Auswahl 3.");
        case "4": [Link]("Das führt zur Auswahl 4.");
        default: [Link]("Der Vorgabefall wird genommen.");
      }
        [Link]("Nach der switch-case-Anweisung.");
  [Link]();
});

Die entscheidenden Veränderungen in dem Beispiel sind wie gesagt der Verzicht auf
break und der Verzicht auf den kleinen Trick der Multiplikation mit dem Wert 1. Damit
steht in der Variable erg ein Datentyp String. Die nachfolgend notierten Trefferfälle von
case sind deshalb auch als Strings notiert. Wenn Sie nun als Besucher der Webseite einen
Wert eingeben, werden alle Ausgaben ab dem ersten Treffer zu sehen sein. Geben Sie
86 5 Kontrollflussanweisungen

etwa den Wert 3 ein, erhalten Sie nach der Kontrollausgabe noch drei Zeilen als weitere
Ausgaben.

 Den Verzicht auf break kann man sinnvoll nutzen. Denke Sie z. B. an ein
Reservierungssystem, mit dem Sie ab einem bestimmten Tag bis zum Ende
der Woche reservieren wollen.

5.2 Iterationsanweisungen

Kommen wir nun zu den Schleifen bzw. Iterationsanweisungen in JavaScript. Diese


wiederholen Anweisungen so lange, bis ein Abbruchkriterium erfüllt ist. Es gibt an-
nehmende bzw. fußgesteuerte und abweisende bzw. kopfgesteuerte Schleifen.

 Die auszuführenden Anweisungen bei Schleifen notiert man in der Regel als
Block. Gibt es nur eine Anweisung, kann die Blocknotation entfallen. Aber das
sollte man nur sparsam machen.

5.2.1 Die while-Schleife

Die while-Schleife ist abweisend und führt am Anfang eines Blocks eine Prüfung einer
Bedingung durch. Die nachfolgenden Anweisungen werden so lange durchlaufen, bis die
Bedingung nicht mehr erfüllt ist. Die Syntax sieht schematisch so aus:

Beispiel

while ([Bedingung]) { …Anweisungen} ◄

Wenn eine Bedingung beim Erreichen der Schleife nicht mehr erfüllt ist, werden die An-
weisungen im Block überhaupt nicht durchgeführt. Normalerweise kontrolliert man die
Anzahl der Wiederholungen mit einer Zählvariable, die vor dem Beginn der Schleife de-
klariert und normalerweise in der Schleife erhöht wird. Oder man bricht den Schleifen-
lauf gezielt ab, was mit break möglich ist. Nach dem Sprung aus der Schleife mit break
geht es im Programmfluss hinter der Schleife weiter. Das ist insbesondere in Verbindung
mit dem Prüfen einer Bedingung über if oft sinnvoll.

 Die while-Schleife benötigt (in der Regel) in ihrem Inneren eine Möglichkeit,
mit der die Schleife abgebrochen oder die Voraussetzungen für einen neuen
Durchlauf verändert werden kann (etwa durch Änderung der Zählvariablen).
Andernfalls erzeugen Sie eine Endlosschleife. Eine Endlosschleife ist eine
nicht endende Abarbeitung der Anweisungen im Inneren. Das kann zwar
sinnvoll sein (bei einer Animation zum Beispiel), ist aber normalerweise nicht
5.2 Iterationsanweisungen 87

gewünscht. Beachten Sie, dass verschiedene Browser Endlosschleifen identi-


fizieren können und einem Anwender die Möglichkeit bereitstellen, diese zu
beenden. Aber weder ist das zuverlässig in allen Browsern gewährleistet noch
muss eine Endlosschleife zwingend eine Fehlersituation bedeuten (sinnvoll
kann etwa eine permanente Animation sein – obgleich man das meist anders
macht). Allgemein sind jedoch Endlosschleifen selten sinnvoll. Diese Aussage
hinsichtlich Endlosschleifen gilt auch für die anderen Schleifenformen.

Der Abbruch einer while-Schleife erfolgt oft darüber, dass eine in der Bedingung ge-
prüfte Variable im Inneren so verändert wird, dass irgendwann die Bedingung nicht mehr
erfüllt ist. Sie sehen es in der nachfolgenden Übung (while_anweisung.js):

"use strict";
var i = 0;
while (i < 10) {
  i++;
  [Link](i + "\t:\t ", i * i);
}

Das Beispiel erzeugt eine Ausgabe mit zehn Zeilen. Zuerst ist der Wert der Zählvariablen
i für unsere Schleife 0. Die Bedingung in der while-Überprüfung ist also wahr und die
Anweisungen werden ausgeführt. Nach der Erhöhung des Werts werden in einer Aus-
gabe mit zwei Spalten der Wert des Zählers und sein Quadrat ausgegeben. Wir haben den
Inkrementoperator verwendet, der dem Ausdruck i = i + 1; entspricht. Das Durchlaufen
der Schleife wird so lange fortgesetzt, bis die Variable den Wert 10 erreicht. Dann ist
die Bedingung in der while-Überprüfung falsch und die Anweisungen werden nicht mehr
ausgeführt.

5.2.2 Die do-while-Schleife

Die do-while-Schleife ist eine Variante der normalen while-Schleife, bei der die Prüfung
erst nach dem ersten Durchlauf der Schleifenanweisungen erfolgt. Dese Schleife ist an-
nehmend oder fußgesteuert. Bei do-while werden die im Inneren notierten Anweisungen
auf jeden Fall einmal ausgeführt. Die schematische Syntax sieht so aus:

Beispiel

do { irgendwelche Anweisungen} while([Bedingung]); ◄

Da die Anwendung ansonsten grundsätzlich der while-Schleife entspricht, wollen wir auf
ein praktisches Beispiel verzichten.
88 5 Kontrollflussanweisungen

5.2.3 Die for-Schleife

Eine weitere Schleifenform wird über das Schlüsselwort for eingeleitet. Die allgemeine
Syntax sieht schematisch so aus:

Beispiel

for ([Zählvariable mit Startwert]; [Bedingung]; [Änderung Zählvariable]) { …An-


weisungen}. ◄

Die for-Schleife ist die kompakteste Form der Schleifen, denn sie enthält im Schleifen-
kopf alle relevanten Daten für die Abarbeitung der Schleife. Die erste Angabe in der
Klammer nach dem Schlüsselwort for gibt in der Regel die Zählvariable an und initia-
lisiert sie mit einem Startwert. Danach folgt – durch ein Semikolon getrennt – die Ab-
bruchbedingung und als dritte Angabe – wieder durch ein Semikolon getrennt – meist
die Wertänderung der Zählvariablen.

 Alle Angaben innerhalb der runden Klammern im Schleifenkopf sind optional.

Für viele Programmierer ist die for-Schleife die sicherste Form einer Schleife, da direkt
am Beginn der Schleife in einer Zeile alle relevanten Informationen für den Schleifen-
durchlauf stehen. Zwar kann man auch in einer for-Schleife komplexere Strukturen auf-
bauen (beispielsweise im Inneren die Zählvariable ändern oder eine interne Abbruch-
bedingung über break schaffen), aber in der Grundform sieht man hier auf einen Blick
alle relevanten Dinge und die Fehlerwahrscheinlichkeit ist relativ gering.

 Die for-Schleife nennt man oft auch zählergesteuert. Durch den Aufbau des
Schleifenkopfs eignet sich diese Schleife zur Verwendung von schleifen-
lokalen Variablen. Eine Zählvariable, die im Schleifenkopf deklariert wird,
ist nur im Schleifenblock gültig. Beachten Sie, dass eine for-Schleife nicht
zwangsläufig zählergesteuert ist und die anderen Schleifen auch zähler-
gesteuert sein können. Eine Syntax wie for(;;) für den Schleifenkopf ist voll-
kommen legitim. Aber es gibt eine „übliche“ Art der Verwendung und in der
nutzt man die for-Schleife zählergesteuert und wählt auch umgekehrt für eine
Anwendung, die zählergesteuert sein soll, oftmals die for-Schleife.

Nehmen wir uns als Übung das Beispiel, welches wir bei while schon hatten, und modi-
fizieren es (for_anweisung.js). Diese Version von dem Programm fragt den Benutzer
nach einer Zahl zwischen 1 und 10. Wenn die eingegebene Zahl im erlaubten Bereich
liegt, werden die Quadrate der Zahlen von 0 bis zur eingegebenen Zahl (ausschließlich)
ausgegeben. Andernfalls wird eine Meldung ausgegeben, die darauf hinweist, dass die
Eingabe außerhalb des erlaubten Bereichs liegt:
5.2 Iterationsanweisungen 89


[Link]("Geben Sie eine Zahl zwischen 1 und 10 ein", (erg) => {
      if (erg > 0 && erg < 11) {
      for (var i = 0; i < erg; i++) {
          [Link](`${i}\t:\t${i * i}`);
        }
      }
      else {
          [Link]("Die Eingabe liegt nicht im erlaubten Wertebereich");
      }
  [Link]();
});

Das Beispiel erzeugt vollkommen analog dem Beispiel mit der while-Schleife eine
Ausgabe mit einer gewissen Anzahl an Zeilen. Allerdings sind alle Informationen zum
Durchlauf der Schleife im Schleifenkopf notiert, sowohl der Wert der Variablen für
unsere Schleife, die Bedingung als auch die Werterhöhung über den Inkrement-Operator.
Aber auch ansonsten gibt es ein paar Modifikationen, damit das Beispiel nicht nur eine
reine Wiederholung der ersten Variante ist:

• Es wird die Anzahl der Zeilen über eine Benutzereingabe festgelegt.


• Mit if (erg > 0 && erg < 11) { … } wird überprüft, ob die Benutzereingabe (erg)
zwischen 1 und 10 liegt. Wenn die Bedingung erfüllt ist, wird der Code im darauf-
folgenden Block ausgeführt, wo wieder innerhalb der Schleife für jede Zahl von 0
bis zur Benutzereingabe (ausschließlich) das Quadrat dieser Zahl berechnet und aus-
gegeben wird.
• Bei der Ausgabe kommt Template-Literal-Interpolation zum Einsatz, wobei hier sogar
ein JavaScript-Ausdruck genutzt wird, um mit ${i * i} innerhalb des Strings das Qua-
drat des aktuellen Werts der Schleifenzählvariable i zu berechnen.
• Falls die Bedingung in der if-Anweisung nicht erfüllt ist (die Benutzereingabe liegt
außerhalb des Bereichs von 1 bis 10), wird der else-Zweig ausgewählt und eine pas-
sende Meldung ausgegeben.

5.2.4 Verschachtelte Schleifen

Schleifen lassen sich auch verschachteln. Das bedeutet, dass Sie innerhalb einer Schleife
eine weitere Schleife ausführen. Dadurch können Sie komplexe Muster durchlaufen oder
Iterationen über mehrdimensionale Datenstrukturen durchführen. Die Arten der Schlei-
fen können unterschiedlich, aber auch gleich sein. Gegebenenfalls benötigt man dann
einen anderen Zählindex für innere Schleifen. Hier ist ein einfaches Beispiel für ver-
schachtelte Schleifen in JavaScript:
90 5 Kontrollflussanweisungen

// Eine verschachtelte Schleife, um ein 2D-Raster auszugeben


for (let i = 0; i < 3; i++) {
  for (let j = 0; j < 3; j++) {
    [Link](`i: ${i}, j: ${j}`);
  }
}

In diesem Beispiel (verschachtelte_schleifen.js) haben wir eine äußere Schleife, die von
0 bis 2 läuft, und eine innere Schleife, die ebenfalls von 0 bis 2 läuft. Während die äu-
ßere Schleife ihren Durchlauf macht, durchläuft die innere Schleife ihren gesamten
Durchlauf für jeden Durchlauf der äußeren Schleife. Das Ergebnis ist, dass jedes mög-
liche Paar von Werten für i und j ausgegeben wird, was einem 3 × 3-Raster entspricht.
Wenn Sie diesen Code ausführen, erhalten Sie die Ausgabe:
i: 0, j: 0
i: 0, j: 1
i: 0, j: 2
i: 1, j: 0

i: 2, j: 2

Wie Sie sehen können, wurden alle Kombinationen von i und j durchlaufen, wobei i von
0 bis 2 und j für jeden Wert von i von 0 bis 2 läuft.

Schleifen sind insbesondere im Zusammenhang mit Arrays von Bedeutung. Wir werden bei deren
Behandlung darauf zurückkommen und insbesondere Spezialvarianten der for-Schleife (for-in, for-
of und forEach) nachtragen – diese macht auch nur bei Arrays und Objekten wirklich Sinn.

5.3 Sprunganweisungen

JavaScript stellt eine gewisse Anzahl an Sprunganweisungen zur Verfügung. Diese ver-
lassen eine umgebende syntaktische Struktur und führen dazu, dass mit der direkten An-
weisung hinter der syntaktischen Struktur weitergemacht wird. Der Begriff einer „syn-
taktischen Struktur“ ist recht abstrakt, aber eine solche syntaktische Struktur kennen
Sie – Schleifen. Allgemein wird eine Schleife beendet, wenn die überprüfte Bedingung
nicht mehr erfüllt ist. Schleifen sind aber recht kritische Stellen in jedem Programm oder
Skript. Es kann leicht zu Endlosschleifen oder ungewollten Durchläufen kommen. Oder
Sie brauchen mehrere Bedingungen, bei denen eine Schleife oder ein Schleifendurchlauf
zu beenden ist.
5.3 Sprunganweisungen 91

5.3.1 Abbruch mit break

Das eine Schlüsselwort für eine Sprunganweisung im Zusammenhang mit Schleifen


und switch-case kennen Sie bereits – break. Die Sprunganweisung break verlässt eine
Syntaxstruktur sofort, wenn diese Stelle im Quelltext erreicht wird. Hier ist ein Beispiel,
das eine Endlosschleife while verwendet und durch break verlassen wird (break_an-
[Link]):

"use strict";
let counter = 0;
while (true) {
  [Link]("Die Schleife läuft noch!");
  counter++;
  // Überprüfung, ob der Zähler 5 erreicht hat
  if (counter === 5) {
    [Link]("Der Zähler hat 5 erreicht. Die Schleife wird beendet.");
    break; // Verlasse die Schleife
  }
}

In diesem Beispiel wird eine Endlosschleife mit while (true) erstellt, die nur durch das
break-Statement verlassen wird. Innerhalb der Schleife wird auf der Konsole eine Nach-
richt ausgegeben und der Zähler erhöht. Wenn der Zähler den Wert 5 erreicht, wird eine
Abschlussnachricht ausgegeben und die Schleife mit break beendet. Wenn Sie dieses
Beispiel ausführen, wird die Ausgabe wie folgt sein:

Die Schleife läuft noch!


Die Schleife läuft noch!
Die Schleife läuft noch!
Die Schleife läuft noch!
Die Schleife läuft noch!
Der Zähler hat 5 erreicht. Die Schleife wird beendet.

Wie Sie sehen können, wird die Schleife nach Erreichen des Zählerwerts 5 verlassen.

5.3.2 Fortsetzen mit continue

Neben break gibt es im Zusammenhang mit Schleifen noch die Sprunganweisung con-
tinue. Sie können damit an einer bestimmten Stelle innerhalb des Schleifenblocks un-
mittelbar den nächsten Schleifendurchlauf erzwingen und die nachfolgenden An-
weisungen innerhalb des Schleifenblocks ignorieren. Die Verwendung von continue
findet man in der Praxis nicht oft. Alternativ lassen sich Bedingungen fast immer so for-
92 5 Kontrollflussanweisungen

mulieren, dass man darauf verzichten kann. Die Anwendung ist aber etwa dann effektiv,
wenn man mit einer Suche eine Liste durchläuft und in einigen Konstellationen nach-
folgende Anweisungen überspringen möchte. Wir erstellen zum Test wieder ein Beispiel
(continue_anweisung.js):

"use strict";
for (var i = 0; i < 10; i++) {
    [Link]("");
    if ((i % 2) != 0) continue;
     [Link]("Der Wert ist ", i);
}

Wir arbeiten wieder mit einer for-Schleife.

• Die erste Anweisung des Schleifenblocks wird für jeden Durchlauf ausgeführt (Aus-
gabe Leerzeichen).
• Danach wird unter Verwendung des Modulo-Operators und des Ungleich-Operators
in einer if-Bedingung getestet, ob die Zählvariable ungerade ist.
• Falls der Test true liefert, wird continue ausgeführt und sofort ein weiterer Durchlauf
der Schleife gestartet. Der Wert der Zählvariable wird dabei natürlich um 1 erhöht.
Die folgende Zeile im Block der for-Schleife wird dabei übersprungen.
• Falls die Zählvariable jedoch gerade ist, wird die Anweisung jedoch ausgeführt (Aus-
gabe des Werts der Zählvariablen).

5.3.3 Rückgabe mit return

Im Zusammenhang mit Funktionen gibt es die Sprunganweisung return. Diese ver-


lässt eine Funktion und gibt in der Regel einen Rückgabewert zurück. Wir werden diese
Sprunganweisung bei den Funktionen behandeln.

5.3.4 Unterbrechung mit throw

Die vierte Sprunganweisung bei JavaScript nennt sich throw. Damit werfen Sie eine so-
genannte Ausnahme aus, die den normalen Programm- bzw. Skriptablauf unterbricht und
zu einer Behandlungsroutine springt. Wir behandeln Ausnahmebehandlung in einem spä-
teren Kapitel mit fortgeschrittenen JavaScript-Techniken.
Arrays, JSON und andere iterierbare
Elemente 6

Übersicht
Im Fokus des Kapitels steht ein erster Kontakt mit Arrays und dem JSON-Format.
Vereinfacht erlauben diese Strukturen das Speichern von mehreren Werten. Beides
sind aber auch Strukturen, die eng mit dem objektorientierten Ansatz in JavaScript
verbunden sind. Auf der einen Seite müsste man sich vor einer Behandlung von
JSON und Arrays die objektorientieren Konzepte noch genauer ansehen. Auf der
anderen Seite sind Arrays so elementar, dass man sie in mehreren Kontexten be-
nötigt, die nicht explizit OOP darstellen. Wie üblich in JavaScript geht man also
vielfach mit Arrays und oft auch JSON um, ohne deren Bedeutung im objekt-
orientierten Kontext zu beachten und teils sich sogar überhaupt bewusst zu sein.
Deshalb wird hier das grundsätzliche Konzept vorgestellt und später betrachten
wir Arrays und JSON erneut. Dann aber explizit unter dem OOP-Aspekt. Zudem
werden wir ganz kurz einen Blick auf Maps, Sets und ein paar weitere iterierbare
Datenstrukturen werfen.

6.1 Arrays

Arrays haben eine große Bedeutung in der Programmierung, wobei in JavaScript die Be-
deutung noch größer ist als in den meisten anderen Programmiersprachen.

 Ein Array (auch Feld oder Datenfeld) ist im Prinzip ein Speicherbereich, in dem Sie
mehrere Werte speichern können. Zugriff auf die Werte erfolgt über einen Bezeichner
(eine Variable, die auf das Array verweist) und einen Index (Schlüssel).

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 93


Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
94 6 Arrays, JSON und andere iterierbare Elemente

In JavaScript ist ein Array ein Objekt1, aber jedes Objekt ist im Kern in JavaScript auch
ein Array – nur eine Liste mit Schlüssel-Werte-Paaren und das ist außergewöhnlich.
Zwar sind die verfügbaren Methoden zwischen JSON-Objekten und „normalen“ Arrays
nicht identisch. Für die Struktur und den Zugriff auf die enthaltenen Elemente ist das
aber meist2 irrelevant. Auf diese Details kommen wir später zurück.

 Die Ausführungen in dem Kapitel gelten vordergründig für Arrays, ohne – bis auf
Ausnahmen – den Objektcharakter zu beachten. Aber sie gelten im Grunde immer
ebenso für alle Objekte, wenn nichts explizit Gegenteiliges ausgeführt wird, da
eben Objekte wie Arrays als Hash-Listen abgebildet werden.

Um das zumindest soweit zu erläutern, wie es an dieser Stelle sinnvoll ist, wird kurz erklärt, wie
speziell in JavaScript Objekte zu sehen sind. Es sind fundamentale Datenstrukturen, die verwendet
werden, um Daten zu organisieren und zu speichern. Objekte in JavaScript bestehen aus Schlüs-
sel-Wert-Paaren (Hash-Liste), wobei jeder Schlüssel eine Zeichenfolge (auch bekannt als Eigen-
schaft oder Attribut) ist und jeder Wert mit diesem eindeutigen Schlüssel verknüpft ist. Objekte
in JavaScript sind dynamisch, was bedeutet, dass Sie neue Eigenschaften als auch Methoden zu
einem Objekt hinzufügen, vorhandene Eigenschaften ändern oder entfernen können, nachdem das
Objekt erstellt wurde. Der Zugriff auf Member eines Objekts mithilfe des Punktoperators (Punkt-
notation oder der Klammerschreibweise ([]) zugreifen. Die Klammerschreibweise ist nützlich,
wenn der Name der Eigenschaft eine Variable ist oder wenn der Name der Eigenschaft Sonder-
zeichen ­enthält.

Ein Array ist von Vorteil, wenn Sie eine Reihe zusammengehörender Informationen spei-
chern wollen. Das könnten beispielsweise die Tage eines Monats sein. Sie können zwar
für jeden Tag des Monats eine eigene Variable definieren, die dann die Nummer des Tags
im Monat aufnimmt. Diese Methode wäre sehr umständlich. Durch die Verwendung
eines Arrays genügt eine Variable und der Index. Mithilfe von Variable und Index können
Sie auf mehrere Elemente zugreifen.

 Arrays in JavaScript sind nullindiziert. Das bedeutet, dass das erste Element in
einem Array den Index 0 hat, das zweite den Index 1 usw. Zumindest gilt das bei
automatisch durchindizierten, numerischen Arrays. Auf die Details gehen wir
gleich ein (Abschn. 6.1.6).

Ein großer Vorteil von Arrays ist, dass Sie damit auch über Schleifen auf die Elemente
zugreifen können (Abschn. 6.3).

1 Was aber in fast allen modernen Sprachen der Fall ist.


2 Ausnahme ist die for-of-Schleife (Abschn. [Link]).
6.1 Arrays 95

 Beachten Sie, dass in JavaScript unterschiedliche Datentypen in einem Array


gespeichert werden können. Das ist in einigen anderen Sprachen nicht der
Fall, die bei Array-Inhalten den gleichen Datentyp fordern. Da Elemente von
jedem in JavaScript erlaubten Datentyp sein kann, kann man auf ganz einfache
Weise verschachtelte Arrays oder allgemein verschachtelte Strukturen erstellen
(Abschn. 6.1.5 und 6.2.3).

6.1.1 Ein Array erzeugen

Ein Array wird in JavaScript etwas anders erzeugt als eine normale Variable. Sie müssen
neben dem Namen, über den das Array zugänglich ist, dem System auch kenntlich machen,
dass eine ganze Sammlung an Werten vorliegt. Es gibt dazu verschiedene Wege in Java-
Script. Durch das Duck-Typing-Konzept wird die Information, dass eine Variable auf ein
Array verweisen soll, ausschließlich durch den zugewiesenen Wert/Datentyp festgelegt. Eine
besondere Kennung einer Array-Variablen, wie es anderen Sprachen üblich ist, gibt es nicht.

[Link] Die Deklaration mit einem Konstruktor


Zum Erzeugen von Arrays kann man den Array-Konstruktor verwenden. Beispiel:

Beispiel

var feldname = new Array(); // leeres Feld ◄

 Ein Konstruktor ist in JavaScript eine Methode bzw. Funktion, um ein Array oder Ob-
jekt zu erzeugen. Dem Aufruf der Konstruktors stellt man das Schlüsselwort new voran.
Man kann in dem Konstruktor auch die Anzahl der Elemente angeben, die das Array ent-
halten soll.

Beispiel

var feldname = new Array(5); // Deklaration eines Feldes mit 5 Elementen ◄

Das ist aber in JavaScript sowohl unnötig als auch wirkungslos. Zwar würde mit einem
numerischen Parameter tatsächlich die Größe des Arrays vorgegeben, aber Sie können
beim Hinzufügen von Elementen zu einem Array jederzeit das Array vergrößern. Des-
halb gibt man in der Praxis eigentlich nie eine Größe des Arrays bei der Deklaration an,
da Array-Elemente sowieso „on the fly“ hinzugefügt werden, wenn sie benötigt werden.
Wie erwähnt, ist ein Array in JavaScript einfach eine Liste mit Key-Value-Einträgen, die
dynamisch geändert werden kann.

 Mit [Link] können Sie auf einfache Weise alle Elemente eines Arrays aus-
geben.
96 6 Arrays, JSON und andere iterierbare Elemente

6.1.2 Deklaration mit einem Array-Literal

Arrays können Sie in JavaScript auch unter Verwendung von einem sogenannten Ar-
ray-Literal erzeugen und damit auf die Notation von einem expliziten Konstruktor ver-
zichten. Sie verwenden dann auch nicht das Schlüsselwort new. In diesem Fall spricht
man von einer deklarativen bzw. literalen Erzeugung. Aber man muss dem System wie-
der kenntlich machen, dass eine Deklaration nicht einfach eine Variable neu anlegt, son-
dern ein Datenfeld. Dazu verwendet man in JavaScript (und vielen anderen Sprachen)
eckige Klammern – das kennzeichnet dann ein Array-Literal. Sie weisen einer Variablen
bei der Deklaration in eckigen Klammern eine Reihe an Werten zu, die durch Kommata
getrennt werden. Die eckigen Klammern können auch leer bleiben. In jedem Fall legt
man über Duck-Typing fest, dass es sich um ein Array handelt. Dabei werden die Ele-
mente automatisch numerisch indiziert, was die Größe von dem Array festlegt.

Beispiel

var namen = ["Müller", "Meier", "Schmitt", "Mustermann"]; ◄

6.1.3 Der Datentyp eines Arrays

Arrays sind Objekte. Das kann man ganz einfach mit typeof bestimmen:

"use strict";
// Literale Erzeugung
var namen = ["Müller", "Meier", "Schmitt", "Mustermann"];
var x = new Array(); // Array-Konstruktor
[Link](namen, typeof namen);
[Link](x, typeof x);

Das ist die Ausgabe:


[ 'Müller', 'Meier', 'Schmitt', 'Mustermann’] object
[] object

6.1.4 Zugriff auf Array-Elemente

Der Zugriff auf ein einzelnes Element des Arrays erfolgt über die Angabe eines Index,
den Sie in eckigen Klammern angeben müssen. So erhalten Sie mit namen[0] das erste
Element des Feldes namen, mit namen[1] das zweite Element usw. Dabei kann man
diese Zugriffe sowohl lesend als auch schreibend verwenden. Hier ist ein vollständiges
Beispiel, das sowohl die literale Erzeugung als auch die Verwendung eines Konstruktors
zeigt ([Link]).
6.1 Arrays 97

"use strict";
// Literale Erzeugung
var namen = ["Müller", "Meier", "Schmitt", "Mustermann"];
var x = []; // Leeres Array
var primzahlen = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
var person = ["Hans", "Dampf", 42] // unterschiedliche Datentypen
Elemente
var jahreszeiten = new Array(); // Array-Konstruktor
// Zuweisung von Elementen zum Array
jahreszeiten[0] = "Frühjahr";
jahreszeiten[1] = "Sommer";
jahreszeiten[2] = "Herbst";
jahreszeiten[3] = "Winter";
// Ausgabe komplette Arrays
[Link](namen);
[Link](primzahlen);
[Link](jahreszeiten);
[Link](person[0], person[1]); // Zugriff auf einzelne Elemente

Das ist die Ausgabe:


[ 'Müller', 'Meier', 'Schmitt', 'Mustermann’ ]
[ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ]
[ 'Frühjahr', 'Sommer', 'Herbst', 'Winter’ ]

 Nutzen Sie einen Index das erste Mal bei einer Wertzuweisung, wird ein neues
Element in dem Array angelegt. Jeder weitere Zugriff mit dem gleichen Index
greift dann auf das Element zu. Man kann es auslesen, aber auch ändern.

6.1.5 Verschachtelte Arrays

Im Gegensatz zu den meisten anderen Programmiersprachen ist das Anlegen von ver-
schachtelten Arrays (auch mehrdimensionale Arrays genannt, wobei diese nur als
Spezialfall zu verstehen sind) in JavaScript im Grunde trivial. Sie geben einfach als Ele-
ment ein weiteres Array an. Dabei können Sie beliebig mit anderen Datentypen mischen
und beliebig viele Ebenen angeben. Beim Zugriff notieren Sie die verschiedenen Dimen-
sionen nacheinander in getrennte eckige Klammern.

"use strict";
var a = [[1,2],[3,4]];
var b = [[1,2],5,[3,4]];
var c = [[1,2],[[3],[4]],[9,[5,6,7,8]]];
[Link](a,b,c);
[Link](a[0],a[1][0]);
[Link](c[2],c[2][1], c[2][1][3]);
98 6 Arrays, JSON und andere iterierbare Elemente

Das ist die Ausgabe:


[ [ 1, 2], [ 3, 4 ] ] [ [ 1, 2], 5, [ 3, 4 ] ] [ [ 1, 2 ], [ [ 3], [ 4 ] ], [ 9, [ 5, 6, 7, 8 ] ] ]
[ 1, 2 ] 3
[ 9, [ 5, 6, 7, 8 ] ] [ 5, 6, 7, 8 ] 8
Der Code definiert drei verschiedene mehrdimensionale bzw. verschachtelte Arrays.

• Das Array a hat zwei Unterarrays [1,2] und [3,4].


• Das Array b hat drei Elemente: [1,2], 5, und [3,4]. Das zweite Element ist ein einzel-
ner Wert, während die anderen beiden Unterarrays sind.
• Das Array c hat drei Elemente: [1,2], [[3],[4]], und [9,[5,6,7,8]]. Sowohl das zweite
als auch das dritte Element enthalten jeweils Unterarrays, die wiederum weitere ver-
schachtelte Arrays haben.
• Der Code gibt dann diese Arrays und bestimmte Elemente dieser Arrays über die con-
[Link]-Funktion aus.
– [Link](a,b,c);: Gibt alle Arrays aus.
– [Link](a[0],a[1][0]);: Gibt das erste Element von a (das Unterarray [1,2])
und das erste Element des zweiten Elements von a (die Zahl 3) aus.
– [Link](c[2],c[2][1], c[2][1][3]);: Gibt das dritte Element von c (
[9,[5,6,7,8]]), das zweite Element des dritten Elements von c ([5,6,7,8]) und das
vierte Element dieses Unterarrays (die Zahl 8) aus.

6.1.6 Die Eigenschaft length

Da Arrays Objekte sind, gibt es Eigenschaften. Bei Arrays gibt es die Eigenschaft
length, die oft als Anzahl der Elemente und damit der Größe eines Arrays erklärt bzw.
interpretiert wird. Das ist falsch. Oder zumindest gilt es nur in Ausnahmesituationen.
Schauen wir uns diese etwas provokante Behauptung mit Beispielen an.

"use strict";
// Literale Erzeugung
var namen = ["Müller", "Meier", "Schmitt", "Mustermann"];
[Link](namen, [Link]);

Die Ausgabe wird das sein:


[ 'Müller', 'Meier', 'Schmitt', 'Mustermann’ ] 4

Das Array enthält ganz offensichtlich vier Elemente und der Wert von length ist 4. Das
sieht sehr vernünftig aus, ist aber vermutlich die Ursache für das Missverständnis. Be-
trachten Sie diese Abwandlung:

"use strict";
// Literale Erzeugung
6.2 Die Objekt-Notation – JSON 99

var namen = ["Müller", "Meier", "Schmitt", "Mustermann"];


namen[10] = "Musterfrau";
[Link](namen, [Link]);

Dem Array wird nachträglich ein Element zugewiesen. Aber beachten Sie, dass eine
„Lücke“ in der Indizierung vorhanden ist. Das ist die Ausgabe.
[ 'Müller', 'Meier', 'Schmitt', 'Mustermann', <6 empty items>, 'Musterfrau’] 11

Es gibt nur 5 Elemente in dem Array, aber length hat den Wert 11. Was über console.
log als „empty items“ bezeichnet wird, sind Elemente mit dem Zustand undefined. Das
erkennen Sie beim direkten Zugriff über einen Index, der auf so ein Array-Element ver-
weist. Was ist der Hintergrund? Die Anzahl der zugewiesenen Werte legt im ersten Fall
indirekt die anfängliche Größe des Datenfeldes fest. Das Array wird dabei automatisch
numerisch indiziert. Hier haben wir genau die oben angedeutete Situation – die Anzahl
der Elemente und damit die Größe von dem Array wird durch die Eigenschaft length re-
präsentiert, sofern man ohne Lücken im Index arbeitet.

 Tatsächlich wird die Eigenschaft length in vielen Situationen nicht die Größe eines
Arrays bzw. die Anzahl der enthaltenen Elemente widerspiegeln, sondern nur den
Wert des höchsten numerischen Index plus 1. Daraus kann man zwar in einigen
Situationen auf die Anzahl der Elemente in einem Array schließen (wenn der Index
wie schon erwähnt durchgängig hochgezählt wurde), aber man kann sich nicht dar-
auf verlassen und meist ist das auch nicht der Fall.

6.1.7 Textindizes

Tatsächlich ist dieses Verhalten absolut logisch, denn in JavaScript gibt es grundsätzlich
nur Textindizes! Wenn Sie numerische Indizes angeben oder diese automatisch gene-
riert werden, wird der numerische Schlüssel im Hintergrund zum String umgewandelt.
Auch andere Datentypen als Indizes werden auf einen String gecastet. Deshalb sind auch
scheinbar unlogische Array-Strukturen gar nicht unlogisch. Das wird noch viel deut-
licher, wenn man JSON betrachtet.

6.2 Die Objekt-Notation – JSON

Objekte und Arrays werden in JavaScript identisch als Listen mit einer Auflistung über
Schlüssel-Werte-Paare (Hash-Listen) verwaltet. Das führt dazu, dass es auch eine de-
klarative Erzeugung von Arrays mit einem sogenannten Objekt-Literal gibt. Dazu no-
tiert man in geschweiften Klammern eine Kommata-getrennte Liste aus Schlüssel-Werte-
Paaren als Literal, das einer Variablen zugewiesen wird. Das erlaubt sprechende Indi-
zes – also Textindizes. Auch damit erzeugen Sie ein Array bzw. ein Objekt, wobei die
100 6 Arrays, JSON und andere iterierbare Elemente

Schlüsselpaare mit Kommata getrennt werden und sowohl in eine als auch mehrere Zei-
len geschrieben werden können. Auch Schlüssel und Wert können bei Bedarf auf meh-
rere Zeilen verteilt werden.

Beispiel
var kapitaen = { name: " Picard", vorname: " Jean-Luc"};

 Wichtig – Textindizes dürfen nicht mit einer Zahl oder einigen anderen Sonder-
zeichen beginnen. Man kann sie sowohl explizit als String angeben oder in JSON
auch ohne Hochkommata notieren. Sie werden von JavaScript so oder so als
Strings verstanden.

Diese Objekt-Notation für Arrays wird als JavaScript Object Notation (JSON) be-
zeichnet. Damit wird auch schon deutlich, dass wir hier von Objekten reden.

6.2.1 Der Datentyp bei JSON

Schauen wir uns an, was typeof an Datentyp bei JSON liefert – es wird object sein:

"use strict";
var kapitaen = {name: "Picard", vorname: "Jean-Luc"};
[Link](kapitaen, typeof kapitaen);

Ausgabe ist das: { name: 'Picard', vorname: 'Jean-Luc'} object

6.2.2 Zugriff auf Elemente in einem JSON-Objekt

Die Verwendung von Textindizes ist auch als assoziierte Arrays bekannt. Bei einem assoziier-
ten Array verwenden Sie als Index den Datentyp String als Schlüssel. Dementsprechend wird der
Index beim Zugriff immer(!) in Hochkommata notiert. Beachten Sie den Unterschied beim Er-
zeugen, wo die Notation in Hochkommata optional ist.

Natürlich können Sie auch numerische Indizes beim Zugriff als String notieren, denn im
Hintergrund werden Indizes vom Typ Number auf String gecastet, weil es eben nur Text-
indizes gibt. Das macht den Umgang mit Arrays extrem effizient, schlank und einfach3.
Auch wenn man ohne diese Detailkenntnisse vielleicht ein „eigenartiges“ Verhalten emp-
findet. Zugriff auf einzelne Elemente eines assoziierten Arrays kann wie bei numerischen

3 Ich kenne keine andere Programmiersprache, die für mich JavaScript beim Umgang mit Arrays

das Wasser reichen kann.


6.2 Die Objekt-Notation – JSON 101

Indizes erfolgen. Etwa über die Klammernotation mit kapitaen["abc"]. oder über die
Punktnotation wie bei [Link]. Wie bei „normalen“ Arrays gilt, dass der Zugriff mit
einem nicht vorhandenen Index undefined liefert.

 Es ist in JavaScript meist irrelevant, auf welche Art Sie ein Array erzeugen. Letzt-
endlich kommt immer von den Inhalten ein strukturell gleiches Objekt heraus,
wobei man nur bei JSON explizit beim Anlegen die Textindizes angeben kann. Bei
JSON-Objekten kann man allerdings auch über die Punktnotation auf Elemente
zugreifen. Etwa über [Link]. Das geht nur bei JSON und nicht bei Arrays
mit numerischer Indizierung. Denn Textindizes dürfen nicht mit einer Zahl oder
einem anderen Sonderzeichen beginnen. Beachten Sie, dass es auch hinsichtlich
des konkreten Objekttyps Unterschiede zwischen einen Array, das mit JSON er-
zeugt wurde, und den anderen beiden Wegen gibt. Das beleuchten wir bei der OOP
genauer. Und nochmal zur Wiederholung – intern werden alle Arten Indizes als
Textindizes verstanden. Auch wenn Sie etwa boolesche Literalen (true oder false)
als Index hinschreiben, werden diese einfach als Texte verstanden. Es gibt keinerlei
qualitativen Unterschied zwischen einem Text "abc", "true" oder "42". Textindizes
werden nicht geparsed und sind damit keine Token. Sondern nur eindeutige Texte
im Kontext der Indizierung des Arrays. Im Prinzip ist es daher auch möglich, nu-
merische, boolesche und assoziative Indizes gemeinsam in einem Datenfeld zu
verwenden. Die Indizes müssen nur eindeutig sein. Sie sollten allerdings von sol-
chen Konstruktionen Abstand nehmen. Der Quelltext wird unübersichtlich und es
gibt meist keinen sinnvollen Grund.

6.2.3 Verschachtelte JSON-Arrays

Verschachtelte Arrays haben wir ja schon gesehen. Man könnte es kurzhalten: „Bei
JSON geht alles identisch!“ Aber diese Aussage setzt voraus, dass das gemeinsame Kon-
zept von Arrays und JSON komplett klar ist. Deshalb werden wir mit Beispielen die An-
gelegenheit verdeutlichen.

"use strict";
var person = {
  name:"Otto", titel: ["Prof", "Dr"], adresse:{
        plz:12345, ort:"Irgendwo", strasse:{ name:"Milchstrasse", nr:42 }
  }
};
[Link](person, typeof person);
[Link]([Link], typeof person["name"]);
[Link]([Link][0], typeof [Link][0]);
[Link]([Link],
  typeof [Link]["strasse"].nr);
102 6 Arrays, JSON und andere iterierbare Elemente

Dieser Code definiert ein JavaScript-Objekt mit dem Namen person, das verschiedene
Eigenschaften enthält. Dabei wurden bewusst an diversen Stellen unterschiedliche
Datentypen verwendet. Es gibt weitere JSON-Objekte, Arrays, Zahlen und Strings als
Inhalte. Das ist eben in JavaScript unproblematisch, einfach und konsistent.

• Die Eigenschaften des person-Objekts sind:


– name: Ein String mit dem Wert "Otto".
– titel: Ein Array mit den Werten ["Prof", "Dr"].
– adresse: Ein weiteres Objekt, das die Adresse der Person darstellt. Dieses Objekt
hat weitere Eigenschaften:
plz: Eine Zahl mit dem Wert 12.345.
ort: Ein String mit dem Wert "Irgendwo".
– strasse: Ein weiteres Objekt, das die Straßendetails enthält. Dieses Objekt hat zwei
Eigenschaften:
name: Ein String mit dem Wert "Milchstrasse".
nr: Eine Zahl mit dem Wert 42.

Der Code gibt dann verschiedene Informationen über das person-Objekt aus:

• [Link](person, typeof person);: Gibt das gesamte person-Objekt aus sowie den
Datentyp von person, der ein Objekt ist.
• [Link]([Link], typeof person["name"]);: Gibt den Wert der name-Eigen-
schaft von person aus ("Otto") sowie den Datentyp dieser Eigenschaft, der ein String
ist.
• [Link]([Link][0], typeof [Link][0]);: Gibt das erste Element des titel-
Arrays von person aus ("Prof") sowie den Datentyp dieses Elements, der ein String
ist.
• [Link]([Link], typeof [Link]["strasse"].nr);: Gibt die
Hausnummer (42) der strasse-Eigenschaft des adresse-Objekts von person aus sowie
den Datentyp dieser Eigenschaft, der eine Zahl ist.

Beachten Sie bei den Ausgaben, dass zum Zugriff auf die Elemente sowohl die Array-Notation
als auch Punktnotation verwendet wird und beide sogar gemischt werden. Auch das ist ein extrem
leistungsfähiges und dennoch konsistentes, einfaches Feature in JavaScript, auch wenn dies an der
Stelle nur aus didaktischen Gründen gemacht wird.

Das ist die Ausgabe (kompakte Darstellung):


{ name: 'Otto', titel: [ 'Prof', 'Dr’], adresse: { plz: 12.345, ort: 'Irgendwo', strasse: {
name: 'Milchstrasse', nr: 42 } }} object
Otto string
Prof string
42 number
6.3 Iteration über Arrays und JSON 103

6.3 Iteration über Arrays und JSON

Ein großer Vorteil von Arrays ist, dass man über Schleifen oder einen Iterator mit wenig
Aufwand auf Elemente zugreifen kann.

6.3.1 Zugriff auf numerisch indizierte Arrays

Auch wenn numerisch indizierte Arrays eigentlich mit Textindizes arbeiten – bei der
Iteration mit einer Zählschleife können wir dennoch den numerischen Index nutzen, um
auf die einzelnen Elemente zuzugreifen. Der Zählindex wird im Inneren der Schleife ein-
fach wieder zu einem String gecastet. So können Sie mit Schleifen über alle Elemente
eines Arrays iterieren. Dazu verwenden wir nachfolgend die for-Schleife, aber Sie kön-
nen auch jede andere Schleifenform verwenden.

"use strict";
var namen = ["Müller", "Meier", "Schmitt", "Mustermann"];
for(let i = 0; i < [Link]; i++) {
  [Link](namen[i]);
}

In der Schleife wird das gesamte Array ausgegeben, indem der Zählindex für den Zugriff
auf ein Element verwendet wird. Nun ist es jedoch eine Folge sowohl der losen Typisie-
rung als auch des internen Aufbaus von Arrays, dass diese Vorgehensweise dann Prob-
leme macht, wenn nicht alle Elemente in dem Datenfeld „belegt“ sind. Dann erhalten
Sie beim Zugriff auf ein Array-Element unter Umständen den Wert undefined. Das soll
praktisch mit einer kleinen Abwandlung verdeutlicht werden:

"use strict";
var namen = new Array()
namen[0] = "Müller";
namen[2] = "Meier";
namen[4] = "Schmitt";
namen[7] = "Mustermann";
for(let i = 0; i < [Link]; i++) {
  [Link](namen[i]);
}

Die Ausgabe ist das:


Müller
undefined
undefined
104 6 Arrays, JSON und andere iterierbare Elemente

Schmitt
undefined
undefined
Mustermann

Das ist ein klares und logische Verhalten, denn in dem Beispiel sind manche Textindizes
einfach nicht vorhanden. Das Problem ist die Schleife, die über einen numerischen Index
läuft und dabei eben auch Zahlen für den Zählindex verwendet, für die es keine String-
Repräsentation gibt. Es gibt ja auch kein namen["IrgendEinText"]. Warum sollte es
namen["6"] geben? Nun liefert length allerdings Informationen über den höchsten nu-
merischen Index des Arrays. Und beim Iterieren über die nicht initialisierten Array-Ele-
mente erhalten Sie undefined.
Sollten Sie nun in solch eine Situation kommen, in der Sie mit einer Schleife arbeiten
und die Indizes des Arrays nicht eine lückenlose numerische Reihe darstellen, können
Sie gegebenenfalls auf undefined überprüfen, bevor Sie ein Array-Element verwenden.
Beachten Sie die folgende Abwandlung des letzten Beispiels:

"use strict";
var namen = new Array()
namen[0] = "Müller";
namen[2] = "Meier";
namen[4] = "Schmitt";
namen[7] = "Mustermann";
for(let i = 0; i < [Link]; i++) {
  if (namen[i] == undefined) continue;
  [Link](namen[i]);
}

Wenn ein Array-Element den undefined-Zustand aufweist, startet mit continue der
nächste Schleifendurchlauf. Aber es gibt bessere Möglichkeiten.

6.3.2 Zugriff auf alle Elemente eines Arrays über for-in und for-of
sowie forEach

Beim Zugriff auf assoziative Datenfelder haben Sie den Preis zu zahlen, dass Sie die
Elemente mit einem nichtnumerischen Index nicht in normalen Schleifen erwischen. So
etwas wie die Verwendung einer Zählschleife ist also bei so einem Datenfeld mit Text-
indizes sowieso nicht möglich. Aber mit der for-of- oder for-in-Schleife ist das kein Pro-
blem. Diese kann natürlich auch auf numerisch indizierte Arrays oder gar gemischt indi-
zierte Arrays angewendet werden, denn – und das sollte mittlerweile kalter Kaffee sein
– es gibt ja sowieso nur Textindizes.
6.3 Iteration über Arrays und JSON 105

[Link] for-in
Das wäre die Syntax:

Beispiel

for ([Zählvariable] in [Arrayname]) { … } ◄

Mit dieser Schleifenform kann man ohne explizite Angabe der Größe des Datenfelds die-
ses vollständig durchlaufen. Damit würden Sie auch die als undefined gekennzeichneten
Array-Elemente nicht stören, da diese von der Schleife überhaupt nicht geliefert werden.

 Beachten Sie, dass die Zählvariable der Schleife den Index darstellt. Das ist in ei-
nigen anderen Sprachen wie Java bei der vergleichbaren Schleife nicht der Fall.
Dort steht die Zählvariable für das Element selbst. Für den Zugriff auf den Inhalt
eines Felds müssen Sie die Zählvariable dann als Index in der Implementation der
Schleife verwenden.

"use strict";
var namen = new Array()
var x = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
var kapitaen = { name: " Picard", vorname: " Jean-Luc"};
namen[0] = "Müller";
namen[2] = "Meier";
namen[4] = "Schmitt";
namen[7] = "Mustermann";
for (let i in x) {
  [Link](i + ": " + x[i] );
}
for (let i in namen) {
  [Link](i + ": " + namen[i] );
}
for (let i in kapitaen) {
  [Link](i + ": " + kapitaen[i] );
}

In der Schleife steht i für den Index und etwa x für das Datenfeld. Dementsprechend ist
x[i] in jedem Schleifendurchlauf der jeweilige Wert eines Array-Elements. Das ist die
Ausgabe:
0: 1
1: 2
2: 3

9: 23
106 6 Arrays, JSON und andere iterierbare Elemente

10: 29
0: Müller
2: Meier
4: Schmitt
7: Mustermann
name: Picard
vorname: Jean-Luc

 Wenn Sie bei einem assoziativen Datenfeld zusätzlich einen numerischen


Index verwerten wollen, können Sie in der for-in-Schleife natürlich eine zusätz-
liche Zählvariable mitlaufen lassen.

[Link] for-of
Iteration mit for-of geht nur in neueren Versionen von JavaScript. Man erhält damit di-
rekt die Elemente eines Arrays.

"use strict";
var namen = new Array()
namen[0] = "Müller";
namen[2] = "Meier";
namen[4] = "Schmitt";
namen[7] = "Mustermann";
for (let i of namen) {
  [Link](i);
}

Beachten Sie, dass wir hier mit abzählbaren Arrays arbeiten und hier dann auch wieder
undefined für die Elemente erhalten können.

[Link] Unterschiede zwischen for-in und for-of


Man sollte sich genauer die Unterschiede zwischen for-in und for-of ansehen. Beides
sind Schleifen bzw. Iteratoren in JavaScript, die dazu dienen, über die Elemente von
Datenstrukturen zu iterieren. Sie haben jedoch unterschiedliche Anwendungen und Ver-
wendungszwecke. Hier sind die Hauptunterschiede zwischen ihnen, wobei wir bereits
soweit präzisieren, dass wir von Objekten sprachen, die grundsätzlich bei Elementen die
Grundlagen bilden, wenn diese nicht primitiv (boolesch oder Zahlen) sind:

• Mit for-in iteriert man über alles Eigenschaftsnamen eines Objekts. Man erhält diese
als Schlüssel des Objekts. Man kann die Schleife auch bei Arrays verwenden,
• Die for-of-Schleifen wird verwendet, um über die Werte einer Iterable-Struktur zu ite-
rieren, wie z. B. Arrays oder Strings, als auch Strukturen wie Maps, Sets usw., die wir
6.3 Iteration über Arrays und JSON 107

bisher noch nicht hatten. Man erhält damit die Werte der Struktur und nicht über die
Schlüssel oder Indizes. Aber for-of kann nicht direkt mit Objekten verwendet werden,
da Objekte nicht iterierbar sind.

 Ab ECMAScript 5 gibt es noch die Möglichkeit [Link](o), um Objekt-


eigenschaften aufzulisten oder zu durchlaufen. Damit erhalten Sie ein Array
mit allen eigenen aufzählbaren Eigenschaftsnamen (Schlüssel) eines Objekts
o und mit [Link](o) ein Array mit allen Eigenschafts-
namen (aufzählbar oder nicht) eines Objekts o. Allerdings wird die Methode
nicht überall unterstützt.

[Link] Objektorientierter Ansatz beim Iterieren


In JavaScript steht auch ein Objektorientierter Ansatz beim Iterieren über passende
Strukturen bereit. Dabei greift wieder die Tatsache, dass es sich dabei um Objekte han-
delt und Objekte von einer iterierbaren Art stellen die Methode forEach zur Verfügung.
Diese kann auf Arrays, Maps und Sets angewendet werden kann. Sie ermöglicht das
Durchlaufen aller Elemente der Datenstruktur und das Ausführen einer Callback-Funk-
tion für jedes Element. Die allgemeine Syntaxlautet:

Beispiel

[Link](callback(currentValue [, index [, array]])[, thisArg]) ◄

Mit dem Callback wird eine Funktion angegeben, die für jedes Element in der Daten-
struktur aufgerufen wird. Sie akzeptiert drei Argumente:

• Das aktuelle Element (currentValue), das während der Iteration verarbeitet wird.
• Der Index des aktuellen Elements.
• Das optionale Array, über das iteriert wird.
• Ein optional Wert, der in der Callback-Funktion festgelegt wird, wenn sie aufgerufen
wird.

Da wir für Beispiele Funktionen benötigen, werden diese in das nächste Kapitel verlagert.

6.3.3 Über verschachtele Strukturen iterieren

Das Iterieren über verschachtelte Arrays oder JSON-Objekte erfolgt, indem Sie jedes
Element, das selbst ein Array oder ein JSON-Objekt ist, wieder selbst iterieren. Das kann
man mit jeder Schleife machen, über die man alle Elemente „erwischt“.
108 6 Arrays, JSON und andere iterierbare Elemente

var a = [[1,2],[3,4]];
var b = [[1,2],[[3],[4]],[5,6,7,8]];
// Iteration über Array 'a'
[Link]("Array a:");
for (var i = 0; i < [Link]; i++) {
  for (var j = 0; j < a[i].length; j++) {
      [Link]("Element [" + i + "][" + j + "]: " + a[i][j]);
  }
}
// Iteration über Array 'b'
[Link]("\nArray b:");
for (var k = 0; k < [Link]; k++) {
  for (var l = 0; l < b[k].length; l++) {
      [Link]("Element [" + k + "][" + l + "]: " + b[k][l]);
  }
}

Die Ausgabe zeigt zuerst den Inhalt von Array a und dann von Array b. Das geht auch
mit for-in. Wobei dies sogar meist die bessere Lösung ist.

"use strict";
var a = [[1,2],[3,4]];
var b = [[1,2],[[3],[4]],[5,6,7,8]];
// Iteration über Array 'a'
[Link]("Array a:");
for (var i in a) {
  for (var j in a[i]) {
      [Link]("Element [" + i + "][" + j + "]: " + a[i][j]);
  }
}
// Iteration über Array 'b'
[Link]("\nArray b:");
for (var k in b) {
  for (var l in b[k]) {
      [Link]("Element [" + k + "][" + l + "]: " + b[k][l]);
  }
}

Die Ausgabe wird identisch. Dieser Code verwendet for-in-Schleifen, um über die Indi-
zes der äußeren Arrays zu iterieren. Anschließend werden weitere for-in-Schleifen ver-
wendet, um über die Indizes der inneren Arrays zu iterieren und die entsprechenden Ele-
mente auszugeben.

 Diese Variante funktioniert auch auf JSON-Objekten.


6.3 Iteration über Arrays und JSON 109

Beachten Sie jedoch, dass diese beiden Lösungen nicht zwischen Array-Elementen bzw.
Objekten und anderen Typen der Eigenschaften unterscheidet. Sie funktionieren nur,
weil wir die Struktur kennen und es sich um 2-dimensionale Arrays mit genau so einer
Form von Inhalten handelt.

[Link] Typüberprüfung
Deshalb sollte man im allgemeinen Fall einer Iteration über verschachtelte Strukturen
auf jedem Element eine Typüberprüfung durchführen. Das kann man mit typeof machen.
Hier ist der Code:

"use strict";
var a = [[1,2],[3,4]];
var b = [[1,2],[[3],[4]],[5,6,7,8]];
[Link]("Array a:"); // Iteration über Array 'a'
for (var i in a) {
  for (var j in a[i]) {
      [Link]("Element [" + i + "][" + j + "]: " + a[i][j]);
  }
}
[Link]("\nArray b:"); // Iteration über Array 'b'
for (var k in b) {
  for (var l in b[k]) {
      [Link]("Element [" + k + "][" + l + "]: " + b[k][l]);
  }
}

In diesem Code wird typeof verwendet, um zu überprüfen, ob ein Element ein Objekt ist.
Wenn ja, wird überprüft, ob es ein verschachteltes Objekt ist, und entsprechend iteriert.
Andernfalls wird das Element direkt ausgegeben, zusammen mit seinem Typ.

Array a:
Element [0][0]: 1, Type: number
Element [0][1]: 2, Type: number
Element [1][0]: 3, Type: number
Element [1][1]: 4, Type: number
Array b:
Element 0 ist ein Objekt:
       Element [0]: 1,2, Type: object
Element 1 ist ein Objekt:
       Element [1][0] ist ein verschachteltes Objekt:
          Element [1][0][0]: 3, Type: number
       Element [1][1] ist ein verschachteltes Objekt:
          Element [1][1][0]: 4, Type: number
110 6 Arrays, JSON und andere iterierbare Elemente

Element 2 ist ein Objekt:


       Element [2]: 5,6,7,8, Type: object

Sie können diesen Code auf so umschreiben, dass statt typeof eine Methode isArray der
Klasse/Prototyp Array zum Einsatz kommt, die das gleiche Resultat bewirkt. Das greift
auf OOP vor, ist aber zum Vergleich ganz interessant. Das wollen wir für Array b in der
Abwandlung des Beispiels machen.


[Link]("\nArray b:"); // Iteration über Array 'b'
for (var k = 0; k < [Link]; k++) {
  if ([Link](b[k])) {
  // Überprüfen, ob das aktuelle Element ein Array ist
      [Link]("Element " + k + " ist ein Array:");
      if ([Link](b[k][0])) {
      // Wenn das Element ein verschachteltes Array ist
        for (var l = 0; l < b[k].length; l++) {
          [Link]("\tElement [" + k + "][" + l + "] ist ein ver-
schachteltes Array:");
          for (var m = 0; m < b[k][l].length; m++) {
          [Link]("\t\tElement [" + k + "][" + l + "][" + m + "]: " + b[k]
[l][m]);
            }
        }
      } else {
      // Wenn das Element kein verschachteltes Array ist
         [Link]("\tElement [" + k + "]: " + b[k]);
            }
     } else {
      [Link]("Element " + k + ": " + b[k]);
  }
}

Es sollte auffallen, dass der Quellcode mit der Typüberprüfung nicht mehr ganz trivial
ist. Und dennoch ist er nicht flexibel genug, denn sollte es mehr Ebenen, erreichen wir
nicht alle einzelnen Elemente. Für eine unbekannte an Ebenen bietet sich eine rekursive
Lösung an.

[Link] Iteration per Rekursion


Rekursion ist allgemein der Königsweg über flexible Datenstruktur. Da wir dafür aber
Funktionen benötigen, wird darauf im nächsten Kapitel eingegangen.

[Link] Elemente löschen
Bei selbstdefinierten Arrays und Objekten kann man mit delete vorhandene Elemente lö-
schen.
6.3 Iteration über Arrays und JSON 111

 Beachten Sie, dass Sie mit delete aber keine vordefinierten Standardobjekte
und -objekteigenschaften von JavaScript löschen können. Ebenso können in
der Regel nur Variablen und Objekte mit delete gelöscht werden, die nicht mit
dem Schlüsselwort var bzw. let deklariert wurden. Was eine Nutzung in Ver-
bindung mit "use strict" für das Löschen von Variablen ausschließt.

Bei numerisch indizierten Arrays wird delete ein undefiniertes „Loch“ an der gelöschten
Stelle hinterlassen, wodurch der Wert von length unverändert bleibt. Bei Objekten oder
assoziierten Arrays wird delete die Eigenschaft ebenso löschen und bei einem späte-
ren Zugriff auf diese Eigenschaft den Wert undefined zurückgeben, da die Eigenschaft
nicht mehr im Objekt vorhanden sein wird. Da allerdings bei solchen Objekten nicht mit
length operiert wird, ist eine Auswirkung auf den Zugriffcode unkritischer. Man muss
schon bewusst einen nicht vorhanden Index angeben. Die Anwendung von delete gibt
true zurück, wenn die Operation erfolgreich war, andernfalls false.

"use strict";
var namen = new Array()
var x = [1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29];
var kapitaen = { name: " Picard", vorname: " Jean-Luc" };
namen[0] = "Müller";
namen[2] = "Meier";
namen[4] = "Schmitt";
namen[7] = "Mustermann";
[Link](x);
[Link](namen);
[Link](kapitaen);
delete(x[3]);
delete(namen[3]);
delete(kapitaen["name"]);
[Link](x);
[Link](kapitaen);

Der gegebene Code demonstriert die Verwendung der delete-Anweisung in JavaScript.


Die Arrays x und namen, sowie das Objekt kapitaen, werden vor und nach dem Löschen
auf der Konsole ausgegeben. Einmal wird delete verwendet, um das Element mit Index
3 aus dem Array x, das Element mit Index 3 aus dem Array namen, und die Eigenschaft
"name" des Objekts kapitaen zu löschen. Die Ausgabe zeigt, wie sich das Löschen von
Elementen und Eigenschaften auf die Arrays und das Objekt auswirkt.
[ 1, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29].
[ 'Müller', <1 empty item>, 'Meier', <1 empty item>, 'Schmitt', <2 empty items>, 'Muster-
mann’].
{ name: ' Picard', vorname: ' Jean-Luc' }.
[ 1, 2, 3, <1 empty item>, 7, 11, 13, 17, 19, 23, 29 ].
{ vorname: ' Jean-Luc' }.
112 6 Arrays, JSON und andere iterierbare Elemente

6.4 Weitere iterative Datenstrukturen

Neben Arrays und JSON gibt es in JavaScript ein paar weitere iterative Strukturen, die
an der Stelle vorgestellt werden sollen. Dabei sind diese meist nur für besondere An-
wendungen oder Optimierungen notwendig und funktionieren auch nur in neueren Ver-
sionen von JavaScript

6.4.1 Maps

In JavaScript ist eine Map ein eingebauter Datenstrukturtyp, der es ermöglicht, Schlüs-
sel-Wert-Paare zu speichern. Im Gegensatz zu automatisch indizierten Arrays, bei denen
die Schlüssel implizit ganzzahlige Indizes sind, können bei Maps beliebige Werte als
Schlüssel verwendet werden. Sie sind damit JSON-Objekte sehr ähnlich, haben aber ei-
nige spezifische Eigenschaften bzw. Methoden und auch die Schüssel sind noch flexibler.
Hier sind einige wichtige Merkmale von Maps in JavaScript:

• Beliebige Schlüssel: Maps ermöglichen es, beliebige Werte als Schlüssel zu ver-
wenden, einschließlich Objekte, Funktionen und sogar andere Maps.
• Iterierbarkeit Maps sind iterierbar, was bedeutet, dass Sie leicht über ihre Schlüssel-
Wert-Paare iterieren können. Dies kann mithilfe von Schleifen oder der foreach-Me-
thode erfolgen.
• Effiziente Suche: Maps bieten eine besonders effiziente Möglichkeit, einen Wert an-
hand seines Schlüssels zu suchen.
• Erhaltung der Reihenfolge: Seit ECMAScript 2015 (ES6) garantieren Maps die Bei-
behaltung der Reihenfolge ihrer Einträge, was bedeutet, dass die Reihenfolge, in der
Elemente hinzugefügt werden, beibehalten wird.

Maps bieten eine flexible Möglichkeit, Daten zu organisieren und abzurufen, ins-
besondere wenn die Schlüssel nicht auf Ganzzahlen beschränkt sind und/oder die
Reihenfolge der Elemente wichtig ist. Hier ist ein einfaches Beispiel, das zeigt, wie eine
Map in JavaScript verwendet wird:

var myMap = new Map();


// Hinzufügen von Schlüssel-Wert-Paaren zur Map
[Link]("name", "John");
[Link]("age", 30);
[Link]("isStudent", false);
// Zugriff auf Werte in der Map
[Link]([Link]("name")); // Ausgabe: John
[Link]([Link]("age")); // Ausgabe: 30
[Link]([Link]("isStudent")); // Ausgabe: false
6.4 Weitere iterative Datenstrukturen 113

// Überprüfen, ob ein Schlüssel in der Map vorhanden ist


[Link]([Link]("name")); // Ausgabe: true
[Link]([Link]("occupation")); // Ausgabe: false
// Löschen eines Schlüssels aus der Map
[Link]("age");
// Überprüfen, ob ein Schlüssel nach dem Löschen vorhanden ist
[Link]([Link]("age")); // Ausgabe: false
// Iteration über die Schlüssel-Wert-Paare der Map
[Link]("Iterating over Map:");
for (let [key, value] of myMap) {
      [Link](key + ": " + value);
}

In diesem Beispiel wird zuerst eine leere Map erstellt. Dann werden Schlüssel-Wert-
Paare zur Map hinzugefügt und darauf zugegriffen. Es wird überprüft, ob bestimmte
Schlüssel in der Map vorhanden sind, und ein Schlüssel wird aus der Map gelöscht.
Schließlich wird über die Schlüssel-Wert-Paare der Map iteriert und sie werden auf der
Konsole ausgegeben.
Eine Map in JavaScript kann auch Objekte (JSON) als Schlüssel verwenden. Hier ist ein
Beispiel, wie eine Map mit einem JSON-Objekt als Schlüssel erstellt und verwendet wird:

var myMap = new Map(); // Erstellen einer Map


var jsonKey = { id: 1, name: "Hans" }; // JSON-Objekt als Schlüssel
[Link](jsonKey, "Wert für Schlüssel"); // Werte in der Map speichern
// Auf den Wert mithilfe des JSON-Schlüssels zugreifen
[Link]([Link](jsonKey));
var anotherJsonKey = { id: 2, name: "Maja" }; // Weiteres JSON-Objekt
[Link](anotherJsonKey, "Werte für anderen Schlüssel");
// Auf den Wert mithilfe des neuen JSON-Schlüssels zugreifen
[Link]([Link](anotherJsonKey)); // Ausgabe:

In diesem Beispiel wird eine Map erstellt, die JSON-Objekte als Schlüssel verwendet
und dann für den Zugriff verwendet.

6.4.2 Sets

Ein Set in JavaScript ist eine eingebaute Datenstruktur, die eine Sammlung eindeutiger
Werte enthält. Im Gegensatz zu Arrays können Sets keine Duplikate enthalten, was be-
deutet, dass jedes Element im Set einmalig ist. Ein Set bietet verschiedene nützliche Me-
thoden zum Hinzufügen, Entfernen und Überprüfen von Elementen sowie zum Durch-
laufen der Elemente in der Reihenfolge ihrer Einfügung. Einige der häufig verwendeten
Methoden/Eigenschaften für Sets sind:
114 6 Arrays, JSON und andere iterierbare Elemente

• add(value): Fügt ein neues Element zum Set hinzu.


• delete(value): Entfernt ein Element aus dem Set.
• has(value): Überprüft, ob ein bestimmtes Element im Set vorhanden ist.
• forEach(callbackFn [, thisArg]): Ruft eine Callback-Funktion für jedes Element im
Set auf.
• size: Anzahl der Elemente im Set.
• clear(): Entfernt alle Elemente aus dem Set.

Hier ist ein einfaches Beispiel, das zeigt, wie ein Set in JavaScript verwendet wird:

var mySet = new Set();


[Link](1);
[Link](2);
[Link](3);
[Link](1); // Hinzufügen eines Duplikats, wird ignoriert
[Link]([Link]); // Ausgabe: 3
[Link](function(value) {
  [Link](value); // Ausgabe: 1, 2, 3
});
[Link](2); // Entfernen eines Elements
[Link]([Link](2)); // Ausgabe: false
[Link](); // Entfernen aller Elemente aus dem Set
[Link]([Link]); // Ausgabe: 0

In diesem Beispiel wird ein Set erstellt und einige Operationen auf diesem Set durch-
geführt, wie das Hinzufügen von Elementen, Überprüfen, ob ein Element vorhanden ist,
Durchlaufen der Elemente und Entfernen von Elementen. Besonders spannend ist die
Verwendung von forEach, denn der Callback, der als Parameter angegeben wird, wird
für jedes Element im Set aufgerufen wird.

6.4.3 Weitere Datenstrukturen

In JavaScript gibt es noch andere vergleichbare Datenstrukturen, die je nach An-


wendungsfall verwendet werden können. Einige davon sind:

• WeakMap und WeakSet sind ähnlich wie Maps und Sets, aber mit dem Unterschied,
dass sie nur schwache Referenzen auf die Schlüssel-Wert-Paare oder Werte halten.
Dadurch können die Einträge im Speicher automatisch freigegeben werden, wenn
keine anderen Referenzen mehr auf sie verweisen, was nützlich ist, um Speicherlecks
zu vermeiden.
6.4 Weitere iterative Datenstrukturen 115

• Eine Queue ist eine abstrakte Datenstruktur, die Elemente nach dem FIFO-Prinzip
(First-In-First-Out) organisiert. Elemente werden am Ende der Queue hinzugefügt
und am Anfang entfernt.
• Ein Stack ist eine abstrakte Datenstruktur, die Elemente nach dem LIFO-Prinzip
(Last-In-First-Out) organisiert. Elemente werden oben auf dem Stapel hinzugefügt
und von dort entfernt.

Diese Datenstrukturen bieten unterschiedliche Funktionalitäten und Leistungsmerkmale,


je nach den Anforderungen Ihrer Anwendung. Es ist wichtig, die richtige Datenstruktur
basierend auf den spezifischen Anforderungen Ihrer Anwendung zu wählen, um effizi-
ente und gut strukturierte Lösungen zu entwickeln.
Funktionen, Prozeduren und Methoden
7

Übersicht
Beim Erstellen von JavaScripts werden Sie fast immer in die Situation kommen,
dass Sie Code zusammenfassen und über einen Namen aufrufen werden. Noch
öfter werden Sie vorhanden Code von jemanden anderem verwenden. Dies nennt
man dann eine Funktion. Aber in diesem Zusammenhang tauchen ebenfalls die Be-
griffe Unterprogramme, Prozeduren und Methoden auf. In dem Kapitel machen
wir uns deutlich, was es damit auf sich hat. wobei davon ausgehe, dass Ihnen die
Thematik grundsätzlich geläufig ist. Wir haben ja auch schon mehrfach mit Funk-
tionen bzw. Methoden gearbeitet. Aber in JavaScript gibt es eine ganze Reihe an
Besonderheiten.

7.1 Was ist allgemein ein Unterprogramm?

Allgemein reden wir bei Unterprogrammen im Moment erst einmal nur über eine
Reihe von Anweisungen im Quelltext, die zusammenfasst werden und einen Bezeichner
oder eine andere Möglichkeit zum nachträglichen Zugriff bekommen. Damit müssen Sie
diese Folge von Anweisungen nicht jedes Mal in den Quelltext schreiben, wenn Sie diese
Anweisungen benötigen. Sie werden nur einmal geschrieben, möglichst umfassend ge-
testet und dann jedes Mal über die Angabe des Bezeichners (oder einer Referenz) auf-
gerufen, wenn diese Folge von Anweisungen benötigt wird. Unterprogramme sind ein
entscheidender Teil des DRY-Konzepts.
In JavaScript werden als Funktionen definierte JavaScript-Anweisungen beim Laden
der Webseite respektive des Skripts nicht direkt ausgeführt, sondern erst, wenn sie ex-
plizit aufgerufen werden.

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 117
Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
118 7 Funktionen, Prozeduren und Methoden

Nur was hat es mit den verschiedenen Arten von Unterprogrammen auf sich? Es gab
früher in einigen Sprachen einen wesentlichen Unterschied zwischen Funktionen und
Prozeduren:

• Eine Funktion erledigt eine Aufgabe und liefert immer einen Wert als Ergebnis zurück
(einen Rückgabewert).
• Eine Prozedur erledigt eine Aufgabe, ohne einen Wert als Ergebnis zurückzugeben.

JavaScript kennt jedoch nur Funktionen und keine Prozeduren. Allerdings muss eine
JavaScript-Funktion keinen Wert zurückgeben, der manchmal sogar zwingend aus-
gewertet werden muss, wie es in anderen Programmiersprachen notwendig sein kann.
Damit sind in JavaScript die Unterschiede aufgehoben. Wir werden in Zukunft aus-
schließlich von Funktionen reden.

 Eine spezielle Form einer Funktion ist eine Methode. Methoden sind Funktionen, die
einem Objekt oder einer Klasse zugeordnet sind und nur über diese genutzt werden kön-
nen. Dazu nutzt man beim Zugriff eine vorangestellte Angabe des Objekts und danach
den Bezeichner wie bei einem „normalen“ Funktionsaufruf (die sogenannte Dot-Nota-
tion oder Punktnotation), was schon von Anfang auch bei der Verwendung von console.
log() zumindest oberflächlich behandelt wurden. Auf weitere Details Methoden und ins-
besondere die Erstellung eigener Methoden in JavaScript kommen wir jedoch bei der
Behandlung von OOP ausführlich zurück, wobei wir auch hier schon einige Dinge be-
sprechen.

7.2 Die Deklaration eigener Funktionen in JavaScript

Es wurde schon thematisiert, dass globale Funktionen als Methoden des globalen Ob-
jekts zu sehen sind (First Class Citizen). Das ist hauptsächlich deshalb von Bedeutung,
weil sie damit in das Objektkonzept selbst passen und zudem überall im Skript verfügbar
sein werden. Allerdings gibt es spezielle Varianten von Funktionen wie innere Funktio-
nen, die nicht im globalen Scope landen. Aber die grundsätzliche Art der Deklaration ist
immer gleich. Sie können in unterschiedlichen Gültigkeitsbereichen eigene Funktionen
in JavaScript deklarieren, indem Sie folgende Syntax notieren:

• Sie beginnen immer mit dem Schlüsselwort function. Sie können in JavaScript im
Gegensatz zu vielen anderen Sprachen wie Java keinen Datentyp für den Rückgabe-
wert angeben. Aber das ist klar, weil Sie grundsätzlich Datentypen in JavaScript nicht
spezifizieren.
• Dem Schlüsselwort function folgt der gewünschte Bezeichner der Funktion (den Sie –
bis auf Sonderzeichen – selbst vollkommen frei auswählen dürfen, der jedoch nicht
7.2 Die Deklaration eigener Funktionen in JavaScript 119

mit einem Schlüsselwort oder reservierten Bezeichner allgemein identisch sein darf).
Nach Konvention werden Bezeichner einer Funktion kleingeschrieben. Damit geben
Sie einen sogenannten Funktionszeiger (auch Funktionsreferenz genannt) an. Der
Funktionsname ist optional, kann aber nur bei sogenannten anonymen Funktionen
weggelassen werden. Diese liefern immer einen Funktionszeiger als Rückgabewert,
über den sie dann verwendet werden können.
• Dann machen Sie mit einer öffnenden runden Klammer weiter.
• Optional folgt eine Parameterliste, welche Variablen bezeichnet, über die Werte an die
Funktion übergeben werden können (bei mehreren Parametern durch Kommata ge-
trennt). Sie können auch hier keine Datentypen für die Parameter angeben. Das sollte
aber klar sein, da Sie ja auch bei normalen Variablendeklarationen keine Datentypen
angeben konnten. Innerhalb der Funktion stehen die Parameter als lokale Variablen
zur Verfügung und die Bezeichner dieser Parameter sind damit nur innerhalb des Be-
reichs der Funktion sichtbar.
• Eine runde, schließende Klammer beendet die sogenannte Funktionsunterschrift
oder Signatur der Funktion.
• Die eigentlichen Anweisungen der Funktion (die sogenannte Implementation oder
Implementierung) werden in nachfolgende geschweifte Klammern eingeschlossen.
Diese geschweiften Klammern bilden also einen Block.
• Mit dem optionalen Schlüsselwort return und einem ebenfalls optionalen Rückgabe-
wert können Sie eine Funktion an jeder Stelle wieder verlassen.

Die Syntax zur Deklaration einer Funktion sieht dementsprechend schematisch so aus:

Beispiel

function [Name]([Parameterliste]) {
…Anweisungen
return [Rückgabewert]; // optional
}◄

 Die Anweisung return sollte – wenn vorhanden – immer die letzte Anweisung
in einer Funktionsimplementierung bzw. dem Programmfluss der Funktion
sein. Wenn der Interpreter diese Anweisung vorfindet, verlässt er unmittel-
bar die Funktion und macht mit dem Code hinter dem Funktionsaufruf wei-
ter. Gegebenenfalls nachfolgend in der Funktionsimplementierung notierte
Anweisungen werden nicht mehr ausgeführt. Diese wären dann sogenannter
unerreichbarer Code (unreachable code) und solcher ist immer (!) ein logi-
scher Fehler im Programm oder Skript. Sie können return allerdings an jeder
Stelle der Funktion in Verbindung mit einer vorangestellten Entscheidungs-
struktur verwenden (ein bedingtes return).
Sie können in JavaScript als Rückgabewert alle Datentypen, aber auch Sondertoken wir
undefined, NaN oder Infinity liefern.
120 7 Funktionen, Prozeduren und Methoden

 Sie können in einer Funktion return auch ohne einen nachgestellten Wert no-
tieren. Dann ist das einfach eine Sprunganweisung, welche die Funktion ver-
lässt, und es wird kein Wert zurückgegeben. Gerade als bedingtes return ist
das in einigen Fällen sinnvoll.

 Funktionen werden bei Webseiten üblicherweise innerhalb des Kopfbereichs


einer Webseite deklariert oder dort über eine externe Datei eingebunden. Be-
achten Sie, dass hier nicht vom Aufruf der Funktion die Rede ist. In der Praxis
finden Sie oft die Verwendung von externen JavaScript-Dateien als Funktions-
bibliotheken. Dies ist ja eine Hauptanwendung für die Bildung von Funktionen.
Dazu verlagern Sie einfach die Funktionsdeklarationen in eine externe Java-
Script-Datei und binden diese in die Webseite ein. Mit einer frühen Einbindung
in die Webseite kann gewährleistet werden, dass die Funktionen (aber auch
Variablen) bereits vor dem Anzeigen einer Webseite vom Browser geladen und
interpretiert wurden. Die Einbindung an der Stelle ist jedoch nicht zwingend.
JavaScript-Funktionen können wie alle JavaScript-Anweisungen an jeder Stelle
in einer HTML-Seite deklariert bzw. eingebunden werden. Es kann dann nur
vorkommen, dass ein Anwender eine Aktion auf der HTML-Seite auslöst und die
dafür notwendige Funktion noch gar nicht geladen wurde. Die Verwendung
von Modulen macht diese Verknüpfung von Ressourcen von der Verbindung
mit einer Webseite und einem Browser oft überflüssig.

7.3 Aufruf einer Funktion

Für den Aufruf einer Funktion muss an zwischen der Verwendung in einer Webseite
und allgemeinen Aufrufen unterscheiden. Wenn Sie eine Webseite verwenden, kön-
nen Sie eine Funktion an jeder Stelle in einer Webseite aufrufen, sofern sie vorher de-
klariert wurde. Bei einer Webseite erfolgt das oft mittels sogenannter Eventhandler, zu
denen wir noch kommen, mit einer Inline-Referenz oder einfach als Anweisung in einem
Skriptbereich. Im letztgenannten Fall rufen wir Funktionen in einem Skript direkt als An-
weisung beim Laden der Webseite oder eines Skripts durch beispielsweise [Link] auf.
Funktionen werden allgemein über den Bezeichner aufgerufen, mit dem sie deklariert
wurden. Wenn die Funktion aufgerufen wird, werden die Anweisungen in dieser Funk-
tion abgearbeitet und eventuelle Ergebnisse über das Schlüsselwort return dem Aufrufer
übergeben. Beachten Sie, dass Sie nur den Funktionsnamen samt Klammern und optio-
naler Werte für die Parameter (nicht die Parameterbezeichner) bei einem Aufruf angeben.

 Die runden Klammern nach dem Bezeichner der Funktion gehören unbedingt
zu einem Aufruf einer Funktion dazu, egal ob Sie darin Werte an die Funk-
tion übergeben oder nicht. Nur bei einer sogenannten Funktionsreferenz (
Abschn. 1.5.3) bzw. einem Callback werden die Klammern fehlen. Das stellt
7.4 Lokale Variablen in Funktionen 121

dann aber auch nur einen Verweis auf die Funktion und keinen Aufruf dar.
Das versehentliche Weglassen der Klammern bei einem geplanten Funktions-
aufruf ist ein tückischer und oft gemachter Fehler.

7.4 Lokale Variablen in Funktionen

Kommen wir noch einmal zum Begriff der lokalen Variablen. Wie gerade erwähnt, sind
Parameter an Funktionen (und auch Methoden) innerhalb des Funktionskörpers lokale
Variablen. Außerhalb des Funktionskörpers sind sie nicht sichtbar. Aber Sie können auch
innerhalb des Funktionskörpers weitere lokale Variablen definieren. Grundsätzlich macht
man das auch sehr oft und das ist eine „Spezialversion“ von blocklokalen Variablen. Das
Verfahren zum Deklarieren einer lokalen Variablen ist einfach. Lokale Variablen werden
innerhalb von Funktionen beziehungsweise vergleichbaren Konstruktionen explizit mit
var oder let deklariert und können dann nur dort benutzt werden. Eventuell außerhalb der
Funktion vorhandene (globale) Variablen gleichen Namens werden temporär verdeckt.
Genaugenommen werden diese zeitweilig auf undefined gesetzt, aber diese Spitzfindig-
keit spielt meist keine Rolle. Ansonsten gelten die üblichen Regeln für var und let, die
auch bei konventionellen Blöcken gelten. Beispiel ([Link]):

"use strict";
var a = "abc"; // globale Variable a
function meineFktion() {
  var a = 42;
  let b = 1;
  [Link]("Wert der lokalen Variablen a: " + a +
  "Der Wert der globalen Variablen ist verdeckt.");
  [Link]("Wert der lokalen Variablen b: " + b);
}
[Link]("Wert der globalen Variablen a: " + a);
meineFktion();
[Link]("Wert der globalen Variablen a: " + a);
[Link]("Wert von b: " + b ); // ACHTUNG – FEHLER

Zuerst wird eine Variable a eingeführt. Dies erfolgt außerhalb einer Funktion und damit
ist sie global im ganzen Skript verfügbar, auch innerhalb jeder Funktion, es sei denn, sie
wird dort verdeckt. Und das machen wir in der Funktion meineFktion() als Erstes. In der
Funktion wird nämlich eine Variable mit gleichem Bezeichner erneut definiert. Damit
ist die globale String-Variable a in dieser Funktion nicht mehr verfügbar und stattdessen
steht die lokale numerische Variable über den Bezeichner a zur Verfügung. Die Ausgabe
in der Funktion beweist das.
122 7 Funktionen, Prozeduren und Methoden

In der Funktion wird noch eine zweite lokale Variable b eingeführt. Auch darauf grei-
fen wir innerhalb der Funktion zu. Die ersten Zugriffe zeigen, dass außerhalb der Funk-
tion meineFktion() die globale String-Variable a verfügbar ist und nicht durch eine Ver-
wendung einer lokalen Variablen gleichen Namens in einer Funktion verändert wird. Die
beiden Bereiche sind strikt getrennt.
Beachten Sie auch die letzte Zeile im Skript. Hier versuchen wir, auf die nur lokal
in der Funktion meineFktion() definierte Variable b zuzugreifen, aber von außerhalb des
lokalen Sichtbarkeitsbereichs. Und das kann nicht gehen. Tatsächlich werden Sie sehen,
dass diese Anweisung keine Ausgabe erzeugt, sondern einen Fehler auswirft. Das ist so-
wohl bei der Deklaration von b mit let als auch var identisch.

 Beachten Sie, dass das Skript vor dem Eintreten eines Fehlers bis zu der Fehler-
stelle ausgeführt wird. Aber eventuell nach der Fehlerstelle notierte Anweisungen
würden nicht mehr ausgeführt. Hier zeigt sich deutlich ein Unterschied zum Com-
piler-Prinzip. Ein Compiler hätte den fehlerhaften Quelltext erst gar nicht über-
setzt. Es ist also möglich, dass fehlerhafte JavaScripts ausgeführt werden, näm-
lich genau so weit, bis der Fehler tatsächlich auftritt. Die Folgen können fatal sein,
denn Sie verlassen sich ja sicherlich darauf, dass ein Skript von Anfang bis Ende
abgearbeitet wird. Aber das Skript kann in so einem Fall seine Aufgabe nur teil-
weise erledigt haben und einen Zustand zurücklassen, der ziemlich unbefriedigend
sein kann. Im Fall von Skripten ist ein richtig gründlicher Test noch wichtiger als
im Compiler-Fall, denn Ihnen stehen nicht die Vorabprüfungen eines Compilers
zur Verfügung.

 Wenn Sie kein "use strict" verwenden, können Sie Variablen ohne var oder let
deklarieren. Selbst wenn Sie dann innerhalb einer Funktion eine neue Variable
ohne var oder let deklarieren, wird diese dem globalen Objekt hinzugefügt.
Die Konsequenzen sind weitreichend und in der Regel nicht gewünscht. Auch
das ist ein Grund, warum man unbedingt "use strict" verwenden sollte.

7.5 Besonderheiten bei JavaScript im Zusammenhang mit


Funktionen

Die Deklaration als auch Verwendung von Funktionen ist bei JavaScript eigentlich ein-
fach. Aber es gibt diverse Besonderheiten zu beachten.
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 123

7.5.1 Funktionen redefinieren – wer zuletzt kommt

Wenn Sie bei der Deklaration einer Variablen mit var arbeiten, ersetzt bei einer mehr-
fachen Deklaration einer Variable in einem Scope die zuletzt deklarierte Version alle vor-
herigen (redek_var.js).

"use strict";
var a = 1;
[Link](a);
var a = true; // Redeklaration mit anderem Wert/Datentyp
[Link](a);

Es gibt keinen Fehler und die Ausgabe ist 1 2. Beim sequenziellen Verarbeiten wird die
Variable verwendet, die in der Sequenz gerade gültig ist. Verwenden Sie let, gibt es einen
Fehler. Nun stellt sich die Frage, was eine mehrfache Deklaration einer Funktion mit
dem identischen Bezeichner im gleichen Scope bewirkt? Beachten Sie das Beispiel (fkt2.
js).

"use strict";
function fkt() {
  [Link](1);
}
fkt();
function fkt() {
  [Link](2);
}
fkt();

Die letzte Deklaration ersetzt die vorherige im gleichen Scope. Interessant ist die Aus-
gabe. Es wird zweimal 2 ausgegeben. Das bedeutet, auch der erste Aufruf nutzt die nach-
folgende Deklaration und ignoriert die erste Deklaration.

7.5.2 Funktionen mit Parametern

Funktionsdeklarationen können Parameter spezifizieren und beim Aufruf einer Funk-


tion können Werte übergeben werden, mit denen in der Funktion gearbeitet werden soll.
Diese Parameter sind in der Funktion lokale Variablen. Es können in JavaScript immer
beliebig viele Parameter beim Funktionsaufruf übergeben werden. Das sehr besondere
Verhalten sehen wir uns genau an. Die Parameter werden bei der Deklaration und beim
Aufruf durch Kommata getrennt in runden Klammern angegeben.
124 7 Funktionen, Prozeduren und Methoden

[Link] Schema einer Funktionsdeklaration mit Parametern


Die Deklaration einer Funktion mit Parameterübergabe hat den folgenden Aufbau:

Beispiel

function funktionsname (Parameter1, Parameter2,…) {


  // JavaScript – Anweisungen
  return rueckgabewert; // optional
}◄

[Link] Funktionsaufrufe – Call-by-Value vs Call-by-Reference


Um eine Funktion aufzurufen, geben Sie den Bezeichner und in den Klammern Werte
für die Parameter an. Die Übergabe der Parameter erfolgt bei primitiven Werten immer
als Wert (Call-by-Value), d. h., Änderungen der lokalen Parameterwerte innerhalb der
Funktion haben keine Auswirkung auf den Wert des übergebenen Parameters außerhalb
der Funktion. Das würde Call-by-Reference bedeuten und das gibt es in JavaScript nur
für Objekte. Das bedeutet, dass primitive Datentypen wie Zahlen und Zeichenfolgen bei
der Übergabe an eine Funktion eine Kopie des Werts erstellen, während Objekte und Ar-
rays eine Referenz auf das ursprüngliche Objekt übergeben und sich Änderungen inner-
halb der Funktion am Objekt außerhalb auswirken ([Link]).

"use strict";
let num = 10; // Primitive Datentypen (Call-by-Value)
function fkt(value) {
  value = 20;
}
fkt(num);
[Link](num); // Ausgabe: 10
let obj = [10]; // Objekte (Call-by-Reference)
function fktObj(ref) {
  ref[0] = 20;
}
fktObj(obj);
[Link](obj[0]); // Ausgabe: 20

In diesem Beispiel bleibt der Wert der Variablen num unverändert, nachdem die Funktion
fkt aufgerufen wurde, da der Wert nur kopiert und an die Funktion übergeben wird. Im
Gegensatz dazu ändert sich der Wert des ersten Elements in dem Array obj, da die Funk-
tion fktObj eine Referenz auf das ursprüngliche Objekt erhält und somit Änderungen
daran vorgenommen werden können. Das zeigt sich auch mit anderen Objekten, wenn
man deren Eigenschaften so ändert.
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 125

[Link] Flexible Anzahl an Funktionsargumenten und der Token


arguments
Wie Sie gesehen haben, können Sie beliebig Übergabewerte bzw. Parameter an eine
Funktion übergeben. Normalerweise geben Sie dazu bei der Deklaration der Funktion
die gewünschten Parameter an, indem Sie dafür deren Anzahl und Namen spezifizieren.
Diese Namen sind dann die Bezeichner für besagte lokale Variablen in der Funktion,
über die die übergebenen Werte verfügbar sind. Und die Anzahl der notwendigen Argu-
mente beim Aufruf der Funktion ist durch die Anzahl der Parameter in der Deklaration
spezifiziert. Für den Aufruf einer Funktion ist jedoch ausschließlich der Bezeichner (die
Funktionsreferenz) relevant, nicht aber die Parameterliste. Das bedeutet, dass man bei
einem Funktionsaufruf nicht die gleiche Anzahl an Parametern angeben muss, wie bei
der Funktionsdeklaration notiert wurden. Es gibt in JavaScript jedoch zusätzlich inner-
halb jeder Funktion und auch Methode eine spezielle implizite Variable mit Namen argu-
ments. Diese ist immer verfügbar und enthält spezifische Informationen über die an die
Funktion übergebenen Argumente, insbesondere über die Anzahl, denn die Argumente
stellen ein Datenfeld dar und wie bei jedem automatisch numerisch indizierten Array
gibt es eine Eigenschaft length. Wir nutzen im folgenden Beispiel ([Link]) eine Schleife,
um über die Einträge im Datenfeld zu iterieren.

Für Leser mit objektorientiertem Hintergrund – mithilfe des Tokens arguments realisiert Java-
Script ein polymorphes (vielgestaltiges) Verhalten in Hinsicht auf Funktionen oder Methoden. Eine
echte Polymorphie mittels Overload gibt es in JavaScript nicht, denn für die Identifizierung einer
Funktion wird ausschließlich der Bezeichner der Funktion verwendet. Die Parameter spielen keine
Rolle. Aber man kann bei einer internen Verwendung von arguments in der Tat eine Funktion so
programmieren, dass man im Sinn der OOP von Polymorphie sprechen kann – insbesondere, wenn
Sie dann noch beachten, dass die Datentypen der Argumente natürlich ebenfalls flexibel sind.

Schauen wir uns ein Beispiel an mit Informationen über Parameter an eine Funktion via
arguments.

"use strict";
function fkt() {
  var ausgabe = "Anzahl der Argumente: " + [Link];
  for (var i = 0; i < [Link]; i++) {
      ausgabe += "\nArgument " + (i + 1) + ": " + arguments[i] +
      "\nTyp des Parameters: " + typeof arguments[i];
     };
     [Link](ausgabe);
}
fkt();
fkt(new Date());
fkt('Die','Antwort','ist','42');
126 7 Funktionen, Prozeduren und Methoden

fkt(1,2,3,5,7,11,13,17,19);
fkt(1,"Text",new Date(),3.14);

Sie sehen die Deklaration einer Funktion, bei der kein Parameter spezifiziert ist. Im Inne-
ren setzen wir eine lokale Variable ausgabe zusammen. Über [Link] wird die
Anzahl der Argumente bestimmt und der lokalen Variable hinzugefügt. In der Schleife
iterieren wir über alle Parameter, die an die Funktion übergeben werden. Dies wird da-
rüber gewährleistet, dass wir als Abbruchbedingung der Schleife die Anzahl der Argu-
mente verwenden, und das wieder über [Link]. Im Inneren der Schleife geben
wir den Inhalt des jeweiligen Parameters aus, und zwar mit arguments[i]. In der Funk-
tion sehen Sie auch den Ausdruck typeof arguments[i]. Mit dem Operator typeof be-
stimmen wir den Datentyp des Parameters.
Zum Aufruf der Funktion verwenden wir nun verschiedene Varianten mit einer unter-
schiedlichen Anzahl an Parametern und unterschiedlichen Parametertypen. Zuerst wird
die Funktion ganz ohne Parameter aufgerufen. Da die Anzahl der Argumente 0 ist, wird
die Schleife im Inneren der Funktion gar nicht durchlaufen, da die Bedingung für eine
Ausführung schon beim Erreichen der Schleife nicht erfüllt ist. Die zweite Version über-
gibt genau ein Argument an die Funktion ein Datumsobjekt. In den folgenden Aufrufen
sehen Sie, dass Sie auch mehrere Argumente übergeben können.

[Link] Mehr oder wenige Argumente


In Zusammenhang mit dem geraden behandelten Thema und dem Token arguments stellt
sich die Frage, wie sich JavaScript verhält, wenn man eine feste Anzahl an Parametern
bei einer Funktion spezifiziert und mehr oder weniger Argumente beim Aufruf angibt? Im
Grunde haben wir das schon behandelt, aber ich möchte einen anderen Blickwinkel wäh-
len. Schauen wir uns zuerst eine entsprechende Modifikation des letzten Beispiels an, wo
die Anzahl der Aufrufargumente nicht mit der Deklaration zusammenpasst ([Link]).

"use strict";
function fkt(a, b) {
  var ausgabe = "Anzahl der Argumente: " + [Link];
  for (var i = 0; i < [Link]; i++) {
      ausgabe +="\nArgument " + (i + 1) + ": " + arguments[i];
     };
     ausgabe += "\nWert von a: " + a + "\nWert von b: " + b;
     [Link](ausgabe);
}
fkt(); //Aufruf ohne Argumente
fkt(new Date()) // Aufruf mit einem Argument - Argument 2 fehlt
fkt(1,2,3,5); // Aufruf mit zu vielen Argumenten

Die in dem Beispiel deklarierte Funktion hat zwei Parameter. Die Bezeichner a und b
stehen als lokale Variablen in der Funktion zur Verfügung. Wie ist nun das Verhältnis
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 127

von arguments zu den explizit bezeichneten Parametern? Die Antwort liefert die Logik
der Funktion, in der wir sowohl arguments als auch a und b explizit verwenden. Die drei
verschiedenen Aufrufe demonstrieren zwei potenziell „falsche“ Aufrufsituationen. Wir
verwenden drei Aufrufsituationen, um das Verhältnis der explizit angegebenen Parameter
zu arguments besser zu verdeutlichen.

• Zuerst wird die Funktion ohne Argumente aufgerufen. Die Länge von arguments ist
damit also 0 und die beiden Variablen a und b haben den Wert undefined.
• Nach deutlicher wird das Verhältnis von explizit spezifizierten Parametern und argu-
ments beim zweiten Aufruf mit einem Argument. Die Werte von a und arguments[0]
sind identisch. Der Wert von b ist wieder undefined.
• Aufruf Nummer 3 macht verschiedene Dinge jetzt komplett offensichtlich:
– Die explizit angegebenen Parameter und die Werte im Datenfeld arguments be-
zeichnen sequenziell jeweils das gleiche Argument, sofern Parameter explizit
spezifiziert sind.
– Sie können arguments in einer Funktion immer verwenden, egal, ob Sie Parameter
bei einer Funktion deklarieren oder nicht.
– Deklarieren Sie mehr Parameter, als Sie beim Aufruf Argumente angeben, haben
die Bezeichner für die nicht initialisierten Parameter den Wert undefined.
– Werte, für es keine Bezeichner mehr gibt, werden dennoch in dem Übergabe-Array
bereitstehen und über den passenden Index verfügbar.

[Link] Anzahl der Parameter kontrollieren


Oben im Buch wurde ja bereits angedeutet, dass JavaScript zwar nicht im klassischen
OOP-Sinn polymorph ist, man aber bei Funktionen ein polymorphes Verhalten selbst
bereitstellen kann. Die Mittel sind arguments, gegebenenfalls typeof und das gezielte
Kontrollieren jedes Übergabeparameters. Schematisch können Sie also so etwas machen:

Beispiel

function fkt() {
  if ([Link] =  = 0) { // spezifische Reaktion auf keinen Parameter
  } else if ([Link] =  = 1) { // spezifische Reaktion auf einen Parameter
    }
  } else if ([Link] =  = [Maximale Anzahl Parameter]) { // spezifische Re-
aktion
  } else { // Default-Reaktion
  }
  } ◄

Wenn Sie dann noch mit typeof gezielt einen Datentyp beachten, haben Sie wirklich ein
polymorphes Verhalten analog Overload.
128 7 Funktionen, Prozeduren und Methoden

[Link] Mehrfache Deklaration einer Funktion mit dem identischen


Bezeichner im gleichen Scope
Wir hatten oben gesehen, dass bei einer mehrfachen Deklaration einer Funktion mit dem
identischen Bezeichner im gleichen Scope die letzte Deklaration die vorherige ersetzt.
Doch was ist, wenn die Funktionen mit unterschiedlicher Parameterliste deklariert wer-
den ([Link])?

"use strict";
function fkt(a) {
  [Link](a);
}
fkt();
function fkt(b) {
  [Link](1 + b);
}
fkt();
function fkt() {
  [Link]("Ohne Parameter");
}
fkt();
fkt(1);
fkt();

Die Antwort bleibt gleich – zuletzt gewinnt. Die zuletzt deklarierte Funktion ersetzt alle
im gleichen Scope bereits deklarierten Funktionen. Weder Anzahl noch Typ der Para-
meter ist relevant. Beim Aufruf ausschließlich der Bezeichner einer Funktion von Be-
deutung, der eine Funktionsreferenz darstellt. Das Verhalten ist auf den ersten Blick
möglicherweise überraschend bis unlogisch, aber es ist im Gegenteil zwangsläufig
und bei genauerem Blick einfach und effizient. Denn Funktionsbezeichner sind Werte
mit Speicheradressen und die mehrfache Deklaration entspricht dem Deklarieren einer
Variablen mit einer Initialisierung durch die Speicheradresse der ersten Funktions-
deklaration. Jede weitere Deklaration weist der vorhandenen Variablen bloß die aktuelle
Speicheradresse als neuen Wert zu. Genaugenommen haben wir im Hintergrund Objekte
und die sind nichts weiter als dynamische Listen. Selbst „normale“ Funktionen sind First
Class Citizen des globalen Objekts.

7.5.3 Funktionsreferenzen

Im Gegensatz zu einem Funktionsaufruf werden bei einer Funktionsreferenz erst ein-


mal rein formal die Klammern fehlen. Eine solche Funktionsreferenz stellt dann aber
auch nur einen Verweis auf die Funktion und keinen Aufruf dar. Nun stellt sich jedoch
die Frage, wann die referenzierte Funktion denn ausgeführt wird? Funktionsreferenzen
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 129

werden bei Webseiten in der Regel an JavaScript-Eventhandler gekoppelt, die bei einem
bestimmten Ereignis die referenzierte Funktion aufrufen. Das bedeutet, wir werden aus-
nahmsweise mal wieder eine HTML-Datei nutzen ([Link]):


    <script>
      "use strict";
      function mFktion() {
        alert("Wer einen Fehler gemacht hat und ihn nicht korrigiert,"
          + "\nbegeht einen zweiten.");
        }
    </script>
  </head>
  <body><h1>Verwendung von Funktionsreferenzen</h1>
    <button id="b1">Klick</button></body>
  <script>
     "use strict";
     [Link]("b1").onclick = mFktion;
  </script></html>

In dem Beispiel wird im Header eine einfache Funktion deklariert. Wichtig ist die Zeile
im zweiten Skriptcontainer. Hier sehen Sie die Funktionsreferenz, die an einen Event-
handler gekoppelt wird.
Mit [Link]("b1") sprechen wir die Schaltfläche in der Webseite
an. Der mit einem Punkt nachgestellte Token onclick ist ein besagter Eventhandler. Wie
der Bezeichner schon aussagt, wird dieser bei einem Klick aufgerufen. Der gesamte
Ausdruck [Link]("b1").onclick bedeutet also übersetzt, dass auf
den Klick auf die Schaltfläche durch den Anwender reagiert werden soll. Und der zu-
gewiesene Wert ist eben ein Verweis auf eine aufzurufende Funktion, die ein alert-Fenster
öffnet (Abb. 7.1) – eine Funktionsreferenz. Beachten Sie die fehlenden Klammern, die an

Abb. 7.1 Die Funktion wurde aufgerufen


130 7 Funktionen, Prozeduren und Methoden

der Stelle unbedingt unterbleiben müssen. Es handelt sich um eine Wertzuweisung wie
bei einer anderen Variablen.

Beachten Sie, dass in dem Beispiel der Skriptcontainer mit der Funktionsreferenz hinter dem Kör-
per der Webseite notiert ist. Das ist kein Zufall oder gar Fehler, sondern für einige Browser not-
wendig. Der Skriptbereich muss in unserem Beispiel auf jeden Fall in der Webseite hinter dem
Element stehen, auf das zugegriffen wird. Andernfalls funktioniert die Zuordnung zur Schaltfläche
nicht. Wenn der Browser die Zeile mit der HTML-Beschreibung der Schaltfläche noch nicht ver-
arbeitet hat, läuft die Anweisung [Link]("b1") ins Leere und der Browser mel-
det einen Fehler. Wir gehen noch genauer auf dieses Thema ein.

Besprechen wir nun genauer, wie Funktionsreferenzen direkt in JavaScript verwendet


werden können. Zuerst einmal ein einfacher Weg ([Link]).

"use strict";
function fkt() {
     [Link]("Ohne Parameter");
}
var alias = fkt;
alias();

• Die Funktion fkt wird wie üblich deklariert.


• Dann wird eine Variable alias erstellt und ihr wird die Funktion fkt zugewiesen. In
JavaScript ist es möglich, Funktionen wie Variablen zu behandeln, sodass die Variable
alias nun auf die gleiche Funktion wie fkt zeigt – eine Funktionsreferenz.
• Schließlich wird die Funktion über ihre Variable alias aufgerufen, indem alias() auf-
gerufen wird. Das ist dann einfach eine Alternative zu dem Aufruf über fkt().

[Link] Parameterübergabe bei Funktionsreferenzen


Bei Funktionsaufrufen übergeben Sie Argumente, indem Sie die Werte für die Para-
meter in die Klammern der Funktion schreiben. Bei Funktionsreferenzen haben Sie aber
explizit kein Klammernpaar, in das Sie Argumente notieren können. Sie können bei

Abb. 7.2 Ein Dialogfenster wurde geöffnet und nach dem Bestätigen ist der Rest der Webseite zu
sehen (Abb. 7.2)
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 131

Funktionsreferenzen also gar keine Argumente für die Parameter angeben! Was also tun?
Die Antwort führt zum Beispiel zu sogenannten anonymen Funktionen (oder genauer –
die direkte Deklaration einer Funktion bei einer Zuweisung), Callbacks und Lambda-
Ausdrücken. Dort werden wir das Problem gleich lösen.

7.5.4 Benannte versus anonyme Funktionen

Bei der Deklaration einer Funktion kann der Funktionsname in einigen Situationen
entfallen. Man redet dann von einer anonymen Funktion (im Gegensatz zu einer be-
nannten Funktion, wie wir sie bisher immer verwendet haben). Eine anonyme Funktions-
deklaration ist dann sinnvoll, wenn sie nur an dieser Stelle verfügbar sein muss. Dann
braucht man auch keinen Funktionsbezeichner. Besonders interessant ist, dass eine sol-
che anonyme Funktion einer Variablen zugewiesen oder an eine Methode weitergereicht
werden kann. Und diese Zuweisung zu einer Variablen ist genau im Fall einer Funktions-
referenz gegeben. Eine anonyme Funktion liefert immer einen Zeiger auf sich selbst zu-
rück.

 Anonyme Funktionen und deren Verwandte wie Lambda-Ausdrücke und Callbacks


sind in modernem JavaScript Dreh- und Angelpunkt. Gerade JavaScript-Frame-
works nutzen so gut wie ausschließlich diese Spezialvarianten von Funktionen.
Insbesondere bei der Ereignisbehandlung. Anonyme Funktionen & Co sind auch
der Schlüssel zur objektorientierten Programmierung in JavaScript.

Wandeln wir die beiden letzten Beispiele einmal ab ([Link]).


      [Link]("b1").onclick = function() {
         alert("Wer einen Fehler gemacht hat und ihn nicht korrigiert," +
          "\nbegeht einen zweiten.");
      };
  </script></html>

In dem Skript sehen Sie, wie eine anonyme Funktion als Funktionsreferenz einem Java-
Script-Eventhandler zugewiesen wird. In den Klammern können Sie wie üblich bei Be-
darf Parameter deklarieren. Natürlich können Sie im Inneren einer so referenzierten an-
onymen Funktion beliebige weitere Funktionen aufrufen und diesen Aufrufen kann man
ja Argumente mitgeben. Sie sehen hier also, wie man im Zusammenhang mit Funktions-
referenzen Argumente angeben kann.

 Üblicherweise wird man im Inneren der anonymen Funktion bei Bedarf eine
Funktionmit Parametern aufrufen.
132 7 Funktionen, Prozeduren und Methoden

7.5.5 Hoisting bei Funktionen

Wie wir bei Variablen besprochen haben, muss man bei der Deklaration auf das Hoisting
aufpassen, bei dem Variablendeklarationen während der Kompilierungsphase an den An-
fang ihres jeweiligen Gültigkeitsbereichs verschoben werden, bevor der Code tatsächlich
ausgeführt wird. Das bedeutet, dass Variablendeklarationen unabhängig davon, wo sie
innerhalb eines Gültigkeitsbereichs deklariert sind, so behandelt werden, als ob sie am
Anfang des Gültigkeitsbereichs stehen. Beispiel:

Beispiel

[Link](x); // undefined
var x = 5; ◄

In dem obigen Code wird kein Fehler erzeugt, obwohl x erst nach der Ausgabe mit
[Link] deklariert wird. Die Deklaration var x wird an den Anfang des Gültigkeits-
bereichs verschoben, aber die Wertzuweisung erfolgt erst nach der Ausgabe. Im Fall von
Funktionen gibt es ebenso Hoisting. Der folgende Code funktioniert, obwohl die Funk-
tion aufgerufen wird, bevor sie deklariert wird. Die Funktionsdeklaration wird an den
Anfang des Gültigkeitsbereichs verschoben.

Beispiel

f(); // "Hallo Welt!"


function f() {
  [Link]("Hallo Welt!");
}◄

Bei Funktionsausdrücken dagegen wird nur die Variablendeklaration gehoistet, nicht


aber die Funktionszuweisung und das führt zu einem Fehler:

Beispiel

f(); // TypeError: f is no Function


var f = function() {
  [Link]("Hallo Welt!");
}; ◄

In diesem Fall wird var f gehoistet, aber die Zuweisung von function() {…} an f nicht,
was dazu führt, dass f undefined ist, wenn der Aufruf ausgeführt werden soll.
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 133

7.5.6 Innere Funktionen – Closures

JavaScript erlaubt auch den Aufbau verschachtelter Funktionen. Dabei wird eine
Funktionsdeklaration direkt in eine andere Funktion hinein notiert! In dem Fall gelten
einige spezielle Regeln:

• Die innere verschachtelte Funktion kann nur über die äußere aufgerufen werden und
ist für direkte Zugriffe unzugänglich!
• Die innere Funktion kann die Variablen oder andere innere Funktionen der äußeren
Funktion verwenden.
• Die äußere Funktion hat keinen Zugang zu den lokalen Variablen der inneren Funktion.
• Die innere Funktion wird als lokale Variable der äußeren Funktion angesehen!

Zur Laufzeit erzeugt der JavaScript-Interpreter beim Aufruf der äußeren Funktion den
Code der inneren Funktion. Dabei entsteht ein sogenanntes Closure (Einschluss) der in-
neren Funktion – das ist der Code der Funktion und eine Referenz auf alle Variablen, die
von der inneren Funktion benötigt werden. Ein Closure kombiniert den Programmcode
mit der lexikalischen Umgebung – d. h., das Closure „merkt“ sich die Umgebung, in der
es erzeugt wurde. Beispiel ([Link]):

"use strict";
function aussen() {
  var a = 21;
  [Link]("Die Antwort ist ");
  function innen1() {
      innen2();
      [Link](a);
  }
  function innen2() {
      a *= 2;
  }
  innen1();
  [Link]("!");
}
aussen();

• In dem Skript wird eine Funktion aussen() definiert, die direkt nach der Deklaration
aufgerufen wird. Damit wird ein statischer Text ausgegeben.
• In der Funktion aussen() gibt es eine lokale Variable a, die mit dem Wert 21 initiali-
siert wird. Dazu finden Sie zwei innere Funktionen innen1() und innen2().
• Die innere Funktion innen2() verdoppelt den Wert der Variable a. Es wird also auf
eine lokale Variable a aus dem Geltungsbereich der äußeren Funktion zugegriffen.
134 7 Funktionen, Prozeduren und Methoden

• Auch in der inneren Funktion innen1() wird auf die lokale Variable a aus dem
Geltungsbereich der äußeren Funktion zugegriffen. Zusätzlich wird aber davor die an-
dere innere Funktion innen2() aufgerufen.
• Der Aufruf der inneren Funktion innen1() erfolgt im Geltungsbereich der äußeren
Funktion, womit also auch indirekt innen2() aufgerufen wird.

Mit Closures kann man in JavaScript das Konzept der Datenkapselung unterstützen, das im Rah-
men der objektorientierten Programmierung zu den Grundprinzipien zählt. Innere Funktionen wer-
den auch gerne als anonyme Funktionen erstellt. Das sehen wir im Abschnitt zu Callbacks.

7.5.7 Lambda-Ausdrücke

In vielen Programmiersprachen haben sogenannte Lambda-Ausdrücke Einzug ge-


halten, die in direktem Bezug zu Funktionen stehen, deren Deklaration aber oftmals ver-
einfachen. Ein Lambda-Ausdruck ist wie eine anonyme Funktion ebenso eine Art Funk-
tion ohne einen eigenen Namen. Die Lambda-Notation ist auch als Pfeilfunktionen oder
Arrow Functions bekannt.

In vielen Programmiersprachen können Lambda-Ausdrücke verwendet werden, um Funktionen


in Form von Funktionsreferenzen als Argumente an andere Funktionen oder Methoden zu über-
geben oder um Funktionen innerhalb anderer Funktionen zu definieren. Lambda-Ausdrücke sind
besonders nützlich in funktionalen Programmiersprachen, in denen Funktionen als „First-Class Ci-
tizens“ betrachtet werden. Das sind Objekte, die an andere Funktionen übergeben oder von ihnen
zurückgegeben werden können. Man muss dabei auch festhalten, dass innere Funktionen/Metho-
den in vielen Programmiersprachen nicht möglich waren und diese durch die Effizienz bei Java-
Script mittels anonymer Funktionen abgehängt wurden. Lambda-Ausdrücke haben diesen Spra-
chen dann quasi „den Hals gerettet“.

Obwohl es im Detail in einigen Programmiersprachen Unterschiede zwischen Lambda-


Ausdrücken und anonymen Funktionen gibt (nicht nur von der Syntax), sind beide
Techniken oft gleich oder zumindest von der Wirkung weitgehend identisch. Statt einen
Funktionsnamen zu verwenden, wird ein Lambda-Ausdruck (wie eine anonyme Funk-
tion) ohne Bezeichner notiert. Ein Lambda-Ausdruck kann ein oder mehrere Argumente
(Parameter) akzeptieren und einen Rückgabewert liefern. Allerdings ist die Funktionali-
tät eines Lambda-Ausdrucks in der Regel sehr einfach. Beispielsweise wird nur ein Aus-
druck für eine Berechnung formuliert und das Ergebnis als Rückgabewert geliefert. Für
komplexere Aufgaben eignen sich Lambda-Ausdrücke in der Regel nicht. Sie haben aber
andere Stärken.

[Link] Konkrete Umsetzung in JavaScript

• In JavaScript beginnen Sie einen Lambda-Ausdruck nicht mit einem spezifischen


Schlüsselwort, wie in vielen anderen Sprachen. Stattdessen wird der Pfeiloperator
=> verwendet.
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 135

• Es darf kein Zeilenumbruch zwischen Parametern und dem Pfeiloperator stehen.


• Lambda-Ausdrücke (Pfeilfunktionen) können entweder einen „knappen“ oder einen
gewöhnlichen Funktionskörper oder Blockkörper haben.

Die nachfolgenden Beispielcodes für ein paar Lambda-Ausdrücke in JavaScript sollen


das Konzept deutlich machen. Beispiel ([Link]):

"use strict";
var lambda1 = () => 4;
var lambda2 = () => {
      return 4;
}
var lambda3 = _ => 4;
[Link](lambda1()); // Aufruf der 1. Lambda-Funktion
[Link](lambda2()); // Aufruf der 2. Lambda-Funktion
[Link](lambda3()); // Aufruf der 3. Lambda-Funktion

Die Lambda-Ausdrücke in dem Beispiel zeigen Deklarationen ohne Parameter. Da ein


Lambda-Ausdruck eine Funktionsreferenz zurückgibt, kann man diese einer Variablen
zuweisen. Der Aufruf erfolgt dann über den Namen der Variablen, gefolgt von einem
Klammernpaar. Das ist dann wie ein gewöhnlicher Funktionsaufruf zu sehen und analog
wie bei anonymen Funktionen.

• Die erste Deklaration eines Lambda-Ausdrucks zeigt eine einfache Deklaration ohne
Parameter mit Zuweisung zu einer Variablen und Rückgabe von einem Literal. Dies
ist die Verwendung der Notation eines „knappen“ Funktionskörpers. In einem knap-
pen Funktionskörper ist lediglich ein Ausdruck nötig, und eine implizite Rückgabe
wird automatisch angehängt. Die Parameterliste für eine parameterlose Funktion
muss mit einem Klammernpaar deutlich gemacht werden.
• Die zweite Deklaration eines Lambda-Ausdrucks ohne Parameter mit Zuweisung zu
einer Variablen und Rückgabe von einem Literal zeigt die explizite Verwendung von
return. Der Körper eines Lambda-Ausdrucks kann in geschweifte Klammern gesetzt
werden (gewöhnlicher Funktionskörper) und muss es auch, wenn dort nicht nur ein-
fache Ausdrücke stehen (etwa explizites return). In einem Blockkörper muss dann
aber auch eine explizite Rückgabe-Anweisung verwendet werden.
• Variante 3 ist eine alternative Deklaration eines Lambda-Ausdrucks ohne Parameter
mit _ statt leerer Klammern für den Parameterausdruck.

Die Lambda-Ausdrücke in dem Beispiel zeigen die Deklaration mit einem Parameter
([Link]):

"use strict";
var lambda1 = x => x * x;
136 7 Funktionen, Prozeduren und Methoden

var lambda2 = (x) => x * x;


[Link](lambda1(11)); // Aufruf der 1. Lambda-Funktion
[Link](lambda2(11)); // Aufruf der 2. Lambda-Funktion

• Die erste Deklaration eines Lambda-Ausdrucks zeigt die Verwendung eines nicht-
geklammerten Übergabewerts und die Zuweisung zu einer Variablen sowie Rückgabe
eines berechneten Werts (Multiplikation des übergebenen Werts mit sich selbst).
• Die zweite Deklaration eines Lambda-Ausdrucks ist fast identisch, nur steht der
Übergabewert in Klammern.

Das dritte Beispiel ([Link]) zeigt die Verwendung mehrerer Parameter.

"use strict";
var lambda1 = (x, y) => x * y;
[Link](lambda1(11, 2)); // Aufruf der Lambda-Funktion

Hier sehen Sie die Deklaration eines Lambda-Ausdrucks mit mehreren Übergabewerten.
Diese kann man dann im Ausdruck verwenden. Wichtig – bei mehreren Parametern müs-
sen diese geklammert werden. Die Lambda-Notation bietet also oftmals eine kürzere und
kompaktere Syntax für die Definition von Funktionen, was den Code lesbarer und weni-
ger fehleranfällig machen kann, insbesondere für kurze und einfache Funktionen.

7.5.8 Callbacks

Zum Abschluss dieses Kontextes von Funktionen soll noch ein wichtiger Begriff ge-
nauer besprochen werden, der von erheblicher Bedeutung ist und auch klärt, wie man
Funktionsreferenzen und anonyme Funktionen als auch die Lambda-Notation direkt aus
JavaScript allgemein nutzen kann.

 Unter einer Callback-Funktion oder kurz einem Callback ist allgemein eine Java-
Script-Funktion zu verstehen, die einen Zeiger auf sich liefert. Damit sind Funktions-
referenzen, anonyme Funktionen als auch Lambda-Ausdrücke per se Callbacks. Aber
nicht jede Callback-Funktion muss eine dieser Varianten sein.

Grundsätzlich kann man Callbacks einer Methode oder Funktion oder auch Iteratoren als
Argument oder Option übergeben. Neben den schon konzeptionell als Callbacks zu ver-
stehenden anonymen Funktionen oder Lambda-Ausdrücken gibt es auch die Möglich-
keit, eine benannte Callback-Funktion zu deklarieren. In allen sinnvoll nutzbaren Situa-
tionen wird sie als Rückgabewert eine Funktionsreferenz liefern. Betrachten Sie folgen-
des Beispiel ([Link]):
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 137

"use strict";
function getText(text) {
  return function() {
      mFktion(text);
  };
}

Betrachten wir die Deklaration der Callback-Funktion getText(). Was wird denn von der
Funktion zurückgegeben? Die Referenz auf function(){ meineFktion(text);}. Dies ist
eine anonyme, innere Funktion (ein Closure) und die liefert einen Zeiger auf die Funk-
tion – wie gewünscht und notwendig. Aber damit haben wir bei einem flüchtigen Blick
doch scheinbar exakt die Situation vorliegen, als ob wir eine anonyme Funktion hin-
geschrieben hätten. Sie können sich dann natürlich fragen, wozu man überhaupt diese
benannten Callback-Funktionen benutzt beziehungsweise warum man eine solche Tech-
nik unterstützt? Man kann sie wiederverwenden, weil man ja im Grunde eine „normale“
Funktion deklariert. Auch kann es interessant sind, wenn man eine komplexere Logik
baut und/oder verschiedene return-Anweisungen liefern möchte. Man hat zudem eben
auch explizit einen konventionellen Funktionsaufruf und bekommt einen Funktions-
zeiger. Übergabeparameter lassen sich so manchmal einfacher verwenden. Dennoch
haben benannte Callbacks in JavaScript etwas an Bedeutung verloren. Insbesondere
Lambda-Ausdrücke sind oft einfacher und schneller notiert.

7.5.9 JavaScript-Generatoren

EinJavaScript-Generator ist eine Funktion, die in der Lage ist, ihre Ausführung zu pau-
sieren und Werte Schritt für Schritt zu generieren, anstatt sie alle auf einmal zurück-
zugeben. Dabei wird das Schlüsselwort yield verwendet und bei der Deklaration dem
Schlüsselwort function ein Stern nachgestellt. Der *-Operator zeigt damit explizit an,
dass es sich um eine Generatorfunktion handelt, und ermöglicht damit erst die Ver-
wendung des yield-Schlüsselworts innerhalb der Funktion. Hier ist ein einfaches Beispiel
für einen JavaScript-Generator ([Link]):

"use strict";
function* zahlenGenerator() {
  yield 1;
  yield 2;
  yield 3;
}
const generator = zahlenGenerator();
[Link]([Link]().value); // Gibt 1 aus
138 7 Funktionen, Prozeduren und Methoden

[Link]([Link]().value); // Gibt 2 aus


[Link]([Link]().value); // Gibt 3 aus
[Link]([Link]().value); // Gibt undefined aus

In diesem Beispiel definiert die Funktion zahlenGenerator einen Generator, welche die
Zahlen 1, 2 und 3 zurückgibt und dabei das yield-Schlüsselwort verwendet. Wenn der
Generator mit der Methode next() aufgerufen wird, pausiert er seine Ausführung beim
ersten yield und gibt den Wert zurück. Bei jedem weiteren Aufruf von next() wird die
Ausführung fortgesetzt, bis alle yield-Statements abgearbeitet sind. Danach wird { value:
undefined, done: true} zurückgegeben, was anzeigt, dass der Generator keine weiteren
Werte erzeugen kann.

7.5.10 Weitere Möglichkeiten für die Deklaration von Funktionen

Bei der Deklaration von Funktionen in JavaScript gibt es noch einige weitere Features
und Besonderheiten, die Sie kennen sollten.

[Link] Bedingte Deklaration
Eine Funktionsdeklaration kann von einer Bedingung abhängig gemacht werden. Auf
diese Weise können Sie z. B. Funktionen mit gleichen Namen, aber unterschiedlichen
Funktionalitäten definieren ([Link]).

// Einbinden des 'readline' Moduls von [Link]


const readline = require('readline');
// Konfigurieren des readline interfaces
const rl = [Link]({
  input: [Link], output: [Link]
});
// Frage stellen und auf Benutzereingabe warten
[Link]('Was soll getan werden? ', (name) => {
  if(name == "Addition") {
      function operation(z1, z2) { return z1 + z2; }
  }
  else {
      function operation(z1, z2) { return z1 - z2; }
  }
  [Link](operation(10, 11));

// readline interface schließen

  [Link]();
  });
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 139

Die Wirkungsweise der Funktion operation wird vom Inhalt der Variablen name ab-
hängig gemacht, die wieder einmal aufgrund einer interaktiven Benutzereingabe in
[Link] erfolgt, um das Beispiel etwas interessanter zu machen. Die eigentliche be-
dingte Deklaration ist auf den if-else-Block eingeschränkt. Beachten Sie, dass wir hier
explizit keinen strikten Modus verwenden, denn der Funktionsbezeichner wird dyna-
misch angelegt und er entspricht damit einer bis dahin nicht vorhanden Variable, die
ohne var oder let angelegt wird. Das Problem kann man zwar durchaus lösen, denn im
Hintergrund wird mit einem Function-Konstruktor gearbeitet, aber die Möglichkeit, die
Funktionsdeklaration von Bedingungen abhängig zu machen (ab JavaScript 1.5 verfüg-
bar), wird in der Praxis selten eingesetzt.

[Link] Vorgabewert von Parametern


Bei einer Funktionsdeklaration kann Parametern bereits einen Vorgabewert zuweisen
(Vorgabeparameter oder auch Defaultparameter). Wenn der Parameter beim Aufruf nicht
gesetzt wird, wird der Vorgabewert verwendet. Beachten Sie, dass Vorgabeparameter nur
am Ende der Parameterliste stehen können. Beispiel: [Link].

"use strict";
function multi(a, b = 1) {
  return a * b;
}
[Link](multi(4, 5))
[Link](multi(4))

Wenn der zweite Parameter beim Aufruf der Funktion nicht angegeben wird, wird dafür
der Wert 1 verwendet.

[Link] Callbacks in Verbindung mit forEach


Mit forEach gibt es in JavaScript eine Iteratormethode von Datenstrukturen, die einen
Callback auf jedes Element anwendet. Hier ist ein einfaches Beispiel, das zeigt, wie fo-
rEach in Verbindung mit Callbacks auf ein Array oder eine andere iterierbare Struktur
angewendet werden kann ([Link]).

"use strict";
var colors = ["rot", "gruen", "blau"];
[Link](function(color, index) {
      [Link]("Farbe an Index " + index + ": " + color);
});

In diesem Beispiel wird auf dem Array colors die Callback-Funktion wird für jedes Ele-
ment im Array ausgeführt. Die Argumente in der Callback-Funktion entsprechen dem
aktuellen Wert und Index des Elements während der Iteration.
140 7 Funktionen, Prozeduren und Methoden

7.5.11 Rekursive Funktionsaufrufe

JavaScript unterstützt sogenannte Selbstaufrufe. Dies nennt man auch Rekursion oder
einen rekursiven Aufruf. Rekursive Aufrufe haben eine gewisse Ähnlichkeit mit Schlei-
fen, da auch damit Anweisungen wiederholt werden. Beachten Sie aber, dass rekur-
sive Aufrufe zwar ähnlich wie Schleifen funktionieren, aber die rekursiven Funktions-
aufrufe intern auf dem sogenannten Stack (Stapel) des Rechners abgelegt und von dort
wieder genommen werden, während Schleifen damit nichts zu tun haben und über einen
Speicherbereich mit Namen Heap (Haufen) arbeiten. Dieser Unterschied kann sich auf
die Performance auswirken, aber vor allen Dingen ist die Reihenfolge einer Verarbeitung
von Bedeutung. Denn während eine Schleife chronologisch bei jedem Durchlauf An-
weisungen ausführt, erfolgt die Abarbeitung bei einer Rekursion nach dem FILO-Prin-
zip. FILO steht für „first in last out“, was uns schon bei der JavaScript-Datenstruktur
Stack begegnet ist. Die Namensgleichheit ist nicht zufällig. Wie bei einem realen Karten-
stapel, auf dem Sie Karten einzeln auf einen Kartenstapel ablegen und wieder einzeln
wegnehmen, werden Sie diese von oben wegnehmen, bis die letzte Karte entnommen
wurde und der Stapel weg ist. Die Karte, die als Erstes (first in) auf den Stapel gelegt
wurde, ist also vor dem „Abarbeiten“ des Stapels ganz unten und wird als letzte Karte
verarbeitet (last out).

[Link] Allgemeine Rekursion
Nun ist die FILO-Reihenfolge bei Rekursion nicht immer von Bedeutung. Im einfachen
Fall ruft sich eine Funktion im Laufe der Abarbeitung bloß selbst wieder auf und macht
irgendetwas, was vom vorherigen Aufruf nicht abhängt. Aber wenn ein Aufruf von dem
vorherigen abhängt, dann ist die Reihenfolge natürlich massiv von Bedeutung. Gerade
wenn eine rekursive Funktion einen Rückgabewert liefert, ist das FILO-Prinzip wichtig.
Die nachfolgenden Listings zeigt sowohl die einfache Situation von unabhängigen Auf-
rufen als auch einen klassischen Algorithmus für Rekursion.

"use strict";
var i=0, j=0;
function rekFkt() {
  j += [Link]();
  i++;
  if (i < 5) rekFkt();
}
rekFkt();
[Link](i, j);

In dem Beispiel ([Link]) gibt es zwei globale Variablen. Eine davon wird als Zähl-
variable (i) verwendet, die andere mittels eines Zufallsmechanismus bei jedem Aufruf
um einen Wert zwischen 0 und 1 erhöht. Sofern der Wert der Zählvariable kleiner 5 ist,
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 141

wird die Funktion wieder selbst aufgerufen. Nach 5 Aufrufen sind die Selbstaufrufen be-
endet und die Werte von i und j werden ausgeben. Die Ausgabe wird etwas der Art sein:
5 4.059594159793312.
Das Ergebnis ist also etwas, was wir auch sehr gut mit einer Schleife hinbekommen
und was nicht wirklich das Verhalten und die Bedeutung von Rekursion ausnutzt oder
deutlich macht. Kommen wir zu einer Modifikation ([Link]):

"use strict";
var i=0, j=0;
function rekFkt() {
  j += [Link]();
  i++;
  [Link](i, j);
  if (i < 5) rekFkt();
}
rekFkt();

Die Ausgabe von i und j erfolgt jetzt innerhalb der rekursiven Funktion. Beachten Sie,
dass diese vor (!) dem Selbstaufruf eines neuen Funktionsaufrufs erfolgt. Sie erhalten
eine Ausgabe der Art:
1 0.9639843195432956
2 1.1839506183493862
3 1.8131400423823207
4 1.9862675394910094
5 2.35612602231419

Das ist eine chronologische Erhöhung von i und j, die Sie hier sehen. Aber auch diese
Variante verschleiert, was Rekursion eigentlich ausmacht. Die nächste Modifikation
([Link]) zeigt aber den Kern unter einem ersten, speziellen Blickwinkel.

"use strict";
var i=0, j=0;
function rekFkt() {
  j += [Link]();
  i++;
  if (i < 5) rekFkt();
  [Link](i, j);
}
rekFkt();

Die Ausgabe von i und j erfolgt wieder innerhalb der rekursiven Funktion, aber nach
(!) dem Selbstaufruf eines neuen Funktionsaufrufs. Sie erhalten fünfmal die gleiche
142 7 Funktionen, Prozeduren und Methoden

­ usgabe. Wie kann das sein? Die Ausgaben erfolgen, nachdem i und j fünfmal erhöht
A
wurden und geben dann fünfmal den Endwert (!) von i und j aus. Jetzt lösen wir mit der
letzten Variation dieses Beispiels die Situation auf. Dazu nutzen wir Parameter ([Link]).

"use strict";
function rekFkt(i=0, j=0) {
  j += [Link]();
  i++;
  if (i < 5) rekFkt(i,j);
  [Link](i, j);
}
rekFkt();

Das Beispiel erzeugt etwa eine Ausgabe wie diese:


5 2.161980400889223
4 1.67665560209027
3 1.0046184762996724
2 0.3275076856749135
1 0.3152071516397168

Das ist eine chronologische Verminderung (!) des Werts von i und j, die Sie hier sehen.
Besprechen wir den genauen Ablauf.

1. Wir haben jetzt keine globalen Variablen, sondern zwei Parameter i und j bei der
Funktion. Wenn die Funktion ohne Argumente aufgerufen wird, werden i und j auf
den Standardwert von 0 gesetzt (Vorgabeparameter).
2. In der Funktion wird zu j wieder ein zufälliger Wert zwischen 0 und 1 addiert und i
inkrementiert.
3. Anschließend wird überprüft, ob i kleiner als 5 ist. Wenn dies der Fall ist, wird die
Funktion rekFkt erneut aufgerufen, wobei die aktuellen Werte von i und j übergeben
werden. Bei der Ausgabe von oben ist das für i 1 und j 0.3152071516397168.
4. Beim nächsten Aufruf sind das für i 1 und j 0.3275076856749135.
5. Beim letzten Aufruf sind das für i 5 und j 2.161980400889223.
6. Bis zu der Stelle ist keine einzige Ausgabe erfolgt. Aber alle fünf Funktionsaufrufe
sind mit den jeweilig aktuellen Werten von i und j auf dem Stack gelandet.
7. Der Stack wird jetzt erst mit den jeweilig aktuellen Werten von i und j vom Stack ge-
nommen und dabei die noch offene Ausgabe getätigt.

Damit sollte das Verhalten von Rekursion klar sein. Aber man kann auch noch den Rück-
gabewert einer Funktion einbeziehen. Eine klassische Anwendung der Rekursion – die
Fakultätsberechnung – soll das demonstrieren ([Link]).
7.5 Besonderheiten bei JavaScript im Zusammenhang mit Funktionen 143

"use strict";
function fak(n) {
  if (n == 0) {
      return 1;
  } else {
      return n * fak(n - 1);
  }
}
[Link](fak(5));

Die Funktion besitzt einen Übergabeparameter und einen Rückgabewert. Wenn die
Funktion aufgerufen wird (fak(5)), hat der Parameter n den Wert 5. Nun wird im Inneren
der Funktion geprüft, ob der Wert von n 0 ist. Das ist nicht der Fall und der else-Zweig
wird „ausgeführt“, wobei „ausgeführt“ hier eben bedeutet, dass der Aufruf der Funktion
auf den Stack gelegt wird, mit dem Wert 5 für den Parameter. Und der else-Zweig sorgt
dafür, dass die nächste „Karte“ auf dem Stapel der Aufruf von fak(4) ist (fak(n – 1)).
Dann kommen fak(3), fak2(), fak(1) und zuletzt fak(0) auf den Stapel.
Zuerst wird fak(0) dann aufgerufen und vom Stapel genommen. Diese Funktion lie-
fert den Rückgabewert 1, was im Moment uninteressant ist. Aber beim nächsten Auf-
ruf (fak(1)) gelangen wir in den else-Zweig. Und dort wird der Wert von n (1) mit dem
Rückgabewert von fak(0) (1) multipliziert. Das Ergebnis (1) ist der Rückgabewert von
fak(1). Nun liegt fak(2) oben im Stapel. Und dort wird der Wert von n (2) mit dem Rück-
gabewert von fak(1) (1) multipliziert. Das Ergebnis (2) ist der Rückgabewert von fak(2).
Jetzt ist fak(3) oben im Stapel und hier wird der Wert von n (3) mit dem Rückgabewert
von fak(2) (2) multipliziert. Das Ergebnis (6) ist der Rückgabewert von fak(3). So geht es
weiter. Bei fak(4) wird der Wert von n (4) mit dem Rückgabewert von fak(3) (6) multi-
pliziert. Das Ergebnis (24) ist der Rückgabewert von fak(4). Der letzte Funktionsaufruf
auf dem Stapel nimmt den Wert 5 für n und multipliziert ihn mit 24. Und diese Rückgabe
von 120 ist letztendlich das, was der Aufrufer von fak(5) bekommt und verarbeiten kann.
Der Stack ist jetzt auch wieder leer.

[Link] Rekursion zur Animation


Es gibt einige Situationen, bei denen weder Stack noch Übergabewert an eine Funktion
bei einem Selbstaufruf wirklich von Bedeutung ist. Zum Beispiel bei einer Animation.
Im Rahmen einer Webseite kann man so etwa die Größe eines Bildes oder eine CSS-
Eigenschaft animiert verändern. Wir werden diese Technik im Abschnitt zu DHTML
vertiefen. Wichtig ist beim Selbstaufruf die Methode mit Namen setTimeout. Sie gehört
zum globalen Objekt eines jeden Browser (window) und erlaubt die zeitverzögerte Aus-
führung einer Aktion. Dazu geben Sie die als ersten Parameter einen Callback und als
zweiten Parameter die zu wartende Anzahl von Millisekunden an. Die schematische Syn-
tax sieht so aus:
144 7 Funktionen, Prozeduren und Methoden

Beispiel

setTimeout( [Callback], [Millisek]) ◄

[Link] Iteration per Rekursion


Kommen wir nun noch zu einer sehr wichtigen Anwendung von Rekursion – der Ite-
ration über Datenstrukturen, deren Tiefe man nicht kennt. Etwa die Anzahl von Ver-
zeichnisebenen bei einem Betriebssystem. Rekursion ist allgemein der Königsweg der
Auswertung einer flexiblen Datenstruktur. Betrachten wir den folgenden Quellcode, der
rekursiv eine Aufgabe löst, die im letzten Kapitel mittels Schleifen erledigt wurde (ver-
schachtelte_arrays_iterieren_rek.js):

"use strict";
var a = [[1,2],[3,4]];
var b = [[1,2],[[3],[4]],[5,6,7,8]];
var c = [[1,2],[[3],[4]],[9,[5,6,7,8]]];
var person = { name:"Otto", titel: ["Prof", "Dr"], adresse: {
  plz:12345, ort:"Irgendwo", strasse: { name:"Milchstrasse", nr:42 } }};
function recursiveIteration(obj) {
  for (var key in obj) {
    // Optional: Test, ob Schlüssel Eigenschaft hat
    //if ([Link](key)) {
          if (typeof obj[key] === "object") {
            [Link](key + " (Object):");
            recursiveIteration(obj[key]); // Rekursiver Aufruf für Objekte
          } else if ([Link](obj[key])) {
            [Link](key + " (Array):");
            for (var i = 0; i < obj[key].length; i++) {
            recursiveIteration(obj[key][i]); // Rekursiver Aufruf
        }
            } else {
           [Link](key + ": "+obj[key]+", Type: " + typeof obj[key]);
        }
    //}
  }
}
[Link]("Array a:");
recursiveIteration(a); // Iteration über Array a
[Link]("Array b:");
recursiveIteration(b); // Iteration über Array b
[Link]("Array c:");
recursiveIteration(c); // Iteration über Array c
[Link]("Person:");
recursiveIteration(person); // Iteration über das Person-Objekt
7.6 Vordefinierte Funktionen in JavaScript 145

In diesem Code haben wir eine Funktion recursiveIteration definiert, die ein Objekt ent-
gegennimmt und rekursiv durch seine Eigenschaften iteriert. Für jedes Eigenschaftspaar
wird überprüft, ob der Wert ein Objekt oder ein Array ist. Wenn es sich um ein Objekt
handelt, wird die Funktion rekursiv aufgerufen. Wenn es sich um ein Array handelt, wird
eine Schleife verwendet, um rekursiv über jedes Element des Arrays zu iterieren. Wenn
der Wert weder ein Objekt noch ein Array ist, wird er einfach ausgegeben, zusammen
mit seinem Typ.

Array a:
0 (Object):
0: 1, Type: number
1: 2, Type: number
1 (Object):
0: 3, Type: number
1: 4, Type: number
Array b:

3: 8, Type: number
Person:
name: Otto, Type: string
titel (Object):
0: Prof, Type: string
1: Dr, Type: string
adresse (Object)

nr: 42, Type: number

7.6 Vordefinierte Funktionen in JavaScript

Es gibt in JavaScript einige vordefinierte Funktionen, die Sie direkt in Ihren Skrip-
ten aufrufen können. Sie gehören unmittelbar zum globalen Objekt. Es gibt einmal die
Funktionen, über die sie einen Parameter konvertieren können. Beispielsweise Number(),
Boolean() oder String(). Dazu gibt es Funktionen, die konkret Objekte erzeugen können
und deren Verwendung wir beim Abschnitt über OOP beleuchten. Aber sie sollen schon
an dieser Stelle angegeben werden, zumal sie auch teils schon verwendet wurden:

• Array() zum Erstellen eines neuen Array.


• Date() zum Erzeugen eines neuen Date-Objekts.
• Mit Error() können Sie ein neues Error-Objekt erstellen.
• Über Function() kann man eine neue Funktion erstellen.
• Mit Object() kann man ein allgemeines neues Objekt erstellen.
146 7 Funktionen, Prozeduren und Methoden

Hintergrundinformation
Erzeugen von Objekten in JavaScript kann literal über ein Array-Literal oder JSON erfolgen. Oder
man verwendet einen Konstruktor. Im Zusammenhang mit den genannten Funktionen wird man
dabei meist ein vorangestelltes new finden. Nun stellt sich die Frage, ob es in JavaScript einen
Unterschied zwischen dem Aufruf von Array() und new Array() oder den anderen Funktionen zum
Erzeugen eines Objekts gibt? Zuerst einmal – die Verwendung mit vorangestelltem new ist üb-
licher. Aber wenn Sie Array() ohne das new-Schlüsselwort aufrufen, wird die Funktion wie eine
reguläre Funktion aufgerufen. Nur gibt sie einfach ein neues Array zurück. Aber das erhalten Sie
auch beim Voranstellen von new. Der Hauptunterschied besteht also hier eigentlich nur darin, wie
Sie Arrays erstellen:

• Als Rückgabewert einer Funktion.


• Über einen Konstruktor.

Aber es kann sich auch weitgehender auswirken, ob man mit oder ohne new arbeitet. Wenn Sie
Date() ohne das new-Schlüsselwort aufrufen, wird die Funktion wie eine reguläre Funktion be-
handelt, und sie gibt eine Zeichenkette mit dem aktuellen Datum und der aktuellen Uhrzeit zurück.
Wenn Sie new Date() verwenden, wird ein neues Date-Objekt erstellt, das das aktuelle Datum und
die aktuelle Uhrzeit darstellt und einen numerischen Timestamp sowie typische Datumsmethoden
bereitstellt ([Link]). Dieses Verhalten hat man auch für andere eingebaute JavaScript-
Konstruktoren. Wenn Sie die Konstruktoren ohne new aufrufen, können sie ein anderes Verhalten
haben, abhängig von der Implementierung, während die Verwendung von new ein neues Objekt
des entsprechenden Typs erstellt ([Link]). In der Regel eröffnet die Verwendung mit new spä-
ter mehr Möglichkeiten. Wir werden das Verhalten auch bei selbstdefinierten Konstruktormethoden
nachbilden.

"use strict";
var a = Array();
var b = new Array();
[Link](a, typeof a);
[Link](b, typeof b);
var c = Date();
var d = new Date();
[Link](c, typeof c);
[Link](d, typeof d);

Die folgenden Funktionen sind ebenso in JavaScript bereits vorhanden (Tab. 7.1):

"use strict";
var a = "50";
var b = "5.6abc";
var c = "§$%&/()?ß";
var d = "[{'alter':42}]";
[Link]('a=', a, '; b=', b);
[Link]('a + b:',a + b);
[Link]('parseInt(a) + parseFloat(b)',parseInt(a) + parseFlo-
at(b));
7.6 Vordefinierte Funktionen in JavaScript 147

Tab. 7.1  Globale Funktionen


eval Die übergebene Zeichenkette wird als JavaScript-Code ausgewertet und das
Ergebnis der Ausführung zurückgegeben. Wenn Sie beispielsweise console.
log(eval("3 + 4 * 6")); notieren, wird in der Konsole das Ergebnis des Rechenaus-
drucks ausgegeben. Die Funktion kann auch zur Erzeugung eines Objekts genutzt
werden, wenn dieses in einem String abgebildet wurde. Die Funktion gilt aber als
Sicherheitsrisiko und ist auch von der Ausführungsgeschwindigkeit schlecht. Sie
sollten den Einsatz vermeiden oder zumindest nur vorsichtig verwenden.
encodeURI Diese Funktionen dienen der Ersetzung von Zeichen in URLs bzw. in Zeichen-
decodeURI ketten und deren Rückwandlung. Sie sind nützlich, um Zeichen in URLs zu
encodeURI- kodieren und zu dekodieren.
Component • e ncodeURI wird verwendet, um eine vollständige URI zu kodieren, indem sie
decodeURI- spezielle Zeichen wie `:``, `/``, `?``, `&``, ` = `` usw. in ihre entsprechenden
Component URI-kodierten Darstellungen umwandelt. Sie wird normalerweise aufgerufen,
wenn Sie einen kompletten URI kodieren möchten, aber die Parameterwerte
nicht kodieren müssen.
• d ecodeURI wird verwendet, um eine URI zu dekodieren, die zuvor mit enco-
deURI kodiert wurde. Sie konvertiert URI-kodierte Zeichenfolgen zurück in
normale Zeichenfolgen.
• encodeURIComponent wird verwendet, um eine URI-Komponente zu kodieren,
indem sie spezielle Zeichen wie `:```,`/```,`?```,`&```,` = `` usw. in ihre ent-
sprechenden URI-kodierten Darstellungen umwandelt. Sie wird normalerweise
aufgerufen, wenn Sie die Werte von Parametern in einer URL kodieren möchten.
• decodeURIComponent wird verwendet, um eine URI-Komponente zu de-
kodieren, die zuvor mit encodeURIComponent kodiert wurde. Sie konvertiert
URI-kodierte Zeichenfolgen zurück in normale Zeichenfolgen.
Es ist wichtig zu beachten, dass encodeURI und decodeURI den kompletten
URI kodieren bzw. dekodieren, während encodeURIComponent und decodeU-
RIComponent einzelne URI-Komponenten (wie Parameterwerte) kodieren bzw.
dekodieren.
escape Über diese Funktionen können Sie Zeichenketten konvertieren, die Steuer-
unescape zeichen (ASCII-Wert von 0 bis 31) enthalten. Diese Funktionen sollten nicht
mehr genutzt werden, da sie nicht immer zuverlässige Ergebnisse liefern und be-
stimmte Zeichen nicht korrekt kodieren bzw. dekodieren. Greifen Sie stattdessen
auf die Funktionen encodeXXX und decodeXXX zurück.
isNaN Mit der Funktion isNaN (is Not a Number = ist keine Zahl) können Sie testen, ob
ein übergebener Wert eine ungültige Zahl ist. Ist der Wert eine Zeichenkette oder
ein boolescher Wert, wird true, ist er eine Zahl, wird false zurückgeliefert. Auf
den Wert NaN selbst können Sie nicht mit einem Vergleich testen, obwohl dieser
als Ergebnis eines mathematischen Ausdrucks geliefert wird, wenn dieser keine
Zahl ergibt. Ein Vergleich if (a =  = NaN) ist nicht möglich bzw. liefert immer
false. NaN dient zwar dazu anzuzeigen, dass eine Operation nicht numerisch ist.
Trotzdem – selbst NaN =  = NaN liefert false. Das liegt daran, dass NaN spezielle
Eigenschaften hat, die es von allen anderen Werten unterscheiden. Gemäß dem
ECMAScript-Standard ist definiert, dass zwei NaN-Werte niemals gleich sind und
Fehler in numerischen Operationen zu signalisieren, die nur ein Signal dafür dar-
stellen, dass eine Berechnung fehlgeschlagen ist oder dass eine Operation nicht
sinnvoll ist. Daher macht es keinen Sinn, zwei Fehler zu vergleichen und zu er-
warten, dass sie gleich sind. Verwenden Sie deshalb die Funktion isNaN.
(Fortsetzung)
148 7 Funktionen, Prozeduren und Methoden

Tab. 7.1 (Fortsetzung)


isFinite Test, ob ein übergebene numerische Wert innerhalb des Zahlenbereichs liegt, den
JavaScript verarbeiten kann. Liegt der Wert außerhalb oder ist der Wert nicht nu-
merisch zu interpretieren, wird false zurückgeliefert, andernfalls true.
parseFloat Wandelt eine Zeichenkette in eine Dezimalzahl um und gibt diese als nume-
rischen Wert zurück. Diese Funktion gibt NaN zurück, wenn die Zeichenkette
nicht mit den Zeichen –, + ,. oder einer Ziffer beginnt. Die Konvertierung der
Zeichenkette in eine Zahl endet mit dem ersten ungültigen Zeichen oder dem
Ende der Zeichenkette.
parseInt Wandelt eine Zeichenkette in eine ganze Zahl um und gibt diese als numerischen
Wert zurück. Diese Funktion liefert NaN, wenn die Zeichenkette nicht mit den
Zeichen –, + oder einer Ziffer beginnt. Die Konvertierung der Zeichenkette in
eine Zahl endet mit dem ersten ungültigen Zeichen oder dem Ende der Zeichen-
kette.

[Link]('parseFloat(a) + parseInt(b)',parseFloat(a) + parseInt(b));


[Link](c,escape(c),encodeURIComponent(c));
[Link](d,eval(d),typeof eval(d), eval(d)[0].alter);

Das ist die Ausgabe des Beispiels [Link]:


a = 50; b = 5.6abc
a + b: 505.6abc
parseInt(a) + parseFloat(b) 55.6
parseFloat(a) + parseInt(b) 55
§$%&/()?ß %A7 %24 %25 %26/%28 %29 %3 F%DF %C2 %A7 %24 %25 %26 %2 F()
%3 F%C3 %9 F
[{'alter':42}] [ { alter: 42 } ] object 42

Besprechen wir die Details:

• Nach der Initialisierung und der Ausgabe der Variablenwerte werden die beiden
Zeichenketten a und b zunächst aneinandergefügt.
• Erst durch die beiden Funktionen parseInt und parseFloat können die Zeichen-
ketten in Zahlen umgewandelt und arithmetisch addiert werden. Die Variable a wird
in die Ganzzahl 50 und die Variable b durch parseFloat in die Dezimalzahl 5.6 um-
gewandelt. Der Zeichenkettenteil abc wird abgeschnitten. Dadurch ergibt sich der ma-
thematische Wert 50 + 5.6 = 55.6.
• Die Funktionen werden mit vertauschten Werten aufgerufen. Zuerst wird a als
Dezimalzahl interpretiert und b als Ganzzahl. Die Konvertierung für die Variable b
wird nach dem Punkt abgeschnitten. Dadurch ergibt sich der mathematische Wert
50 + 5 = 55.
7.6 Vordefinierte Funktionen in JavaScript 149

• Das Kodieren von einem String sehen Sie mit zwei verschiedenen Funktionen. Be-
achten Sie die unterschiedlichen Wege. Das veraltete escape erzeugt die Maskierun-
gen teils auf eine andere Art wie encodeURIComponent. Prinzipiell wird jedes mas-
kierte Zeichen mit einem Prozentzeichen und einem zweistelligen Hex-Code der Uni-
code-Kodierung dargestellt.
• Die Anwendung von eval erfolgt auf einen String, der die Deklaration eines Arrays
mit JSON als Inhalt enthält. Nach dem Evaluieren erhalten Sie ein Objekt, dessen
Eigenschaften Sie verwenden können.

 In HTML gibt es keine Eingabefelder, die speziell für Zahlen genutzt werden
können. Die Eingaben werden immer als Zeichenkette betrachtet. Möchten
Sie mit den Eingaben rechnen, können Sie mit isNaN z. B. überprüfen, ob die
Eingabe als Zahl interpretiert werden kann. Danach haben Sie die Möglich-
keit, die Zeichenkette über parseFloat oder parseInt in eine Zahl des ent-
sprechenden Typs umzuwandeln.
Module
8

Übersicht
Modularisierung bedeutet das Zusammenfassen von Codeteilen, um diese später
besser wiederverwenden zu können. Das beginnt bei Unterprogrammen und hat in
JavaScript vor einiger Zeit zu einer weiteren, neueren Form der Strukturierung von
JavaScript-Quellcode geführt – Modulen.

8.1 Was sind Module?

Vereinzelt sind wir in vorherigen Beispielen schon mit Modulen in Berührung ge-
kommen. Damit war es z. B. möglich, in einem Skript unter [Link] eine Benutzer-
eingabe von der Konsole entgegen zu nehmen. In JavaScript bezieht sich der Begriff
„Modul“ auf eine Möglichkeit, Code zu organisieren, indem man ihn in separate Dateien
aufteilt, die unabhängig voneinander funktionieren können. Das hat von der Idee durch-
aus Ähnlichkeiten zu Unterprogrammen, aber auch Klassen bzw. Objekten. Auch hier
spricht man von einer „Modularisierung“, aber die Erstellung von Modulen ist in Java-
Script eine Dateitechnik und relativ unabhängig davon, was man genau an Strukturen in
dem Modul bereitstellt. Dennoch ist die Strukturierung und Wiederverwendbarkeit von
Code auch hier der Fokus. Ebenso kann man Konflikte zwischen verschiedenen Teilen
eines Codes zu vermeiden. Es gibt verschiedene Standards und Techniken, um Module in
JavaScript zu erstellen. Zwei betrachten wir etwas näher.

• CommonJS
• ES6-Module

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 151
Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
152 8 Module

ES6-Module bieten einige Vorteile gegenüber CommonJS, wie zum Beispiel die sta-
tische Struktur, die zu einem besseren Tree-Shaking führen kann (entfernen nicht ver-
wendeter Codepassagen), und die Unterstützung für asynchrone Module. Allerdings sind
sie möglicherweise nicht in allen Umgebungen vollständig unterstützt. In beiden Fällen
helfen Module, Code sauber zu organisieren, Abhängigkeiten klar zu definieren und die
Wartbarkeit von Projekten zu verbessern.

8.2 CommonJS

CommonJS bezeichnet einen Standard für serverseitige JavaScript-Anwendungen, der


unter anderem von [Link] unterstützt und dort auch bei verschiedenen Erweiterungen
verwendet wird und die wir bei besagten Beispielen schon verwendet haben. Bei Com-
monJS können Sie die Anweisung require verwenden, um Module zu importieren, und
[Link] oder exports, um Objekte oder Funktionen aus einem Modul zu ex-
portieren.

 Wichtig
Wenn Sie diese Art von Modulen in einem Browser verwenden, werden Sie
normalerweise eine Fehlermeldung der Art erhalten: Uncaught ReferenceError: re-
quire is not defined
CommonJS war ursprünglich nicht für ein Browserumfeld gedacht, aber es gibt
mittlerweile mehrere Konstellationen, in denen das doch geht (Abschn. 8.4).

Bei CommonJS können Dateiangaben absolut oder relativ sein.

• Bei einer absoluten Referenzierung beginnt die Dateiangabe beginnt mit dem Datei-
pfad, der von der Wurzel des Projekts ausgeht. Zum Beispiel: /pfad/zum/[Link].
• Bei der relativen Dateiangabe beginnt man mit./ (für das aktuelle Verzeichnis). Be-
achten Sie auf jeden Fall den vorgestellten Punkt. Oder Sie verwenden zwei voran-
gestellte Punkte (../) für übergeordnete Verzeichnisse, gefolgt von dem Pfad zum
Modul relativ zur aktuellen Datei. Zum Beispiel./[Link]. oder./modulDir/mein-
[Link].

Es ist wichtig zu beachten, dass die Dateiendung (in der Regel *.js) normalerweise weg-
gelassen werden kann, da [Link] automatisch nach Dateien mit der angegebenen Er-
weiterung sucht, wenn keine Erweiterung angegeben ist.

Sie können auch bei Modulen einen strikten Modus einstellen.

Hier ist ein Beispiel mit mehreren Exports in einer Datei [Link], die sich in einem
Unterverzeichnis commonJSmodule der importierenden Datei befinden soll, und die
8.2 CommonJS 153

Funktionen add, multi, sub und div, aber nicht pot exportiert. Dabei gibt es verschiedene
Wege. Man kann einmal exports um die jeweiligen Funktionszeiger erweitern. Oder man
weist [Link] einen Funktionszeiger oder in geschweiften Klammern mehrere
Funktionszeiger zu:

// Modul, das Funktionen exportiert


"use strict";
function add(a, b) {
    return a + b;
}
function multi(a, b) {
return a * b;
}
function div(a, b) {
    return a / b;
}
function sub(a, b) {
    return a - b;
}
// Wird nicht exportiert
function pot(a, b) {
    return a ** b;
}
[Link] = add;
[Link] = multi;
[Link] = div;
[Link] = sub;
// Alternative
/*
[Link] = { multi, div, add, sub };
*/

Von den fünf deklarierten Funktionen werden vier exportiert und die fünfte Funktion
(pot) bleibt privat. Beim Exportieren kann man – wie hier – für jede Funktion eine
einzelne Anweisung angeben oder aber – wie hier auskommentiert – alle zusammen in
geschweiften Klammern. Nutzen Sie aber nur eine der Varianten.

 Wenn Sie nacheinander [Link] Funktionszeiger zuweisen, ersetzt der je-


weils letzte alle vorherigen. Das ist genauso wie bei jeder konventionellen mehr-
fachen Wertzuweisen. So können Sie also nicht (!) mehrere Funktionen exportieren.

Hier ist ein Beispiel mit einem Import ([Link]), bei dem sich das Modul
math relativ im Unterverzeichnis commonJSmodule befindet.
154 8 Module

"use strict";
// Funktionen in Modul importieren. Funktion sub wird nicht importiert
// Einzelne Anweisungen für jede Funktion
/*
const { add } = require('./commonJSmodule/math');
const { multi } = require('./commonJSmodule/math');
const { div } = require('./commonJSmodule/math');
*/
// Eine Anweisung für alle importierten Funktionen
const { add, multi, div } = require('./commonJSmodule/math');
// Funktionen aus dem Modul verwenden
[Link](add(2, 3)); // Ausgabe: 5
[Link](multi(2, 3)); // Ausgabe: 6
[Link](div(3, 2)); // Ausgabe: 1.5

Man kann die exportierten Funktionen einzeln importieren (hier auskommentiert) oder
mehrere in geschweiften Klammern. Das ist analog dem Export. Beachten Sie, dass das
Modul zwar vier Funktionen exportiert, hier aber zu Demonstrationszwecken nur drei
davon importiert werden. Das ist natürlich möglich und sogar ein Kernkriterium von
Modulen, nur einen Teil der bereitgestellten Funktionalität zu nutzen. Wie Sie die im-
portierten Elemente nutzen, ist dann vollkommen Ihre Sache. Funktionszeiger werden
meist Konstanten zugewiesen, aber das ist keineswegs zwangsläufig. Typischerweise ex-
portieren Module Funktionen. Allerdings können Sie auch Variablen etc. bis hin zu Ob-
jekten und Klassen1 exportieren. Im Grunde alles, was Sie in JavaScript deklarieren kön-
nen. Das wäre als Beispiel das Modul [Link]:

// Modul, das Funktionen als auch Variablen und Konstanten exportiert


"use strict";
const wert = 42;
function pot(a, b) {
    return a ** b;
}
[Link] = pot;
exports.wert1 = 21;
exports.wert2 = wert;

In der JavaScript-Datei [Link] gehen Sie damit vollkommen analog um


als wären die Deklarationen dort erfolgt:

1 Das sehen wir uns noch kurz im nächsten Kapitel an.


8.3 ES6-Module 155

"use strict";
const { pot, wert2 } = require('./commonJSmodule/[Link]');
var { wert1 } = require('./commonJSmodule/werte');
// Funktionen aus dem Modul werden verwendet
[Link](pot(2, 3)); // Ausgabe: 8
[Link](wert2); // Ausgabe: 42
[Link](wert1); // Ausgabe: 21
wert1 *= 2;
[Link](wert1); // Ausgabe: 42

Beachten Sie, dass aus didaktischen Gründen bei der einen Importanweisung die Datei-
endung angegeben wurde. Obwohl es nicht notwendig und sogar unüblich ist, ist das
nicht verboten.

8.3 ES6-Module

ES6-Module (ECMAScript-Module) bezeichnet den modernen Standard für das Arbei-


ten mit Modulen in JavaScript, der auch von Browsern und Tools wie Webpack und
Babel nativ unterstützt wird. Mit ES6-Modulen können Sie die Anweisungen import
und export verwenden, um Module bzw. deren Bestandteile zu importieren und zu ex-
portieren. Auch [Link] unterstützt ab der Version 12 ES6-Module offiziell. Es ist wich-
tig zu beachten, dass ES6-Module einige Unterschiede zu CommonJS-Modulen auf-
weisen, insbesondere in Bezug auf die Syntax und das Verhalten von this. Die Details
sprengen aber unseren Rahmen und sind vor allen Dingen bei einem Umstieg von Be-
deutung.

 Wichtig
Die Dateierweiterung von ES6-Modulen ist [Link] [Link]. Die Wahl der
Dateierweiterungen hängt von der Art des Moduls ab und wie es im Browser oder
in [Link] verwendet wird.

• [Link]-Erweiterung wird traditionell für JavaScript-Dateien verwendet,


unabhängig davon, ob es sich um CommonJS- oder ES6-Module handelt.
Wenn man beabsichtigt, ein Modul sowohl im Browser als auch in Node.
js zu verwenden und es keine besonderen Anforderungen gibt, kann man
[Link]-Erweiterung verwenden.
• [Link]-Erweiterung wird speziell für ECMAScript-Module (ES6-­
Module) verwendet, um sie von traditionellen JavaScript-Dateien zu
unterscheiden. Damit kann die Module also explizit als ES6-Module
kennzeichnen und zudem die native Unterstützung in modernen Browsern
156 8 Module

nutzen, sofern diese vorhanden ist. Dies ist besonders nützlich, wenn um
eine Datei als ES6-Modul zu kennzeichnen, aber keine Konfigurations-
dateien geändert werden soll oder kann. So etwas kann allerdings sinnvoll
oder auch notwendig sein. Etwa in der Datei [Link] eines Projekts
das Feld type auf "module" setzen. Dadurch wird [Link] angewiesen,
[Link]-Dateien standardmäßig als ES6-Module zu behandeln.

Ein Modul [Link] könnte so aussehen:

// Modul, das Funktionen exportiert


"use strict";
export function add(a, b) {
    return a + b;
}
export function multi(a, b) {
    return a * b;
}
export function div(a, b) {
    return a / b;
}
export function sub(a, b) {
    return a - b;
}
// Wird nicht exportiert
function pot(a, b) {
    return a ** b;
}

Vor jeder zu exportierenden Funktion steht einfach eine export-Anweisung. In dem im-
portierenden Modul steht dann die import-Anweisung:

"use strict";
import { add, multi } from './es6module/[Link]';
import { div } from './es6module/[Link]';
// Funktionen aus dem Modul werden verwendet
[Link](add(2, 3)); // Ausgabe: 5
[Link](multi(2, 3)); // Ausgabe: 6
[Link](div(3, 2)); // Ausgabe: 1.5

Auch bei ES6-Modulen kann man mehrere Exportanweisungen in einer einzelnen An-
weisung zusammenfassen (Objekt-Destrukturierung) bezeichnet. Das geht analog wie
bei den CommonJS-Modulen:
8.4 Module in Browsern 157

Beispiel

function eins() { …}
function zwei() { …}
export { eine, zwei}; // Mehrere Exportanweisungen in einer einzigen Anweisung zu-
sammen ◄

8.4 Module in Browsern

Moderne Browser bieten Unterstützung für die Arbeit mit Modulen – insbesondere
ES6-Module, aber auch eingeschränkt CommonJS-Module, obwohl diese ursprüng-
lich nur für die Verwendung mit [Link] entwickelt wurden. Dies wurde hauptsächlich
durch die Einführung von Tools wie Browserify, webpack und RequireJS ermöglicht,
die CommonJS-Module für den Browser kompatibel machen. Bei Bedarf informie-
ren Sie sich über die Verwendung eines Modul-Bundlers oder die Transpilierung mit
Babel. Wir werden uns auf ES6-Module beschränken, da zudem die Verwendung von
CommonJS-Modulen im Browser einige Einschränkungen haben kann, insbesondere
wenn es um die asynchrone Natur von [Link]-Modulen geht. In vielen Fällen sind
ES6-Module eine bessere Option, da sie nativ von vielen modernen Browser unterstützt
werden und modernere Features bieten. Damit ein Browser das Modulsystem samt der
import-­Anweisungen korrekt versteht, verwendet man das type = "module"-Attribut im
<script>-Tag.

 Ältere Browser benötigen u. U. die Verwendung eines Build-Tools, was hier wie
gesagt nicht verfolgen.

Auf der Seite des Webservers muss sichergestellt werden, dass die Dateien korrekt
bereitgestellt werden. Das betrifft vor allen Dingen die Pfadangaben, die wir gleich in
dem Beispiel noch einmal thematisieren. Zudem gilt beim Verwenden von Modulen im
Browser das CORS (Cross-Origin Resource Sharing)-Konzept. Das bedeutet, dass Mo-
dule aus verschiedenen Domains oder Dateisystempfaden möglicherweise nicht geladen
werden können, es sei denn, die Servereinstellungen erlauben dies explizit.

 Es gibt einige Fehlermeldungen beim Laden eines Moduls, deren Lösung


recht standardisiert angegangen werden können. Eine Fehlermeldung beim
Laden eines Moduls aufgrund eines nicht freigegebenen MIME-Typs ('text/
html') bedeutet, dass der Browser versucht hat, ein JavaScript-Modul zu
laden, aber stattdessen vom Server eine Antwort mit dem MIME-Typ "text/
html" erhalten hat. Ursachen können eine fehlende Datei oder ein falscher
158 8 Module

Dateipfad sein, aber auch eine fehlerhafte Serverkonfiguration. Der Server


sollte die angeforderte JavaScript-Datei mit dem richtigen MIME-Typ "applica-
tion/javascript" oder "text/javascript" zurückgeben. Auch Weiterleitungen oder
Umleitungen können zu dem Fehler führen.

 Aufgrund von Sicherheitsrichtlinien können Browser lokale Dateien nicht über


das file-Protokoll importieren.

Nachfolgend sehen Sie ein Beispiel mit nativer Unterstützung im Browser (moduleIm-
[Link]):


<body><script type="module" src="[Link]"></script>
   <script type="module" src="es6module/[Link]"></script></body></html>

Sie finden hier gleich zwei Module, die in der Webseite eingebunden werden. Diese ver-
wenden beide ein weiteres Modul. Beachten Sie, dass sich die Datei [Link] im gleichen
Verzeichnis und die Datei [Link] im Unterverzeichnis es6module in Bezug auf die
Webseite befinden. Das ist dann interessant, wenn man von da aus eine weitere Modul-
datei referenziert. Hier ist die Datei [Link] im gleichen Verzeichnis, die ein Modul im
Unterverzeichnis es6module importiert. Beachten Sie, dass hier./ (der Punkt ist wichtig)
die Basis des Pfades ist.

// [Link]
import { drei, vier } from './es6module/[Link]';
drei(); // Ausgabe: foo
vier(); // Ausgabe: bar

Das ist die Datei [Link]:

// [Link]
import { eins, zwei } from './[Link]';
eins(); // Ausgabe: foo
zwei(); // Ausgabe: bar

Da sich diese Datei im gleichen Verzeichnis wie die Datei [Link] befindet, entfällt
die Verzeichnisangabe. Das exportierende Modul enthält nur die Deklaration von Funk-
tionen, denen export vorangestellt wird.

// [Link]
export function eins() {
   [Link](1);
}
8.4 Module in Browsern 159

export function zwei() {


   [Link](2);
}
export function drei() {
   [Link](3);
}
export function vier() {
  [Link](4);
}
Objekte und JavaScript
9

Übersicht
Der wesentliche (wenn nicht gar ausschließliche) Nutzen von JavaScript besteht
darin, dass Sie damit browser- und computerbezogene Objekte abfragen und mani-
pulieren können. Das wurde bereits in sämtlichen Quellcodes bisher auch gemacht.
Selbst für die primitive Ausgabe von Informationen muss man ein passendes Ob-
jekt verwenden. Auch sonst haben wir immer wieder am Rande mit Objekten ge-
arbeitet, wenngleich JavaScript auch sehr viel mit Funktionen arbeitet. In dem Ka-
pitel gehen wir jetzt an die Grundlagen. Sie lernen hier etwas über

• Grundlagen der OOP,


• JavaScript und Objekte im Allgemeinen,
• Klassen und Prototypen samt Vererbung, Instanzen, Methoden und Eigen-
schaften sowie
• die spezielle Situation der OOP bei JavaScript.

9.1 Grundlagen der OOP

JavaScript arbeitet im Wesentlichen mit zwei Paradigma bei der Programmierung:

• Dem funktionalen Paradigma.


• Dem objektorientierten Paradigma.

 Ein Paradigma bezeichnet ein grundlegendes Konzept, eine Denkweise oder Me-
thode, die ein bestimmtes Fachgebiet prägt. Im Kontext der Informatik und insbesondere
der Softwareentwicklung bezieht sich „Paradigma“ auf grundlegende Ansätze oder

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 161
Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
162 9 Objekte und JavaScript

­ odelle, die verwendet werden, um Programme zu entwerfen, schreiben und organisie-


M
ren. Ein Programmierparadigma legt Strukturierung, Design und Methoden fest, die ver-
wendet werden, um Programme zu entwickeln. Es beeinflusst, wie man Probleme an-
geht, Code schreibt und wie Programme letztendlich funktionieren.

9.1.1 Was sind Objekte?

JavaScript ist zumindest eine objektbasierende1 Sprache, die grundsätzlich mit Objekten
umgehen kann. Entweder werden die Objekte vom System bereitgestellt oder man kann
diese aus JavaScript erzeugen und vielfältig verwenden. Die selbst erzeugten oder vom
System bereitgestellten Objekte werden durch Eigenschaften/Attribute und Methoden
(verallgemeinert Member) sowie einen spezifischen Zustand charakterisiert. Dieser Zu-
stand kann dafür verantwortlich sein, ob bestimmte Methoden eines spezifischen Objekts
temporär verfügbar sind oder nicht. Wie Member in JavaScript verwendet werden, haben
wir schon behandelt – über die Punktnotation. Diese entspricht dem Senden einer Bot-
schaft oder Nachricht an das Objekt.

9.1.2 Klassen, Instanzen, Prototypen und Vererbung

Allgemein werden in der OOP ähnliche Objekte zu Gruppierungen zusammengefasst,


die eine leichtere Klassifizierung der Objekte ermöglichen. Die Member der Objekte
werden in den Gruppierungen gesammelt und für eine spätere Erzeugung von realen Ob-
jekten verwendet. Diese Beschreibungen oder Baupläne für konkrete Objekte nennt man
in der allgemeinen Objektorientierung Klassen (auch Objektdeklaration genannt), die
Objekte selbst sind im OO-Sprachgebrauch Instanzen dieser Klassen.

Der Zustand ist kein Kriterium, das in einer Klasse eine Rolle spielt, sondern rein auf das konkrete
Objekt bezogen. Allerdings kann man in einer Klassen Anfangszustände vorbelegen.

Zentrale Bedeutung in der vollständigen OOP hat bei der Bildung von Klassen eine
hierarchische Struktur der Gruppierungen. Gemeinsame Dinge sollen in der objekt-
orientierten Philosophie möglichst in einer übergeordneten Klasse – einer sogenannten
Superklasse – zusammengefasst werden (Verallgemeinerung). Erst wenn Unter-
scheidungen möglich beziehungsweise notwendig sind, welche nicht für alle Mitglieder
einer Klasse gelten, werden Untergruppierungen – untergeordnete Klassen – gebildet
(Spezialisierung). Jede (gewöhnliche) Klasse kann eine Vielzahl von Unterklassen (Sub-
klassen) und konkrete Instanzen (Objekte) haben. Die voneinander abgeleiteten Klassen

1 In neuen Versionen sogar voll objektorientiert.


9.1 Grundlagen der OOP 163

bilden einen Klassenbaum. Genau genommen wird der Klassenbaum meist in der Dar-
stellung gestürzt bzw. auf den Kopf gestellt. „Oben“ im Baum ist dann die Wurzel (engl.
root).
Die Beziehung einer übergeordneten Klasse zu ihren Subklassen ist streng hierar-
chisch und heißt Vererbung. Jede Subklasse bekommt alle Member der Superklasse ver-
erbt, die dafür vorgesehen sind. Jede Subklasse sollte sinnvollerweise mindestens einen
Member mehr haben, welche die Superklasse nicht hat, oder vererbte Dinge irgend-
wie verändern. Abgeleitete Klassen übernehmen die freigegebenen Member aller über-
geordneten Klassen. Die abgeleitete Klasse verwendet die Member der Superklasse. Die
Auswahl der Member in der Klassenhierarchie ist so geregelt, dass de gewünschte Mem-
ber in der nächsthöheren Superklasse gesucht wird, wenn ein Member in einer konkreten
Objektklasse nicht vorhanden ist. Ist ein Member dort ebenfalls nicht vorhanden, erfolgt
die Suche in der nächsthöheren Klasse im Klassenbaum, bis die oberste Superklasse des
Klassenbaums erreicht ist. Der Zugriff erfolgt also in der ersten Klasse, in der ein Treffer
erfolgt.

Der Begriff einer Klasse ist in JavaScript etwas gefährlich, denn historisch arbeitet JavaScript
nicht mit klassischen Klassen, sondern sogenannten Prototypen. Dabei ist die Anwendung eines
Prototyps zur Objekterzeugung in der JavaScript-Programmierung nicht von der Instanzierung
einer Klasse relevant zu unterscheiden. Um ein Beispiel aus dem realen Leben anzuführen. Eine
Klasse ist wie ein Bauplan, den ein Architekt erstellt hat und nach dem sich beim Bau eines Hau-
ses gerichtet wird. Ein Prototyp ist ein bereits existierendes Haus, von dem eine Kopie erstellt
wird. An dem neuen Haus können Sie nicht erkennen, ob es nach einem Bauplan oder einem kon-
kret schon vorhandenen Muster erstellt wurde. Aber selbst bei der Erstellung kann man in Java-
Script nicht erkennen, ob nach einem Bauplan oder einem konkret schon vorhandenen Muster vor-
gegangen wird. Das sehen wir noch bei der konkreten Syntax.

9.1.3 Ziele der OOP – Wiederverwendbarkeit und bessere


Softwarequalität

Das (!) Argument für die OOP und der Grund dafür, dass sie sich vielfach durchgesetzt
hat, ist Wiederverwendbarkeit. In der althergebrachten Programmierung2 können die
Datenebene und die Anweisungsebene aufgetrennt werden. Das Problem dieser Art der
Programmierung ist, dass bei einer solchen Aufteilung Änderungen in der Datenebene
Auswirkungen auf die unterschiedlichsten Programmsegmente haben können und damit
die Wiederverwendbarkeit von Unterprogrammen sehr eingeschränkt ist, da oft Ab-
hängigkeiten zu anderen Bestandteilen eines Programms existieren (beispielsweise zu

2 Die historisch erste Generation der Programmierung mit Maschinensprache und die zweite Ge-
neration mit Assembler sollen hier nicht beachtet werden, sondern nur die höheren Programmier-
sprachen.
164 9 Objekte und JavaScript

globalen Variablen). Objektorientierte Programmierung lässt sich im Vergleich zu diesem


Paradigma der Programmierung darüber abgrenzen beziehungsweise definieren, dass zu-
sammengehörende Anweisungen und Daten eine zusammengehörende, abgeschlossene
und eigenständige Einheit bilden – Objekte! Und diese sind ohne Abhängigkeiten zu ver-
wenden und ohne Randwirkungen intern zu ändern.

 Objektorientierte Programmierung hebt die Trennung von Daten- und Anweisungs-


ebene auf.

Die objektorientierte Programmierung verfolgt noch ein Ziel. Bei der Entwicklung kom-
plexer Software-Systeme ist die wichtigste Aufgabe, eine möglichst hohe Softwarequali-
tät zu erreichen.

9.1.4 Kernkonzepte der Objektorientierung

Die nachfolgenden theoretischen Kernkonzepte werden als Basis der objektorientierten


Software-Entwicklung oft genannt (zum Teil wurden Sie schon angedeutet):

• Objekte dienen als Abstraktion (Vereinfachung) eines realen Gegenstands oder Kon-
zepts mit einem spezifischen Zustand und einem spezifischen Verhalten. Sie nennt
man Instanzen einer Klasse.
• Alle Bestandteile, die direkt zu einem Objekt gehören, nennt man Instanzelemente.
Der Name resultiert daraus, dass „Instanz“ oft als Synonym für „Objekt“ genommen
wird. Man bezeichnet diese Bestandteile oft auch als,,nicht-statisch'' oder,,dyna-
misch'', da sie für jede Instanz dynamisch erstellt werden.
• Klassen dienen als Baupläne für Objekte. In der umgekehrten Sichtweise „klassi-
fizieren“ sie alle Gemeinsamkeiten, die verwandte Objekte auszeichnen. Man nennt
sie auch die Objekttypen oder kurz Typen. Bei JavaScript arbeitet man aber auch mit
Prototypen, was schon angedeutet wurde und worauf wir noch genauer eingehen.
• Objekte werden immer aus Klassen erzeugt. Aber viele Programmiersprachen bre-
chen diese Vorgabe und erlauben die Erzeugung von Objekten ohne Klassen. Arrays
sind etwa in Java oder C# explizit klassenlose Objekte.
• In einer streng objektorientierten Welt gibt es nur Objekte. Damit müssen auch Klas-
sen selbst Objekte sein. Nur wenn Objekte aus Klassen erzeugt werden – woraus wer-
den Klassen erzeugt? Die Antwort sind Metaklassen, für die es in vielen Sprachen
zwar nicht explizit eine Syntax zur Notation als Quellcode gibt, die aber das Konzept
konsistent halten.
• Ebenso darf es in einer streng objektorientierten Welt keine primitiven Datentypen
geben, was auch eine Berechnung von 1 + 1 oder 1 * 1 unmöglich macht. Es darf nur
Objekte geben, die Botschaften austauschen. Diese Vorgabe ist absolut praxisfern und
hat zum Scheitern von OO-Sprachen geführt, die dies erzwungen haben. Jede aktuelle
9.1 Grundlagen der OOP 165

OO-Sprache gestattet den Umgang mit primitiven Datentypen, aber tatsächlich han-
delt es dabei dann im Hintergrund meist dennoch um Objekte.
• Globale Variablen und Funktionen existieren außerhalb von Objekten. Diese darf es
in einer streng objektorientierten Welt nicht geben. JavaScript umgeht das Problem
durch die Definition eines globalen Objekts.
• Alle Bestandteile, die nicht direkt zu einem spezifischen Objekt, sondern einer Klasse
gehören, nennt man Klassenelemente oder auch statische Elemente, wobei man hier
in JavaScript eben wegen der Prototypen aufpassen muss. In manchen Sprachen gibt
es auch Unterschiede zwischen Klassenelementen und statischen Elementen.
• Attribute sind eine Beschreibung objekt- und klassenbezogener Daten. Oft wird auch
der Bezeichner „Eigenschaft“ für „Attribut“ verwendet, wobei das einige Sprachen
differenzieren.
• Methoden sind die Beschreibung objekt- und klassenbezogener Funktionalität.
• Assoziationen und Aggregationen sind Bezeichnungen von Mechanismen zum Aus-
druck von Objektbeziehungen.
• Vererbung ist eine besondere Form der Assoziation und ein Mechanismus zum Gene-
ralisieren und Spezialisieren von Objekttypen.
• Der Begriff Polymorphie ist nicht eindeutig und wird in verschiedenen Quellen
unterschiedlich streng ausgelegt. Generell bedeutet er Vielgestaltigkeit. Polymorphie
dient in einigen Sprachen zur flexiblen Auswahl geeigneter Methoden identischen Na-
mens anhand des Objekttyps und der Argumentenliste. Das geht in JavaScript ja ex-
plizit nicht, denn die Parameter spielen keine Rolle bei der Auswahl, wie wir gesehen
haben. Aber der Begriff kann auch weiter gefasst werden und dann weitere Mechanis-
men zum vielgestaltigen Verhalten umfassen. Damit versteht man das Konzept auch
wieder so wie im philosophischen Ansatz.
• Schnittstellen implementieren in einigen Sprachen einen Mechanismus zur Struktu-
rierung von Klassenbeziehungen, den es in JavaScript nicht gibt.
• Abstrakte Klassen implementieren in einigen Sprachen einen Mechanismus zum
Erzwingen bestimmter Operationen in spezialisierten Klassen, den es in JavaScript
ebenfalls nicht gibt.
• Generischen Klassen dienen zur Darstellung von Klassenfamilien. Auch das Kon-
zept gibt es in JavaScript nicht und wäre wegen der losen Typisierung auch wenig
hilfreich.

Auch Sprachen, die explizit als (streng) objektorientiert eingestuft werden, implementieren nicht
unbedingt alle diese Charakteristika oder brechen Sie ganz gezielt (primitive Datentypen, klassen-
lose Objekte, …).

9.1.5 Wie lange gibt es Objektorientierung als Konzept?

In vielen Schulungen zu OOP frage ich Teilnehmer, seit wann es das Konzept der
Objektorientierung gibt. Nach einer dramaturgischen Pause schiebe ich die Bemerkung
166 9 Objekte und JavaScript

nach, dass die Antwort nicht so genau sein muss und es mir auf ein paar Hundert Jahre
nicht ankommt. Der Effekt ist immer gleich. Aber tatsächlich muss sich man beim Kon-
zept der Objektorientierung von der IT lösen und hin zur Philosophie über die reale
Welt gehen. Das wurde mir erstmals klar, als ich auf dem Weg zu einer Java-Schulung
im Auto ein Hörbuch zu griechischer Philosophie gehört habe. Plötzlich ging es da um
Abstraktion, Klassen, Instanzen und Objekte, Metaklassen, Eigenschaften, Methoden,
Polymorphie und weitere Fachbegriffe, die ich intuitiv der OOP zugeordnet habe. Aber
„natürlich“ ist das Konzept der Objektorientierung nicht auf IT und Programmierung be-
schränkt. Das Gegenteil ist der Fall – die Programmierung mit objektorientiertem Ansatz
wird wie der Umgang mit Objekten in der realen, „natürlichen“ Welt verstanden. Auch
wenn man lange Zeit anders programmiert hat.

9.1.6 Hintergründe der OOP

Aber auch die OOP selbst ist kein wirklich neuer Ansatz. Im Gegenteil – die Idee der
OOP reicht sogar bereits bis Anfang der 70iger Jahre und teilweise noch weiter zurück.
Sehr frühe Vertreter von Programmiersprachen mit objektorientiertem Denkansatz sind
Lisp oder Logo. In den 70iger Jahren entstand als wichtigster Vertreter erster objekt-
orientierter Sprachen Smalltalk, das sich in mehreren Zyklen entwickelte und als einer
der geistigen Väter von Java gilt. Aber erst mit Java in der Mitte der 90iger-Jahree
hat sich die OOP richtig durchgesetzt3. Seit der Zeit entstanden eine Reihe moderner,
eigenständiger Programmiersprachen, die sich ausdrücklich als rein objektorientierte
Programmiersprachen verstehen und mit prozeduralen Erblasten vollkommen bre-
chen. Java zählt wie gesagt explizit dazu. Oder von Microsoft die Sprache C#. Diese
Programmiersprachen erzwingen einen streng-objektorientierten Ansatz und unterstützen
im Allgemeinen die wichtigsten Konzepte der objektorientierten Programmierung – al-
lerdings in unterschiedlichem Maße. Andere Sprachen, die in dieser Zeit oder auch spä-
ter entstanden, verfolgen einen hybriden Ansatz und erzwingen keine strenge objekt-
orientierte Programmierung – z. B. JavaScript, Python PHP oder F#.

9.1.7 Funktionales Paradigma

JavaScript war von Anfang an so konzipiert, dass ein hybrides Konzept aus funktionalen
und objektbasiertem Programmierparadigma die Basis gebildet hat. Dabei k­ onzentriert

3 C/C +  + warzwar zu der Zeit auch schon sehr populär, aber eine hybride Sprache, in der man
sowohl objektorientiert als auch funktional programmieren konnte. Und oft wurde in C/C +  + -Pro-
jekten nicht wirklich objektorientiert programmiert – was ich auch eigener Erfahrung weiß.
9.1 Grundlagen der OOP 167

man sich beim funktionalen Ansatz auf die Verwendung von Funktionen als grund-
legende Bausteine zur Problemlösung. Die funktionale Programmierung betont die Aus-
wertung von Ausdrücken und Funktionen. Dabei sind Funktionen als First-Class Citizen
implementiert. Damit werden Funktionen genauso behandelt wie Daten. Sie können als
Argumente an andere Funktionen übergeben, von Funktionen zurückgegeben und in Va-
riablen gespeichert werden. Auf der anderen Seite kann man – wie in JavaScript – glo-
bale Funktionen ebenso als Methoden eines globalen Objekts sehen und verbindet damit
widerspruchsfrei das funktionale und objektorientierte Paradigma. Rein funktionale
Sprachen machen das nicht, aber hybride Sprachen wie JavaScript oder auch Python. Ein
Kernkonzept bei funktionaler Programmierung ist auch die Unveränderlichkeit (Immuta-
bility) von Daten. Das bedeutet, dass sie nach ihrer Erstellung nicht mehr geändert wer-
den können. Statt Daten zu ändern, werden neue Datenstrukturen erstellt. Auch Rekur-
sion ist ein häufig verwendetes Konzept in der funktionalen Programmierung und es gibt
Funktionen höherer Ordnung. Damit können Funktionen andere Funktionen als Argu-
mente akzeptieren und/oder Funktionen zurückgeben.

9.1.8 Alternative Paradigma

Neben objektorientierter und funktionaler Programmierung gibt es auch andere


Programmierparadigmen, die verschiedene Ansätze zur Problemlösung bieten. Hier sind
einige weitere wichtige Paradigmen:

• Bei der prozeduralen Programmierung wird Code in Unterprogrammen organisiert,


die eine Reihe von Anweisungen ausführen, um eine bestimmte Aufgabe zu erledigen.
Prozedurale Programmierung legt den Fokus auf die Verwendung von Prozeduren zur
Strukturierung des Codes und ähnelt einer vereinfachten funktionalen Programmie-
rung.
• Die imperative Programmierung konzentriert sich auf das Ausführen von An-
weisungen in einer bestimmten Reihenfolge,
• Logische Programmierung basiert auf der Verwendung von logischen Aussagen und
Regeln, um Probleme zu modellieren und zu lösen.
• Deklarative Programmierung betont, was berechnet werden soll, anstatt wie es be-
rechnet werden soll. Es beschreibt die gewünschten Ergebnisse, ohne explizit die
Schritte zur Erreichung dieser Ergebnisse anzugeben. SQL ist ein Beispiel für eine
deklarative Programmiersprache.
• In der reaktiven Programmierung werden Programme so strukturiert, dass sie auf Er-
eignisse und Änderungen reagieren. Dies wird oft verwendet, um benutzerfreundliche
und skalierbare Anwendungen zu erstellen, die auf Echtzeitdaten reagieren können. In
Kombination mit Webseiten kommen Sie bei JavaScript über Eventhandler damit in
Berührung. Aber auch Callbacks allgemein lassen sich in diese Theorie eingliedern.
168 9 Objekte und JavaScript

Die Unterschiede zwischen den verschiedenen Programmierparadigmen liegen haupt-


sächlich in der Art und Weise, wie Probleme betrachtet und gelöst werden, sowie in den
Konstruktionsprinzipien, die in jedem Paradigma verwendet werden. Teils kann man bei
einer Sprache auch nicht klar abtrennen, welches Paradigma verfolgt wird und oft ist die
Trennung auch nur akademischer Natur und irrelevant in der Praxis.

9.2 Wie Objekte in JavaScript realisiert werden

Die Umsetzung von Objekten in JavaScript ist genial, effizient und einfach, da sowohl
funktionale als auch objektorientierte Techniken kombiniert werden. Wir hatten ja schon
bei den Arrays festgehalten, dass jedes Array ein Objekt, aber auch jedes Objekt im
Grunde ein (assoziiertes) Array ist – eine Hash-Liste, in der die Keys die Eigenschaften
darstellen. Und da Funktionen in der funktionalen Programmierung als Werte verstanden
werden, die Variablen und damit auch Eigenschaften zugewiesen werden können, ist
damit im Grunde schon alles bekannt, wie ein Objekt in JavaScript aussieht. Etwa so,
wenn man eine JSON-Notation verwendet ([Link]):

"use strict";
let kunde = { name: "Max Mustermann", alter: 42,
     reden: function() { [Link]("Guten Tag.") }
}
[Link]([Link], kunde["alter"]);
[Link]();

Es gibt zwei Eigenschaften (name und alter) sowie eine Methode reden. Diese wird als
innere, anonyme Funktion deklariert und der Rückgabewert als Funktionszeiger dem
Bezeichner reden zugewiesen. Damit wird das zu einer Methode des Objekts, ist aber
gleichzeitig eine Eigenschaft. Der Aufruf eines Members kann sowohl über die Punkt-
notation als auch die Array-Notation erfolgen, was in dem Beispiel aus didaktischen
Gründen beides gemacht wird. Sie können das Objekt aber auch als Array deklarieren
und haben das gleiche Resultat, wobei wir hier das Array nach der Erzeugung erweitern
([Link]):

"use strict";
let kunde = [];
kunde["name"] = "Max Mustermann";
[Link]= 42;
kunde["reden"] = function() { [Link]("Guten Tag.") };
[Link]([Link], kunde["alter"]);
[Link]();
9.2 Wie Objekte in JavaScript realisiert werden 169

Diese maximale Flexibilität sowohl beim Erstellen von Objekten als auch dem Zugriff
auf die Member kann an vielen Stellen interessant sein. Allerdings besteht auch das Ri-
siko, dass der Quellcode beim schlechter les- und wartbar wird.

 Tipp Es gibt ein paar Techniken, die Ihnen im Zusammenhang mit Objekten
nützlich sein können.

1. Test auf Existenz eines Objekts oder einer Eigenschaft ist in JavaScript
ganz einfach. Über eine Anweisung, die eine Bedingung prüft – meist die if-
Anweisung – können Sie testen, ob ein Objekt eine bestimmte Eigenschaft
besitzt oder ob das Objekt vorhanden ist. Existiert die Eigenschaft bzw. das
Objekt (und nur dann), erhalten Sie als Ergebnis true.
2. Die with-Anweisung erlaubt einen verkürzten Zugriff auf mehrere Mem-
ber eines Objekts. Dazu wird nach dem Schlüsselwort with das Objekt in
Klammern angefügt. Dadurch muss im folgenden Anweisungsblock nur
der Name der Eigenschaft ohne den Variablennamen angegeben werden.
Dabei wird für jeden Bezeichner (Variable) zuerst nach einer Eigenschaft in
dem Objekt gesucht, dann nach einem lokalen und globalen Bezeichner.
Die with-Anweisung gilt allerdings als schlecht für die Performance. Vorteil
ist die Reduzierung des Schreibaufwands und der Codegröße. Sie müssen
bei einer Verwendung die Vor- und Nachteile abwägen.
3. Sie können über Objekte wie jedes Array iterieren. Mit normalen Schleifen,
aber vor allen Dingen die for-in-Anweisung. Auch for-of kann manchmal
nützlich sein. Dabei ist es wesentlich, dass Sie dabei die Array-Notation ver-
wenden können. Beachten Sie, dass Standardobjekte von JavaScript aller-
dings in der Regel ihre innere Struktur verbergen. Auf der anderen Seite ist
zu beachten, dass for-in etwa nicht nur über die direkt definierten Eigen-
schaften iteriert, sondern auch über die vererbten Eigenschaften des Proto-
typs. Daher ist es oft ratsam, zusätzliche Bedingungen zu verwenden, um
sicherzustellen, dass Sie nur die gewünschten Eigenschaften erhalten.
4. Mit dem instanceof-Operator können Sie prüfen, ob ein Objekt vom einem
bestimmten Typ ist. In diesem Fall – und nur dann – wird als Ergebniswert
true geliefert.

9.2.1 Array = Objekt?

Wie Sie gesehen haben, ist jedes Array ein Objekt und jedes Objekt ein Array. Zu-
mindest kann man auf alle Eigenschaften eines Objekts auch über die Array-Notation
zugreifen und auf alle Elemente in einem assoziierten Array über die Punktnotation.
Dennoch gibt es Unterschiede zwischen „normalen“ Arrays und JSON-Objekten. Auf
170 9 Objekte und JavaScript

der einen Seite sind numerisch indizierte Arrays zu sehen, die keine „gültigen“ Keys für
Member bei der Punktnotation bereitstellen. Der andere Punkt ist die „Herkunft“ und aus
dieser ergeben sich Unterschiede hinsichtlich der verfügbaren Member der resultieren-
den Objekte. Schauen wir das an und nutzen dazu den Operator instanceof ([Link]):

"use strict";
let kunde1 = [];
let kunde2 = new Array();
let kunde3 = {};
[Link](kunde1 instanceof Array);
[Link](kunde2 instanceof Array);
[Link](kunde3 instanceof Array);

Wie Sie an der Ausgabe erkennen können, ist ein JSON-Objekt keine Instanz von Array.
Damit fehlen sämtliche spezifischen Member eines Arrays. Sowohl JSON-Objekte als
auch assoziierte Arrays können daher zwar immer die gleichen Datenstrukturen ab-
bilden, aber nur echte Arrays haben darüber hinausgehende Funktionalitäten. Das wer-
den wir im nächsten Kapitel besprechen.

9.2.2 Klassen vs Prototypen in JavaScript

Wir haben bisher schon zahlreiche Objekte erzeugt. Etwa Arrays über new Array() oder
auch ein Datumsobjekt über new Date() oder als literal über die JSON-Notation als auch
ein Array-Literal (Objektinitialisierer). Bei der Anwendung von einem Konstruktor kön-
nen Sie nicht erkennen, ob dieser eine Klasse oder ein Prototyp verwendet. Zumindest
in neuen Versionen, wo überhaupt erst Klassen syntaktisch hingeschrieben werden kön-
nen. In älteren Versionen von JavaScript hat sich die Frage deshalb gar nicht gestellt. In
JavaScript gibt es die zwei Hauptmechanismen zur Erstellung von Objekten und ihrer
Strukturierung, die im Resultat gleiche Ergebnisse liefern. Aber von der Schreibweise
und dem Konzept selbst gibt es ein paar Unterschiede:

• In JavaScript basiert die OOP von Natur aus auf Prototypen. Jedes Objekt in Java-
Script hat einen Prototyp, von dem es Member erbt. Entwickler können den Prototyp
eines Objekts direkt manipulieren, um die Struktur und das Verhalten des Objekts zu
ändern. Der oberste Prototyp (die Wurzel) ist Object.
• „Echte“ Klassen stehen erst ab ECMAScript 6 (ES6) in JavaScript zur Verfügung. Sie
bieten aber im Grunde nur eine syntaktische Abstraktion oberhalb des Prototypen-
basierten Ansatzes und erleichtern hauptsächlich die Erstellung von Objekten und ins-
besondere die Vererbung auch ohne tiefere Kenntnis von JavaScript. Mit der Klassen-
syntax können Entwickler OOP in einem Stil betreiben, der ähnlich zu Sprachen wie
Java, C# oder C +  + ist. Oft hört man, dass Klassen damit eine klarere und syntaktisch
ansprechendere Möglichkeit bieten, Objekte zu definieren.
9.2 Wie Objekte in JavaScript realisiert werden 171

Im Gegensatz zu Prototypen bieten Klassen eine direkte syntaktische Möglichkeit zur


Vererbung von Member über die Verwendung von extends. Prototypen erfordern die ma-
nuelle Verwaltung des Prototypketten durch die Zuweisung von [Link]() oder di-
rekte Manipulation des Prototyps.

 In JavaScript werden Klassen letztendlich zu Prototypen konvertiert, wenn der


Code ausgeführt wird. Klassen sind also wirklich nur eine syntaktische Er-
weiterung, um das Prototypen-basierte System von JavaScript einfacher und ver-
trauter für Entwickler aus anderen OO-Sprachen zu gestalten.

9.2.3 Was zeichnet in JavaScript eine Methode gegenüber einer


normalen Funktion aus?

Zuerst klären wir die Frage, was eigentlich in JavaScript eine Methode von einer nor-
malen Funktion unterscheidet? Diese Frage dürfte vor allen Dingen Leser mit Hinter-
grundwissen in der objektorientierten Programmierung ziemlich auf den Nägeln bren-
nen, obwohl die Lösung bereits im Buch mehrfach angedeutet wurde. Die Antwort ist:
NICHTS! Jede Methode ist bloß eine (besondere) Funktion und in JavaScript kann jede
gewöhnliche Funktion unmittelbar als Methode verwendet werden. Sie muss bloß über
eine Funktionsreferenz an ein Objekt oder einen Objekttyp gebunden werden. Entweder
als benannte oder als anonyme Funktion, wie wir es oben gemacht haben.

In der objektorientierten Philosophie ist alles ein Objekt. Außerhalb von Objekten kann damit
nichts existieren, was globale Variablen als auch Funktionen im Grunde unmöglich macht. Obwohl
JavaScript diese Inkonsistenz wie erwähnt durch die Definition des globalen Objekts samt First-
Class Citizens konzeptionell auflöst – eine benannte Funktion, die als Methode verwendet wird,
steht außerhalb einer Objektdeklaration und kann immer auch ohne ein vorangestelltes Objekt als
gewöhnliche Funktion aufgerufen werden kann. Bei der Verwendung einer anonymen Funktion
(Closure) als Methode vermeiden Sie dieses Problem.

[Link] Referenz auf das aktuelle Objekt – this und global


Obwohl sich eine Methode und eine Funktion in JavaScript also eigentlich nur dadurch
unterscheiden, ob sie über ein Objekt oder ohne Objekt verwendet werden, sollte man
this und global nicht außer Acht lassen. Genaugenommen geht es um den Kontext, in
dem die Funktion bzw. Methode aufgerufen wird.

 Das Schlüsselwort this ist eine Referenz auf das aktuelle Objekt.

Das Schlüsselwort this erlaubt einen Aufbau von Funktionen, die allen Ansprüchen der
OOP genügen und zudem nur (sinnvoll) über ein Objekt verwendet werden können.

 Das globale Objekt in JavaScript ist window im Browserumfeld und global in Node.
js-Umgebungen & Co.
172 9 Objekte und JavaScript

Wir schauen einige Beispiele zur Verdeutlichung an. Angenommen, wir wollen in den
ersten beiden Beispielen ([Link] und [Link]) den Namen und das Alter des Kunden
innerhalb der Methoden reden ausgeben. Also etwa so ([Link]):

Beispiel

  reden: function() { [Link](name, alter); } ◄

Das wird nicht gehen. Denn sowohl name als auch alter sind Instanzelemente des Ob-
jekts kunde. Wenn wir die beiden Bezeichner so in einer inneren Funktion verwenden,
werden diese als globale oder lokale Variablen gesucht. Und die gibt es beide nicht. Wür-
den Sie diese jedoch als globale oder auch lokale Variablen in der Methode deklarieren,
„verdecken“ diese die Instanzelemente mit gleichem Namen.

In JavaScript ist nicht wirklich ein Verdecken, wie es etwa in Java oder C# der Fall ist. Ohne
vorangestelltes Objekt sind die Instanzelemente einfach syntaktisch nicht zugänglich.

Betrachten wir zuerst diese Variation der Beispiele weiter oben ([Link]):

"use strict";
var name, alter;
let kunde = { name: "Max Mustermann", alter: 42,
    reden: function() { [Link](name, alter); }
}
[Link]();

Die Ausgabe wir zweimal undefined sein. Wir geben in der Methode die Werte der glo-
balen Variablen aus – nicht die Werte der Eigenschaften. Doch damit kommen Sie aber
an die Werte von name und alter ([Link]).

"use strict";
let kunde = { name: "Max Mustermann", alter: 42,
    reden: function() { [Link]([Link], [Link]); }
}
[Link]();

Sie stellen den Namen des Objekts auch innerhalb der Methode voran. Nur der Nach-
teil dieser Vorgehensweise ist gravierend. Sie brauchen den Namen des aktuellen Ob-
jekts und den haben Sie nicht immer. Er kann sich sogar erst zur Laufzeit ergeben oder
sich ändern. Genau deshalb verwendet man this – eine Referenz auf das aktuelle Objekt.
Ein Stellvertreterbegriff, der vollkommen unabhängig von dem Namen des Objekts ist
([Link]).
9.2 Wie Objekte in JavaScript realisiert werden 173

"use strict";
let kunde = { name: "Max Mustermann", alter: 42,
     reden: function() { [Link]([Link], [Link]); }
}
[Link]();

Betrachten wir nun den Fall, dass die eigentliche Deklaration der Methode reden außer-
halb von der Objektdeklaration notiert wird. Das kombinieren wir mit einem Zugriff auf
this und global. Sowohl innerhalb des Objekts als auch außerhalb. Denn außerhalb einer
konkreten Objektdeklaration haben wir ja das globale Objekt. Was meinen Sie, was this
und global in dem Fall darstellen ([Link]):?

meinGlobaleVa = 42;
let kunde = { name: "Max Mustermann", alter: 42, reden: bla }
function bla() {
       for(let i in this){
           [Link](i,this[i]);
       }
        for (var prop in global) {
            [Link](prop + ": " + global[prop]);
       }
  }
[Link]();
bla();

Wir iterieren in der extern deklarierten Methode sowohl über this als auch global. Wenn
die Funktion als Methode aufgerufen wird, werden über this die drei Eigenschaften (!)
name, alter und reden aufgegeben. Ich habe bei reden explizit von einer Eigenschaft ge-
sprochen, denn das ist ja aus Sicht der Hash-Liste einfach ein Wert, der einem Key zu-
geordnet ist. Die Ausgabe zeigt deutlich, dass es eine Funktionsreferenz ist. Wenn Sie
die Funktion ohne das Objekt aufrufen, hat this keine Eigenschaften. Die Iteration über
global hingegen liefert beim Aufruf über das Objekt als auch als „normale“ Funktion
das gleiche Ergebnis. Beachten Sie, dass der Name für die Eigenschaft reden nicht (!)
identisch mit dem Bezeichner der referenzierten Funktion ist. Dies wurde hier in dem
Beispiel bewusst so gewählt. In der Praxis wird man die Bezeichner jedoch oft identisch
wählen, damit die Beziehung leichter nachzuvollziehen ist. Aber das ist – wie Sie hier
sehen – nicht zwangsläufig notwendig. Wir verwenden in diesem Beispiel bewusst kei-
nen strikten Modus. Die Variable meinGlobalVa landet damit im globalen Objekt und
wird bei der Iteration über global auch ausgegeben. Verwenden Sie "use strict", werden
Sie zur Deklaration mit var oder let gezwungen und die Variablen steht nicht über global
zur Verfügung.
174 9 Objekte und JavaScript

name Max Mustermann


alter 42
reden [Function: bla]

Der Rest der Ausgabe sind die Eigenschaften des globalen Objekts. Damit kann man
this also verwenden, um eine Funktion zu deklarieren, die nur als Methode Sinn macht.
Diese Beschränkung erhöht damit nebenher auch die Softwarequalität.

9.2.4 Ein einzelnes Objekt erweitern

Nähern wir uns dem OOP-Konzept von JavaScript weiter über die nachträgliche Ma-
nipulation von einem Objekt. Ein Objekt in JavaScript als eine dynamische Hash-Liste
lässt sich durch die interne tabellenartige Struktur und die lose Typisierung einfach da-
durch erweitern, in dem man den Bezeichner des Objekts notiert und über die Punkt-
oder Array-Notation die neu gewünschte Eigenschaft samt Wertzuweisung hinschreibt.
Es entsteht dann einfach ein neuer Eintrag in der Tabelle, die die Struktur von dem Ob-
jekt abbildet. Das Schlüssel-Werte-Paar wird in der Tabelle angehängt, wobei wir so
etwas auch schon mit dem Array-Beispiel von oben gemacht haben. Damit kann man
natürlich auch neue Methoden ergänzen, denn diese werden ja als Funktionsreferenz
beim Objekt verankert und die Funktionsreferenz wird einer neuen Objekteigenschaft
zugewiesen. Betrachten wir zuerst, wie ein einzelnes Objekt als Instanz von Object er-
weitert wird ([Link]):

"use strict";
var obj = new Object();
function getWS() {
    return "[Link]
}
[Link] = "RJS EDV-KnowHow";
[Link] = getWS;
[Link] = function() { return "[Link] }
[Link]("Firma:", [Link]);
[Link]("Webseite:", [Link]());
[Link]("Blog:", [Link]());
var obj2 = new Object();
   for(var i in obj2){
        [Link](obj2[i]);
}

Zuerst erzeugen wir ein Objekt vom Typ Object und speichern die Referenz darauf in
der Variable obj. Dieses stellt damit nur die ganz elementaren Eigenschaften bereit,
9.3 Details zu Prototyping 175

die jedes Objekt in JavaScript hat. In der Folge wird die Eigenschaft firma dem Objekt
hinzugefügt. Danach sehen Sie eine neu hinzugefügte Funktionsreferenz auf die Funk-
tion getWS(). Der Name für die Eigenschaft wurde aus didaktischen Gründen auch
hier nicht (!) identisch mit dem Bezeichner der referenzierten Funktion gewählt. Eine
innere Deklaration verwenden wir für Methode getBlog(), die damit ebenfalls über das
erweiterte Objekt zur Verfügung steht. Danach werden die erweiterten Member des Ob-
jekts genauso benutzt, wie Sie Standardeigenschaften und Standardmethoden verwenden
würden. Die Erweiterung erfolgte in dem Beispiel aber explizit nur für ein Objekt. An-
dere Objekte vom Typ Object (und erst Recht von anderen Typen) im gleichen Skript
werden nicht die neuen Eigenschaften haben. Das testen wir einfach indem ein weiteres
Objekt vom Typ Object erzeugen und darüber iterieren. Es wird keine Ausgabe erzeugt.
Der Zugriff auf [Link] würde den Wert undefined liefern.

Für Leser mit Erfahrung in der OOP – die gerade beschriebene Vorgehensweise entspricht der Er-
weiterung der Instanzelemente.

9.3 Details zu Prototyping

Sie haben gerade gesehen, wie einfach Sie in JavaScript bestehende Objekte individuell
erweitern können. Aber geht das auch mit allen Objekten eines Typs in einem Skript?
Ja – über die Erweiterung der Objektdeklaration (also im weiteren Sinn der Klasse) mit-
tels Prototyping. Wenn Sie in JavaScript mit Prototypen arbeiten bedeutet das, dass Ko-
pien bestehender Objekte erzeugt werden. Diese können anschließend manipuliert wer-
den. Und diese Manipulation bedeutet wiederum, dass eine Klasse beziehungsweise alle
darauf aufbauenden Objekte mit neuen Eigenschaften und/oder Methoden erweitert oder
verändert werden. Eine bereits fertige Klasse – bzw. genauer die darauf basierenden Ob-
jekte – kann um eventuell fehlende Funktionalität erweitert werden. Besonders interes-
sant ist Prototyping, wenn die Erweiterung auf eine Objektdeklaration angewendet wird,
wenn bereits ein Objekt existiert. Für ein existierendes Objekt eines Typs bedeutet Proto-
typing, dass es erweitert werden kann, nachdem (!) es bereits erzeugt wurde. Der Grund
ist die schon angedeutete Struktur eines Objekts in JavaScript. Ein Objekt in JavaScript
verhält sich wie gesagt wie eine Hashtabelle, bei der der Name eines Eintrags immer
eine Zeichenkette ist und die einfach durch Hinzufügen eines weiteren Eintrags erweitert
werden kann. Und diese Struktur wirkt sich auf alle Objekte eines Typs aus – egal wann
sie erzeugt werden, denn auch ein Prototyp selbst hat diese Hashstruktur. In JavaScript
besitzt jede Objektdeklaration eine Eigenschaft prototype. Die allgemeine Syntax dazu
ist die folgende:

Beispiel

[Objekttyp].prototype.[Eigenschaft/Methode] = [Wert]; ◄
176 9 Objekte und JavaScript

Sobald diese Aktion durchgeführt wurde, besitzen alle zukünftig erzeugten, aber auch
alle vorher bereits erzeugten Objekte die neue Eigenschaft beziehungsweise Methode.
Und weil es immer wieder gerne falsch gemacht wird – wenden Sie Ihre Aufmerksam-
keit noch einmal auf den Adressaten der Botschaft – es ist beim Einsatz von prototype
der Objekttyp, nicht die Instanz, wie im vorherigen Abschnitt! Des Weiteren ist zu be-
achten, dass nach der prototype-Anweisung erzeugte Objekte für die zugefügte Eigen-
schaft immer den über prototype angegebenen Vorgabewert besitzen, solange dieser
nicht explizit mit einer Zuweisung verändert wurde.

 Beim Prototyping haben einige Browser teils recht tückische Verhaltens-


weisen gezeigt. Insbesondere dann, wenn das Prototyping in externe Dateien
ausgelagert wird und bei einigen Standardklassen von JavaScript angewendet
wird. Teils konnte es rein durch die Position, wo das Prototyping durchgeführt
wurde, zu Problemen kommen. Ich würde allgemein Prototyping nur auf
eigene Objektdeklarationen anwenden.

9.3.1 Prototyping von Objekttypen

Wir wollen in dem konkreten Beispiel zum Prototyping nun auf Basis einer Standard-
klasse von JavaScript einen Prototyp erweitern. Dabei wollen wir die Klasse Object
(die Wurzel der gesamten Objekthierarchie in JavaScript) um die Eigenschaft ort sowie
eine Methode getZufall erweitern, allerdings ein Objekt vom Typ Date erzeugen und
darüber die Eigenschaften verwenden. Das gesamte Verfahren zur Erweiterung geht so
([Link]):

"use strict";
[Link] = "Feldberg";
[Link]= function(dat) { return [Link](); };
var b1 = new Date();
[Link]([Link], [Link]())

Zuerst wird die Standardeigenschaft prototype der Klasse Object verwendet und darüber
die Eigenschaft und die Methode deklariert und gleich ein Wert zugewiesen. Nun wird
ein Date-Objekt erzeugt und in der Folge über dieses Objekt auf die erweiterten Eigen-
schaften zugegriffen. Dies funktioniert, da ja Object die „Superklasse“ von Date ist und
damit auch dort hinzugefügten Eigenschaften zur Verfügung stehen.

 Sie können im Prinzip auch andere Standardtypen von JavaScript erweitern,


wobei – wie erwähnt – einige Konstellationen da nicht zuverlässig sind. Eigene
Typen können Sie so aber immer erweitern.
9.3 Details zu Prototyping 177

9.3.2 Und doch nicht ganz ein Klassenelement

Wenn Sie Erfahrung mit OOP haben – wir scheinen durch die Erweiterung des Prototyps
Klassenelemente bzw. statische Elemente anzulegen. Aber es handelt sich nur um spe-
zielle JavaScript-Varianten der Klassenelemente bzw. statischen Elemente, die gewisse
Besonderheiten besitzen und damit nicht ganz dem üblichen Verständnis entsprechen.
Oder genauer – die konkrete Anwendung spielt wieder eine Rolle, ob es wirklich stati-
sche Elemente sind oder nicht. Schauen wir uns das folgende Skript [Link] an:

"use strict";
var obj1 = new Object();
[Link] = "RJS EDV-KnowHow";
var obj2 = new Object();
[Link]([Link], [Link]);

Zuerst wird ein Objekt vom Typ Object erzeugt, danach (!) die Klasse Object per Proto-
typing erweitert und danach erst ein zweites Objekt vom Typ Object erzeugt. Sie sehen
an der Ausgabe, dass sowohl der Zugriff über das erste als auch das zweite Objekt den
gleichen Wert der Eigenschaft firma liefert. Auch wenn das erste Objekt bereits vor der
Erweiterung der Klasse erzeugt wurde. Hätten wir nun echte statische Elemente, würde
Folgendes gelten: wenn wir den Wert der Eigenschaft firma über den Typ oder aber auch
eines der Objekte ändern, wäre der Wert auch unmittelbar in allen anderen Objekten die-
ses Typs geändert – das typische Verhalten eines Klassenelements, sofern die Änderung
über ein Objekt in der Syntax einer Sprache erlaubt ist (etwa in Java – C# gestattet das
nicht). Betrachten Sie jedoch folgende Erweiterung des Beispiels ([Link]):

"use strict";
var obj1 = new Object();
[Link] = "RJS EDV-KnowHow";
var obj2 = new Object();
[Link]="Weingut St Urban";
[Link]([Link], [Link]);

Das Beispiel ist analog dem Vorgänger aufgebaut. Nur wird über eines der Objekte
(obj1) der Wert der Eigenschaft firma geändert. Die nachfolgende Ausgabe beweist, dass
der Wert von firma in obj2 dadurch jedoch nicht geändert wurde. Wenn wir jedoch über
die Eigenschaft prototype der Klasse/Prototyp den Wert von firma ändern, ist er für alle
Objekte geändert, wie wir davor gesehen haben. Tatsächlich haben wir in JavaScript eine
Art Zwittersituation zwischen Klassen- und Instanzelementen.

Objekte in JavaScript sind – analog normaler Vererbung – mit den Objekten verknüpft, von denen
Sie abstammen. Diese Verkettung endet im Vererbungsbaum spätestens bei [Link]. Wenn
ein Member über ein Objekt aufgerufen und dort nicht gefunden wird, werden über diese interne
178 9 Objekte und JavaScript

Verbindung alle übergeordneten Objekte nach einer passenden Member durchsucht. Sollte die
Suche bis zu [Link] erfolglos verlaufen sein, wird das Ergebnis undefined geliefert.

Um Erstellung von Prototypen samt erweiterter OO-Techniken in JavaScript nun rich-


tig zu verstehen, nehmen wir noch den Weg über die Erstellung eigener Konstruktor-
methoden.

9.3.3 Erstellen von Konstruktormethoden

Sie konnten früher in JavaScript wie gesagt Klassen im strengen Sinn der OOP nicht
hinschreiben und auch heute ist das nur eine Abstraktion des darunterliegenden Prototyp-
konzepts. Im Sinn von Prototypen können Sie jedoch eigene Objektdefinitionen schrei-
ben und daraus dann Objekte erstellen. Damit Sie in JavaScript nun ein Objekt nach
eigenen Vorstellungen anlegen können, sind effektiv zwei Schritte nötig.

• Sie müssen eine eigene Objektdeklaration bzw. Konstruktormethode erstellen.


• Mit Hilfe dieser Objektdeklaration wird dann wie bei vordefinierten Objekt-
deklarationen oder einer literalen Erzeugung eine konkrete Objektinstanz erstellt.

Um aus einer Objektdeklaration explizit mit einem Konstruktor eine neue Objektinstanz
anzulegen, stellt man in einer Anweisung das reservierte JavaScript-Schlüsselwort new
voran, gefolgt von dem Bezeichner der Objektdeklaration und anschließend einem
Klammerpaar (eventuell mit Parametern darin). Dies sieht von der Syntax her schema-
tisch so aus:

Beispiel

[ObjektInstanz] = new [Objektdeklaration]([optionale Parameter]); ◄

Der Konstruktor hat in JavaScript immer den gleichen Bezeichner wie die Objekt-
deklaration. Die einzige Aufgabe eines Konstruktors ist die Erzeugung eines Ob-
jekts. Dabei werden bei Bedarf Initialisierungen vorgenommen, sonstige notwendige
Schritte ausgeführt und vor allen Dingen Speicherplatz für das Objekt reserviert. So-
fern ein Objekt seine Arbeit nicht dadurch vollständig erledigt hat, dass es erzeugt
wurde und unmittelbar dabei einige Arbeitsschritte durchgeführt hat, weist man einer
Variablen eine Referenz auf das Objekt zu. Über den Namen der Variablen können Sie
dann bei Bedarf auf das Objekt zugreifen. Die Zuweisung erfolgt in einem Schritt mit
der Erzeugung. Das reine Zuweisungsverfahren unterscheidet sich nicht von dem Vor-
gang, wie wir ihn bei primitiven Datentypen verwenden. Nur beinhaltet die Variable
eine Referenz auf das erzeugte Objekt (man nennt das in anderen Sprachen auch
einen Pointer – Zeiger).
9.3 Details zu Prototyping 179

[Link] Was zeichnet eine Konstruktormethode aus?


Wie wird nun eine Konstruktormethode deklariert, die dann mit dem vorangestellten
JavaScript-Schlüsselwort new eine Referenz auf ein Objekt zurückgibt. Dies sieht ja von
der Syntax her schematisch so aus:

Beispiel

function [Konstruktormethode](){ … [Beschreibungen von Eigenschaften] …}



var [Referenz auf ObjektInstanz] = new [Konstruktormethode](); ◄

 In JavaScript ist bei jedem Objekt die Eigenschaft constructor standardmäßig vor-
handen. Sie verweist auf den Konstruktor, mit dem das Objekt erstellt wurde.

Jetzt ist das Verwirrende für Umsteiger aus vielen Programmiersprachen wie Java oder
C#, dass an eine Funktion überhaupt keine Anforderungen gestellt werden, damit Sie als
Konstruktor dienen kann! Genau wie bei Methoden. Die „Adelung“ einer gewöhnlichen
Funktion zum Konstruktor erfolgt ausschließlich über die Anwendung mittels des new-
Operators. Wird eine Funktion damit aufgerufen, agiert sie als Konstruktor. Oder anders
ausgedrückt. Jede (!) Funktion kann im Prinzip als Konstruktor verwendet werden. Also
geht sogar so etwas ([Link]):

"use strict";
function meinKonstruktor() {}
var o = new meinKonstruktor();
[Link](typeof o, [Link]);

In dem Code wird einfach eine leere Funktion definiert. Sie hat überhaupt keinen sinn-
vollen Inhalt und ist in keiner Weise besonders gekennzeichnet. Dennoch kann Sie zur
Erzeugung von einem Objekt verwendet werden, was wir new meinKonstruktor(); ma-
chen. Sie sehen an der Ausgabe object [Function: meinKonstruktor], dass die Eigen-
schaft constructor von dem erzeugten Objekt auf diese Funktion referenziert. Zwar wird
das so erzeugte Objekt über keine spezifischen Member verfügen, was die nachfolgende
Iteration über das Objekt zeigt. Aber wir haben ein voll funktionales Objekt erzeugt, was
typeof bestätigt.

[Link] this bei Konstruktormethoden


Allgemein sollte eine Funktion, die als Konstruktor eingesetzt wird, mehr bieten
als nur ein primitives Objekt vom Typ Object zu erzeugen. Bei der Deklaration einer
Konstruktormethode ist wieder das this-Schlüsselwort wichtig für den Zugriff auf das
aktuelle Objekt selbst. Tatsächlich führt der Aufruf einer beliebigen Funktion mit dem
180 9 Objekte und JavaScript

vorangestellten new dazu, dass intern die Variable this auf das gerade neu erstellte Ob-
jekt gesetzt wird. Wenn Sie in JavaScript in der Form this.[variable] in der Konstruktor-
methode eine Variable deklarieren, ist das ein Zugriff auf eine Eigenschaft von dem
irgendwann daraus erzeugten Objekt. Nun schreibt man per Konvention den Namen des
Konstruktors groß. Allgemein beginnen Funktionen in JavaScript per Übereinkunft mit
einem Kleinbuchstaben. Es macht sehr viel Sinn, auch in JavaScript die Namenskon-
ventionen zu übernehmen. Wenn Sie die Regel einhalten kann jeder sofort aufgrund des
Bezeichners einer Funktion bereits erkennen, dass dies ein Konstruktor sein soll (kons-
[Link]).

"use strict";
function reden(was) { return [Link] + ": " + was;}
function Person(personalnummer, rolle) {
    [Link] = personalnummer;
    [Link] = rolle;
    [Link] = reden;
}
var firma = new Array();// Objekterzeugung
firma["chef"] = new Person("001", "Boss");
firma["projektleiter"] = new Person("007", "Projektleiter");
firma["mitarbeiter"] = new Person("010", "Programmierer");
[Link](firma["chef"].reden("Was macht das Projekt?"));
[Link](firma["projektleiter"].reden("Da muss ich meine Mit-
arbeiter fragen."));
[Link](firma["mitarbeiter"].reden("Alles im grünen Bereich."));

Zuerst finden Sie im Quelltext eine gewöhnliche Funktionsdeklaration vor. Allerdings


verwenden wir darin this, um auf eine Eigenschaft eines Objekts zu referenzieren. So
macht die Funktion nur dann Sinn, wenn Sie innerhalb eines Objekts als Methode ver-
wendet wird. In der Deklaration vom dem Konstruktor wird die Funktion reden in Form
einer Funktionsreferenz als Methode verankert. Beim Konstruktor finden Sie auch zwei
Parameter, die jeweils zur Initialisierung der gleichnamigen Eigenschaften verwendet
werden. Die Syntax der Form [Link] = rolle; ist dabei nicht zufällig, sondern in der
Praxis werden Parameter der Funktion (was ja gleichbedeutend mit lokalen Variablen ist)
immer so wie die Eigenschaften benannt. Das macht eine korrekte Zuordnung einfach
und sorgt für eine gute Wartbarkeit. In den folgenden Zeilen werden drei Objekte er-
zeugt. Diese werden in der Folge über ein assoziatives Array verfügbar gemacht. In der
Folgenwerden spezifische Aufrufe der Methode reden für die jeweils spezifischen Eigen-
schaften und Anwendungen aufgerufen.

 Tipp Natürlich können Sie auch bei selbstdefinierten Klassen/Konstruktoren


das Prototyping-Verfahren verwenden, um die Klasse zu erweitern. Sie kön-
nen also so etwas machen:
9.3 Details zu Prototyping 181

[Link] = "Fix & Fertig";


Mit der Anweisung wird Prototyping auf Person angewendet. Diese Zeile
erweitert die Konstruktormethode um die Eigenschaft firmenname, die dabei
den Vorgabewert "Fix & Fertig" zugewiesen bekommt.

[Link] Klassendefinitionen
Zwar kann man in gewisser Weise in JavaScript Klassen definieren, indem man
Konstruktormethoden wie eben geeignet aufbaut. Nur sind das für OO-Puristen keine
„echten“ Klassen im strengen Sinn. Ungeachtet der schon erwähnten Tatsache, dass
auch die neue Klassensyntax in JavaScript unter der Oberfläche weiter Prototyping ist,
kann man zumindest syntaktisch Klassen in der allgemein üblichen Notation erstellen,
bei dem das Schlüsselwort class dem Klassenbezeichner vorangestellt wird. Im Inneren
werden als wichtigsten Part der neuen Syntax die bisherige Notation eines Konstruktors
als innere Funktion notieren mit dem Bezeichner constructor erledigt, nur werden man-
che bisher mehr oder weniger freiwilligen Verhaltensweisen im Umgang mit solchen
Funktionen in ein festes Konzept für die Verwendung eingezwängt, was der Sache mehr
Sicherheit und Konsistenz gibt. Innere „Hilfsfunktionen“ werden ohne function in die
Klasse geschrieben. Nehmen wir einmal einen Konstruktor, den wir hier gerade definiert
haben und verlagern ihn in eine Klasse und ändern ein bisschen die Funktionalität (klas-
[Link]).

"use strict";
class Person {
    ausgabe(){
      [Link]("Zuteilen des Mikros");
    }
    constructor(personalnummer, rolle) {
      [Link] = personalnummer;
      [Link] = rolle;
      [Link] = function (was) {
        [Link]();
        return [Link] + ": " + was;
        };
    }
}
var p = new Person("007", "Projektleiter");;
[Link]([Link]("Heiße Luft"));

Sie haben bei Bedarf also eine Reihe von lokalen Hilfsfunktionen und dazu finden Sie
als Kern wieder den Konstruktor, mit dem das Objekt erstellt wird. Ganz wie bisher und
Sie sehen, dass die Unterschiede eigentlich marginal sind und man auch deshalb heute
schon nahezu widerspruchsfrei bei der Objektbeschreibung mit einem Konstruktor von
einer Art Klasse sprechen kann. Allerdings sind die Möglichkeiten so einer expliziten
Klassendeklaration umfangreicher, da es z. B. Vererbung auf einfache Weise gibt.
182 9 Objekte und JavaScript

9.4 Vererbung

Früher hatten Sie in JavaScript hinsichtlich Vererbung einigen Aufwand zu betreiben und
keinen einfachen Weg über Schlüsselwort extends wie in Java oder C# zur Verfügung,
um darüber die Superklasse von einer Klasse bei deren Deklaration explizit anzugeben.
Das wird mittlerweile aber unterstützt. Aber der klassische Weg mittels der Eigen-
schaften constructor und prototype eines jeden Objekts steht immer noch zur Verfügung,
um eine Vererbung aufzubauen. Nicht nur das. Wie erwähnt, wird die Abstraktion über
extends im Hintergrund immer noch diesen Weg geht.

9.4.1 Vererbung mit Prototyping

Betrachten wir erst die JavaScript-Datei zur Vererbung in JavaScript per Prototyping
([Link]):

"use strict";
//Konstruktormethode Superklasse
function Person(name, vorname) {
    [Link] = name;
    [Link] = vorname;
}
//Konstruktormethode Subklasse
function Mitarbeiter(name, vorname, pid) {
    [Link](name, vorname);
    [Link] = pid;
}
[Link] = new Person();
var pfoertner = new Mitarbeiter("Feuerstein", "Fred", "007");
[Link]([Link] + " " + [Link] + " - Kennung:
" + [Link] + "\n" + "Ist 'vorname' eine eigene Eigenschaft von
dem Objekt? " + [Link]("vorname"));

Zuerst sehen Sie eine Funktion Person(name, vorname), die als Konstruktormethode
für eine „Superklasse“ dienen soll. Die Methode macht nicht mehr als, dass generier-
ten Instanzvariablen eines daraus erzeugten Objekts die Werte der Übergabeparameter
zugewiesen werden. In den folgenden Zeilen finden Sie eine weitere Funktion Mit-
arbeiter(name, vorname, pid), die als Konstruktormethode für die „Subklasse“ geplant
ist und die von der Superklasse erben soll. Beachten Sie, dass die ersten beiden Para-
meter denen der Funktion Person() entsprechen. Aus dem dritten Parameter dieser Funk-
tion wird eine neue Eigenschaft geschaffen. Die ersten beiden Parameter der ­Funktion
9.4 Vererbung 183

werden aber per [Link](name, vorname); an die constructor-Eigenschaft der


„Superklasse“ weitergereicht und ist der eine entscheidende Part zum Aufbau einer
Superklasse-Subklassen-Verbindung. Dieser Schritt muss aber zwingend mit der Kopp-
lung der prototype-Eigenschaft der Subklasse an ein Objekt der Superklasse verbunden
werden, sonst läuft das System in eine endlose rekursive (und erfolglose) Suche nach
den „vererbten“ Eigenschaften ([Link] = new Person();). Nachfolgend
erzeugen wir dann ein Objekt vom Typ Mitarbeiter und fragen dann die vorhandenen
Eigenschaften ab – sowohl die vererbten als auch die in Mitarbeiter neu hinzugefügten
Eigenschaften. Die Methode hasOwnProperty erlaubt dabei eine Überprüfung, ob ein
Objekt über eine bestimmte Eigenschaft verfügt oder nicht.

9.4.2 Vererbung mit class und extends

In modernen Versionen von JavaScript bzw. ECMAScript kann man das sonst übliche
syntaktische Konzept der Vererbung bei Klassen nutzen. Wenn Sie in JavaScript eine
Subklasse von einer Superklasse erzeugen wollen, nutzen Sie dazu bei der Deklaration
der Subklasse das Schlüsselwort extends, gefolgt von der Superklasse. Im Konstruktor
der Subklasse greifen Sie mit der Methode super auf den Konstruktor der Superklasse zu
und reichen damit die Werte aus der Subklasse weiter. Beispiel: [Link].

"use strict";
class Person {
    constructor(name, alter, geschlecht) {
      [Link] = name; [Link] = alter; [Link] = geschlecht;
    }
}
class Mitarbeiter extends Person {
    constructor(name, alter, geschlecht, id) {
      super(name, alter, geschlecht);
      [Link] = id;
    }
}
let mitarbeiter1 = new Mitarbeiter("Max Mustermann", 25, "männlich",
4711);
[Link](mitarbeiter1);

Die Klasse Person deklariert bereit drei Eigenschaften. Die Subklasse Mitarbeiter er-
gänzt eine weitere Eigenschaft. Damit benötigt der Konstruktor von Mitarbeiter vier An-
gaben.
184 9 Objekte und JavaScript

9.5 Datenkapselung

Bei der echten objektorientierten Programmierung ist ganz wesentlich, dass ein Ob-
jekt seine innere Struktur vollständig vor anderen Objekten oder überhaupt einem di-
rekten Zugriff von außen verstecken kann. Man nennt dies Information Hiding oder
Datenkapselung. Um das zu gewährleisten, stellen viele objektorientierte Sprachen so-
genannte Zugriffsmodifizierer bzw. Zugriffsmodifier zu einer sehr feingliedrigen Steue-
rung des Zugriffs auf die Objektinterna zur Verfügung. Etwa die Zugänglichkeit über die
Schlüsselworte private, protected oder public, wenn man an Java oder C# denkt. Diese
gibt es in JavaScript zwar auch als reservierte Schlüsselworte4, aber für sie gibt es keine
Implementierung. Oder kurz und brutal – sie werden in JavaScript nicht unterstützt. Aber
das ist gut so! Denn meiner Meinung nach ist das JavaScript-Konzept schlanker, einfach
und effizienter und der Verzicht auf Sichtsbarkeitsmodifierer keinerlei Einschränkung,
sondern ein Gewinn5. Um jetzt aber bereits eine Zugriffsregelung bei JavaScript-
Objekten durchzuführen, muss man in JavaScript eine besondere Art der Programmie-
rung wählen. Ein Weg führt über den Einsatz von lokalen Variablen. Denn mit var oder
let spezifizierte Variablen in Funktionen sind nur dort zugänglich. Und wenn man dann
noch bei den Funktionsreferenzen zur Implementierung von Methoden anonyme oder in-
nere Funktionen (Closures) verwendet, bekommt man ohne die Notwendigkeit von Zu-
griffsmodifizierern eine echte Zugriffsreglementierung hin. Dann können Sie auch über
gewisse Konventionen Techniken wie Getter oder Setter samt Nur-lese-Eigenschaften
(read-only) als auch Nur-schreibe-Eigenschaften (write-only) auch in JavaScript um-
setzen.

 Settermethoden oder kurz Setter sind Methoden/Funktionen zum indirekten Schreib-


zugriff auf Eigenschaften eines Objekts. Analog sind Gettermethoden oder kurz Getter
Methoden/Funktionen zum indirekten Lesezugriff auf die Eigenschaften. Sie können
damit den Zugriff auf eine Eigenschaft mit einer gewissen Logik versehen und zudem
steuern, ob ein Zugriff nur lesend (kein Setter), nur schreibend (kein Getter) oder beides
sein darf. Diese Steuerung der Art des Zugriffs auf Objekteigenschaften ist in der OOP
sehr populär und unabdingbare Basis von guter Softwarequalität.

So könnte man bei einem Konstruktor Datenkapselung mit Gettern und Settern um-
setzen:

4 Im strikten Modus wird eine Variable mit Namen private beispielsweise einen Fehler auslösen.
Ohne den strikten Modus könnten Sie diesen Bezeichner sogar verwenden, aber das ist echte „Sa-
botage“ des Quellcodes.
5 Wie jeder Verzicht auf redundante oder überflüssige Syntaxkonstrukte.
9.5 Datenkapselung 185

Beispiel

function RJS() {
  var firmaInternerName = "RJS EDV KnowHow";
  var urlInternerName = "";
  [Link] = function() { return urlInternerName;};
  [Link] = function(name) { urlInternerName = name;};
  [Link] = firmaInternerName;
}◄

Eigentlich ist es nicht notwendig, hier irgendetwas zu erklären, so einfach und logisch ist
das in JavaScript, wenn die genialen Konstruktionen von Closures und Funktionen sowie
lokalen Gültigkeitsbereichen konsequent angewendet werden. Dennoch ganz kurz:

• Alles, was nicht nach außen gehen soll, wird im lokalen Gültigkeitsbereich gehalten.
• Alles, was nach außen gehen soll, wird über this oder gegebenfalls ein intern er-
zeugtes Objekt als Rückgabewert zugänglich gemacht.
• Durch die Namenskonvention von Gettern und Settern ist der Zugang für die An-
wendung streng logisch und offensichtlich.

Nun soll aber noch eine kleine Abwandlung einen Trick zeigen, der dem Fall vorbeugt,
dass der Konstruktor wie eine „normale“ Funktion (also ohne new) aufgerufen wird.

Beispiel

function RJS() {
  var name = new Object();
  var firmaInternerName = "RJS EDV KnowHow";
  var urlInternerName = "";
  [Link] = function() { return urlInternerName;};
  [Link] = function(name) { urlInternerName = name;};
  [Link] = firmaInternerName;
  return name;
}◄

In dieser Objektdeklaration wird mit var name = new Object(); ein einfaches Objekt an-
gelegt und in einer lokalen Variable gespeichert. Da name ein Objekt repräsentiert bzw.
auf ein Objekt verweist, können wir dieses einfach durch Hinzufügen einer neuen Eigen-
schaft erweitern. Die lokale Variable wird also anstelle von this verwendet. Sonst bleibt
alles wie vorher. Der Trick ist nur, dass der Konstruktor mit return name; das Objekt
zurückgeben wird. Und zwar egal, ob der Aufruf mit new RJS() oder nur RJS() erfolgt.
Hier sehen Sie erneut, dass sich Konstruktormethoden und konventionelle Funktionen
in JavaScript nicht unterscheiden. Nur die Art des Aufrufs macht den Unterschied. Da
186 9 Objekte und JavaScript

wir aber unsere Funktion hier speziell gebaut haben, agiert die Funktion bei dieser Ver-
wendung ohne new im Sinn einer sogenannte Factory bzw. so, wie wir es schon bei ei-
nigen Standardfunktionen in JavaScript gesehen haben, die mit und ohne new ein Objekt
zurückgegeben haben. Unsere Funktion ist also sowohl als Konstruktor mit einer sinn-
vollen Datenkapselung als auch Funktion zum Erzeugen eines Objekts identisch zu ge-
brauchen.
Sie sehen also, dass eine Datenkapselung in JavaScript nicht nur möglich, sondern
sogar sehr effizient umzusetzen ist. Aber das ist– wie die Vererbung – in JavaScript nur
durch etwas trickreiche Programmierung möglich, bei der man sich strikt an eine ge-
wisse Art der Programmierung halten muss. Die Freiheit und Flexibilität von JavaScript
lässt als Preis auch Programmierung zu, die in vielen anderen objektorientierten Spra-
chen durch ein umfangreiches Gitter als Zwangsbedingungen unterbunden wird.

9.6 Module bei OOP

Im letzten Kapitel wurden Module bereits besprochen. Diese können auch objekt-
orientiert eingesetzt werden, um eine weitere Strukturierungsmöglichkeit zu schaffen.
Sie können Objekte, Konstruktoren bis hin zu Klassen auch in Modulen bereitstellen.
Da muss nichts wirklich Neues gezeigt werden. Ganz einfach wäre das so, wenn man
ein kurzes Beispiel zu ES6-Modulen ansehen will. Die Datei [Link] importiert einen
Konstruktor und eine Klasse aus dem Modul myModule im Verzeichnis es6module.

// [Link]
import { meinKonstruktor, MeineKlasse } from './es6module/myModule.
mjs';
new meinKonstruktor();
new MeineKlasse();

In der Datei [Link] sind der Konstruktor und die Klasse wie gewohnt deklariert,
nur mit einem vorangestellten export versehen.

// [Link]
export function meinKonstruktor() {}
export class MeineKlasse{}

9.7 Frameworks und POJO

RIAs mit ihren reichhaltigen Möglichkeiten verändern die Art der Nutzung des WWW.
Ebenso wird die Bedeutung von klassischen Desktop-Applikationen neu positio-
niert. Viele früher nur als Desktop-Applikation genutzte Programmtypen finden sich
9.7 Frameworks und POJO 187

p­ lötzlich im Web und werden mit dem Browser ausgeführt. Seien es persönliche Ka-
lender, vollständige Office-Programme, Spiele, Routenplaner, ganz integrierte Ent-
wicklungsumgebungen oder Kommunikationsprogramme. Auch mobile Webseiten
oder Anwendungen für Handys oder Smartphones basieren mehr und mehr auf Web-
Technologie. Dies verändert nicht zuletzt das Anwenderverhalten wie auch die An-
wendererwartung bei Internet-Applikationen im Allgemeinen sowie der Verfügbarkeit
von Leistungen. RIAs stehen auf der einen Seite als klassische Web-Applikationen (aber
mit einem gewissen Mehrwert) immer zur Verfügung, wenn man einen halbwegs schnel-
len Internetzugang und einen modernen Browser hat. Andererseits sind sie teilweise von
der Bedienung, der Performance wie auch Optik mittlerweile fast gar nicht mehr von
klassischen Desktop- oder Mobil-Applikationen zu unterscheiden.

9.7.1 Frameworks und Toolkits

Über die letzten Jahre hat sich die Einstellung durchgesetzt, dass eine Erstellung von
RIAs ohne den Einsatz von Web-Frameworks oder Toolkits nur schwer zu bewerk-
stelligen, zumindest nicht mit einem adäquaten Aufwand. Diese Tendenz hat sich zwar
abschwächt und wieder häufiger findet man Aussagen, dass man die Abhängigkeit von
Frameworks und den teils hohen Overhead vermeiden möchte, aber dennoch sind Frame-
works im Web immer noch sehr verbreitet. Was genau ein Framework ist und wie es sich
von einem Toolkit unterscheidet, ist nicht standardisiert. Sowohl eine verbindliche Defi-
nition als auch eine Abgrenzung ist nicht einfach. Allgemein versteht man unter einem
Framework ein Programmiergerüst, das bestimmte Funktionalitäten bereitstellt. Ein Fra-
mework ist selbst kein fertiges Programm, sondern stellt den Rahmen (Frame) zur Ver-
fügung, innerhalb dessen ein oder mehrere Programmierer eine Anwendung erstellen.
Ein Framework beinhaltet in der Regel eine Bibliothek mit nützlichen vorgegebenen
Code-strukturen (meist auf Basis einer spezifischen Sprache wie JavaScript), legt aber –
im Gegensatz zu einer reinen Bibliothek – auch eine Steuerung von Verhaltensweisen
bei der Verwendung fest (z. B. eine Syntax bzw. Grammatik). Frameworks können auf
verschiedene Weise arbeiten. Beliebt sind JavaScript-Frameworks, die dem Programmie-
rer im Browser Unterstützung bei JavaScript-Aktionen liefern. Aber auch aufseiten des
Webservers gibt es Frameworks und im Browser bekommen Besucher meist gar nicht
mit, dass Webseiten im Hintergrund von einem Framework generiert wurden. Dazu gibt
es Frameworks, die bestimmte Entwurfsmuster bereitstellen sowie spezielle Techniken
auf dem Server fordern.
Bei einem Toolkit steht dagegen mehr die Sammlung an Programmen (Tools) im
Fokus, die aber durchaus auch auf spezifischen Bibliotheken und einem Syntaxkonzept
aufsetzen können. Sowohl ein Framework, aber vor allen Dingen Toolkits stellen viel-
fach sogenannte Widgets beziehungsweise Komponenten zur Verfügung. Dabei handelt
es sich in der Regel um Elemente, aus denen eine grafische Benutzerschnittstelle (UI –
User Interface oder GUI – Graphical User Interface) zusammengesetzt wird.
188 9 Objekte und JavaScript

Nun gibt es verschiedene Arten von Frameworks. Ein sehr populäres, reines Java-
Script-Framework ist jQuery ([Link] Das clientseitige Framework ist auch
Grundlage weiterer darauf aufbauender Frameworks und unterstützt beim Programmie-
ren von JavaScript durch eine darauf aufbauende zusätzliche Syntax und sowie kom-
fortable erweiterte Funktionalitäten. Andere JavaScript-Frameworks setzen auf Ent-
wurfsmuster wie etwa [Link] ([Link] Dabei werden meist die Logik der An-
wendung von der Benutzeroberfläche getrennt. Es gibt zahlreiche Frameworks mit noch
einigen anderen Ansätzen, aber im Kern wird bei allen Web-Frameworks zumindest im
Client auf JavaScript gesetzt.

9.7.2 POJOs

Im Zusammenhang mit OOP fällt in der letzten Zeit verstärkt der Begriff POJO. Das
steht für „Plain Old JavaScript Object“ und bezeichnet ein einfaches JavaScript-Objekt,
das keine besonderen Methoden oder Eigenschaften hat, die von speziellen Klassen oder
Prototypen abgeleitet sind. POJOs sind im Grunde genommen nur Container für Daten.
Sie enthalten normalerweise nur Eigenschaften (auch bekannt als Schlüssel-Wert-Paare),
die einfache JavaScript-Typen wie Zeichenketten, Zahlen, Booleans, Arrays oder andere
POJOs sein können. POJOs sind in JavaScript weit verbreitet und gewinnen immer mehr
Bedeutung, da sie flexibel und einfach zu erstellen sind und oft als Datenstrukturen ver-
wendet werden, um Informationen zu speichern und zu übertragen. Sie sind in gewisser
Weise eine Gegenbewegung zu der vorherigen Tendenz, Objekte immer aufwändiger und
komplexer zu machen. Gerade viele Frameworks im Umfeld von JavaScript haben mit
der Zeit sehr komplexe Objekte entwickelt, wobei auch gerade in Bezug auf Frameworks
POJOs eine wichtige Rolle spielen können, wenn diese auf dem Prinzip der „Konvention
vor Konfiguration“ basieren oder die Datenmodellierung und Datenaustausch verein-
fachen möchten. Ein Beispiel in der JavaScript-Welt ist Angular. Einige Frameworks er-
möglichen auch die Bindung von POJOs an Benutzeroberflächenelemente oder Seriali-
sierung und Deserialisierung über POJOs.
Eingebaute Objektdeklarationen
10

Übersicht
JavaScript beinhaltet einige „eingebaute Klassen“, aus denen native Objekte er-
zeugt werden oder die bereits ohne Instanziierung direkt genutzt werden können.
In JavaScript sind die Objektdeklarationen und damit auch sämtliche nativen
Objekte alle von einer Grunddeklaration abgeleitet – der Wurzel Object. Diese
Objektdeklaration ist also eine Art Superklasse aller Objektdeklarationen im
JavaScript-Klassenbaum, auch wenn man – wie wir mehrfach schon besprochen
haben – genaugenommen einen Prototyp vorliegen hat. Bereits dieser globale
Objekttyp enthält alle Grundeigenschaften und -methoden, die jedes Objekt dann
beinhaltet. Dies sind die Grundfunktionalitäten, die eine Struktur braucht, um
im System überhaupt als Objekt zu agieren. Alle Objekte bei JavaScript erben
also von wenigstens einem anderen Objekt. In diesem Kapitel betrachten wir die
Standardobjekte von JavaScript, welche vom API bereits verfügbar sind.

10.1 Alles sind Objekte

Bei JavaScript ist im Grunde sowieso fast alles ein Objekt. Sogar alle einfachen Typen,
mit Ausnahme von Sondertypen wie null oder undefined (was ja auch keine echten Typen
sind), werden wie Objekte behandelt. Damit haben auch diese einfachen Typen Member.
Die build-in-Funktionen sind von der Philosophie her sowieso Methoden des globalen
Objekts. Etwas inkonsistent in JavaScript ist jedoch, dass numerische Variablen zwar
als Objekte verstanden werden, numerische Literale aber nicht, die damit keine Mem-
ber bereitstellen. Das ist meines Erachtens deshalb inkonsistent, weil String-Literale
und auch boolesche Literale explizit Objekte darstellen und man direkt darüber Member

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 189
Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
190 10 Eingebaute Objektdeklarationen

v­ erwenden kann. Aber die Notation zum Zugriff auf Member wäre bei numerischen Li-
teralen auch schwierig, weil die Punktnotation ausscheidet. Der Punkt ist ja Bestandteil
eines numerischen Gleitkommaliterals.

Beispiel

alert([Link]()); // Das geht so nicht ◄

Aber das geht:

Beispiel

var a = 5;
alert([Link]());
alert([Link]());
alert("das geht".toUpperCase()); ◄

10.2 Standardobjekte in JavaScript erzeugen

Um in JavaScript selbst Objekte zu erzeugen, benötigen Sie bekanntlich im Allgemeinen


zwei Dinge:

• Entweder ein passendes Literal (Array-Literal oder JSON) oder meist eine Objekt-
deklaration bzw. Prototyp/Klasse als Bauplan für das zu erzeugende Objekt und seine
Member.
• Einen Mechanismus, der im nicht-literalen Fall mithilfe dieses Bauplans ein konkre-
tes Objekt erstellt. Also einen Konstruktor.

JavaScript stellt nun eine Reihe interner Klassen/Prototypen bereit, aus denen Sie auf
diese Weise Objekte erzeugen können. Schauen wir uns ein paar konkrete Beispiele an,
welche Standardobjekte von JavaScript erzeugen:

Beispiel

d = new Date();
b = new Array();
datum = new Date(2025,11,31,1,0,0);
resObj = new XMLHttpRequest();
meinBild = new Image(); ◄

Zur Erzeugung von Objekten gibt es in ECMAScript auch eine Methode [Link], um ein
Prototype-Objekt ohne die Definition einer Konstruktorfunktion auszuwählen. Wir gehen darauf
nicht weiter ein.
10.3 Klassenmethoden und Klasseneigenschaften verwenden 191

10.3 Klassenmethoden und Klasseneigenschaften verwenden

Um Member von Objekten nutzen zu können, muss man wie gesagt allgemein zu-
erst eine Objektinstanz auf dem beschriebenen Weg erzeugen. Danach kann man über
die Objektinstanz die verfügbaren Instanzelemente verwenden. Es gibt aber in der all-
gemeinen OOP auch das Konzept sogenannter Klassenelemente. Hierbei wird ein Mem-
ber direkt von einer Klasse zur Verfügung gestellt. Dies bedeutet mit anderen Worten,
dass Sie solche Elemente nutzen können, ohne vorher mit einem Konstruktor oder lite-
ral eine konkrete Instanz erzeugt zu haben. Bei der Botschaft stellen Sie stattdessen die
Klasse als Adressat voran. Solche Elemente werden bei JavaScript dann zur Verfügung
gestellt, wenn die konkrete Erzeugung einer Objektinstanz wenig Sinn macht oder un-
nötig aufwendig wäre. Dabei ist es irrelevant, ob wir diese Elemente Klassenelemente
oder Elemente eines Prototyps nennen. Im Fall von Object gibt es etwa die Eigenschaft
prototype, die wir schon im letzten Kapitel gesehen haben. Weitere Beispiele stehen vor
allen Dingen über Math zur Verfügung. Diese Objektdeklaration stellt Konstanten und
Methoden für mathematische Vorgänge bereit. Es wäre wenig sinnvoll, wenn Sie zuerst
ein Objekt erzeugen müssten, um eine Methode zur Verfügung zu haben, die beispiels-
weise eine Kommazahl auf eine ganze Zahl runden soll. Der Vorgang ist grundsätzlich
für alle Zahlen identisch und es hängt nicht davon ab, was für eine konkrete Objekt-
instanz Sie verwenden würden. Das Objekt selbst wäre vollkommen uninteressant für
die beabsichtigte Aktion. Hier macht es viel mehr Sinn, wenn Sie einfach die Objekt-
deklaration und dann die passende Methode verwenden. Beispiel:

Beispiel

[Link]
[Link]()
[Link]()
Math.E
Math.SQRT2 ◄

Klassennamen bzw. die Bezeichner von Prototypen beginnen in JavaScript grundsätzlich mit
einem Großbuchstaben. Das ist zwar nicht verbindlich, aber eine Konvention, die bei offiziellen
Quellen immer eingehalten wird. Wenn Sie also im Quelltext eine solche Notation finden, bei der
der Adressat einer Botschaft großgeschrieben wird, sollte es sich um eine Klasse bzw. einen Proto-
typ handeln und nicht eine selbst erzeugte Objektinstanz oder den Bezeichner für ein automatisch
generiertes Objekt. Denn diese werden ebenfalls per Konvention immer mit einem Kleinbuch-
staben begonnen. Dies gilt natürlich nur, wenn sich der Programmierer an die allgemein gültigen
Namensregeln hält.
192 10 Eingebaute Objektdeklarationen

10.4 Native Objektdeklarationen in JavaScript

Es gibt in JavaScript einige nativ zu nutzende Klassen/Prototypen, die entweder direkt


genutzt oder instanziiert werden können. Konkret werden wir uns um

• mathematische Objektdeklarationen,
• Datenfelder (kompakt und nur als Erinnerung),
• Datumsoperationen,
• Wahrheitsobjekte,
• Zeichenkettenoperationen,
• Bildobjekte und
• kurz Funktionsobjekte und reguläre Ausdrücke

kümmern. Allen diesen Objekten und Typen ist gemein, dass sie zum Kern von Java-
Script selbst gehören und unmittelbar in jedem JavaScript genutzt werden können. Aller-
dings ist die praktische Bedeutung einiger dieser Objekte eher gering, andere sind recht
abstrakt und andere wiederum sind nicht unbedingt trivial anzuwenden. Aber ein paar
Objekte bzw. ausgewählte Member davon sind von absolut grundlegender Bedeutung.

10.4.1 Math

Ein eingebauter Typ in JavaScript ist Math. Darüber stehen Ihnen diverse Methoden,
Konstanten und Eigenschaften für verschiedene Berechnungen bzw. auf mathematischen
Grundlagen basierende Operationen zur Verfügung.

 Math stellt keinen Konstruktor zur Verfügung. Selbst wenn Sie es wollten, könnten
Sie mit new Math() keine Objekte vom Typ Math erzeugen.

[Link] Eigenschaften bzw. Konstanten


Math besitzt erst einmal eine ganze Menge von Konstanten und Eigenschaften, welche
wir in diesem Rahmen aber nicht alle ausführlich diskutieren wollen. Hier folgt deshalb
nur eine kurze Auflistung wichtigster Eigenschaften bzw. Konstanten (in einem prakti-
schen Beispiel werden wir eine Auswahl verwenden) (Tab. 10.1):

[Link] Methoden
Math besitzt ebenso eine ganze Reihe von nützlichen Methoden, obwohl auch diese
natürlich mathematisch motiviert sind und wir sie in diesem Rahmen ebenfalls nicht alle
bis ins Detail diskutieren wollen (Tab. 10.2).
10.4 Native Objektdeklarationen in JavaScript 193

Tab. 10.1  Eigenschaften von Math


Eigenschaft Beschreibung
E Die Eulersche Konstante
LN10 Der natürliche Logarithmus von 10 – eine Konstante
LN2 Natürlicher Logarithmus von 2 – eine Konstante
LOG10E Logarithmus der Eulerschen Konstante von 10 – eine Konstante
LOG2E Der Logarithmus der Eulerschen Konstante von 2 – eine Konstante
PI Die berühmte Konstante PI
SQRT1_2 Konstante für Quadratwurzel aus 0,5
SQRT2 Eine Konstante für Quadratwurzel aus 2

Tab. 10.2  Eine Auswahl an wichtigen Methoden von Math


Methode Beschreibung
Abs Der absolute Wert des übergebenen Parameters. Dies ist immer ein positiver Wert,
selbst wenn er vorher negativ war.
Cos Kosinus des übergebenen Parameters, einer beliebigen Zahl.
Exp Die Eulersche Zahl hoch dem übergebenen Parameter, einer beliebigen Zahl.
floor Mit floor() ermitteln Sie die nächste niedrigere ganze Zahl. Sie runden also eine Zahl
ab. Beachten Sie, dass das kein kaufmännisches Runden darstellt.
Log Natürliche Logarithmus des übergebenen Parameters, einer beliebigen Zahl. Die Me-
thode ist die Umkehrung von exp().
max Maximum von zwei beliebigen, als Parameter übergebenen Zahlen.
min Minimum von zwei beliebigen, als Parameter übergebenen Zahlen.
pow Die Methode berechnet den Wert des ersten Parameters hoch dem zweiten Parameter
(Exponent) – die Potenz.
random Eine Zufallszahl zwischen 0 und 1 (exklusive).
round Die Methode berechnet die nächste Ganzzahl des übergebenen Parameters, einer be-
liebigen Zahl. Dabei wird die kaufmännische Rundung angewendet. Das bedeutet,
dass ein Nachkommaanteil kleiner als 0,5 abgerundet wird. Größere Nachkomma-
anteile werden aufgerundet. Falls die Zahl selbst ganzzahlig ist, wird sie nicht ver-
ändert.
sin Sinus des übergebenen Parameters, einer beliebigen Zahl.
sqrt Quadratwurzel des übergebenen Parameters, einer beliebigen Zahl.
tan Tangens einer als Parameter übergebenen Zahl.
194 10 Eingebaute Objektdeklarationen

Spielen wir ein vollständiges Beispiel durch ([Link]):

"use strict";
[Link]("Cosinus (40)",[Link](40));
[Link]("Zufallszahl", [Link]());
[Link]("Quadratwurzel 2",Math.SQRT2);
[Link]("Abrunden von PI",[Link]([Link]));

In dem Listing werden einfach Member von Math verwendet.

10.4.2 Instanzen und Elemente von Number

Auch Number dient in JavaScript mathematischen Zwecken. Wir haben damit schon ge-
arbeitet. Sie stellt wie Math Klassenelemente zur Verfügung, allerdings können Sie im
Gegensatz zu Math auch Objekte vom Typ Number erstellen. Im Fall der Anwendung
von Methoden müssen Sie das sogar machen, während Eigenschaften als Klassen-
elemente angesprochen werden. Number hat vor allen Dingen als Typ des Ergebnisses
von mathematischen Operationen und mathematischen Literalen als auch Umgang mit
den Grenzen von mathematischen Wertebereichen Bedeutung. Die Erstellung einer Ins-
tanz von Number geht sowohl über einen Konstruktor als auch deklarativ durch einfache
Zuweisung eines Zahlenliterals oder eines numerischen Ausdrucks zu einer Variablen.
Sie werden vermutlich nur selten Number über einen Konstruktor instanziieren, sondern
meist einfach mit Variablen oder Literalen vom Typ Number arbeiten und darüber kom-
men Sie an die entsprechenden Methoden und – obwohl meist nicht sinnvoll – auch die
Eigenschaften. In der Regel können Sie bei Eigenschaften auch auf das Voranstellen der
Klasse verzichten und einfach den Bezeichner der Eigenschaft notieren. Dieser repräsen-
tiert einen eingebauten Token (Tab. 10.3).
Auf die Eigenschaften von Number können Sie in Vergleichen prüfen, etwa mit if
(ausdruck > = MIN_VALUE) { … }. Allerdings haben wir die Besonderheit von NaN
schon behandelt. Darauf können Sie nur indirekt mit der Toplevel-JavaScript-Funktion
isNaN prüfen (Tab. 10.4).
Beachten Sie, dass dies wie erwähnt allesamt keine (!) Klassenmethoden sind. Ein
Aufruf der Art [Link](4); ist also nicht möglich. Stattdessen wird ein Ob-
jekt vom Typ Number der Methode vorangestellt. Dieses wird entweder deklarativ oder
mit einem Konstruktor erzeugt oder kann auch einfach ein Zahlenliteral sein.

10.4.3 Arrays als Objekte

Erinnern wir uns noch einmal an Arrays. In JavaScript werden alle Objekte über eine
Array-Struktur repräsentiert und umgekehrt wird jedes Array explizit als Objekt gesehen.
10.4 Native Objektdeklarationen in JavaScript 195

Tab. 10.3  Eigenschaften von Number


Eigenschaft Beschreibung
MAX_VALUE Größte positive Zahl, die in JavaScript verwendet werden kann. Das ist
ungefähr der Wert 1.79E + 308.
MIN_VALUE Kleinste positive Zahl, die in JavaScript verwendet werden kann. Das
ist ungefähr der Wert 5e-324.
NaN Der Token steht für Not a Number – das Ergebnis von einer Operation,
die einen nichtnumerischen Wert liefert.
NEGATIVE_INFINITY Repräsentation von Negativ-Unendlich. Das ist der Wert –Infinity.
Diese Eigenschaft steht für alle negativen Werte, die jenseits der mini-
mal in JavaScript möglichen numerischen Zahl liegen.
POSITIVE_INFINITY Repräsentation von Positiv-Unendlich. Das ist der Wert Infinity. Diese
Eigenschaft steht für alle positiven Werte, die jenseits der maximal in
JavaScript möglichen numerischen Zahl liegen.

Tab. 10.4  Methoden von Number


Methode Beschreibung
toExponential Mit der Methode erzwingen Sie die Exponentialschreibweise einer Zahl.
toFixed Bei dieser Methode geben Sie als Parameter die Anzahl der gewünschten Nach-
kommastellen bei der Darstellung der Zahl an. Diese Methode rundet auto-
matisch kaufmännisch.
toPrecision Der Aufruf dieser Methode erzwingt eine bestimmte Genauigkeit bei der Dar-
stellung einer Zahl. Das Verhalten ist aber etwas trickreich. Die Stellen für die
Genauigkeit betreffen sowohl den Vor- als auch Nachkommateil, wobei das
konkrete Verhalten an der Größe und der Darstellung der Originalzahl hängt:
- Ist eine Basiszahl zu groß für die gewünschte Darstellung, wird die Zahl in
Exponentialschreibweise dargestellt.
- Hat die Basiszahl weniger Stellen, als an Genauigkeit gefordert wird, werden
die Stellen nach dem Komma (Punkt) mit Nullen so weit ergänzt, bis die ge-
wünschte Anzahl an Ziffern vorhanden ist.
- Können alle Vorkommastellen dargestellt werden und ist dann die Anzahl der
Nachkommastellen zu groß, werden diese abgeschnitten.
toString Wie jedes Objekt verfügt auch ein Objekt vom Typ Number über diese Me-
thode. Aber diese Methode hier bei Number hat ein paar spezifische Besonder-
heiten. Zum einen wird natürlich ein numerischer Wert in einen String ge-
wandelt. Die Methode besitzt aber auch noch einen optionalen Parameter. Der
Parameter gibt die Basis des gewünschten Zahlensystems an (z. B. 10 für das
Dezimalsystem oder 16 für das Hexadezimalsystem). Der optionale Parameter
kann Werte zwischen 2 und 36 annehmen. Wird kein Parameter angegeben, so
wird die Zahl als Dezimalzahl behandelt.
196 10 Eingebaute Objektdeklarationen

Damit gibt es auch implementierte Member. Die für eine Iteration über das Array wich-
tigste (und einzige) Eigenschaft ist length, was bei indizierten Datenfeldern die Anzahl
der Elemente im Datenfeld enthält. Ganz konkret haben Datenfelder darüber hinaus
noch einige Methoden, wobei deren Bedeutung in vielen Skripten – im Gegensatz zur
Wichtigkeit von Arrays selbst – eher gering ist. Hier ist eine Auswahl wichtiger Metho-
den, wobei diese ausdrücklich eben nicht bei JSON-Objekten bereitstehen (Tab. 10.5):
Die Anwendung der Methoden ist einfach, weshalb wir auf Beispiele hier verzichten.
Allerdings werden die Methoden in einigen Beispielen im Buch teils angewendet.

10.4.4 Objekte vom Typ Boolean

Grundsätzlich werden Wahrheitswerte in JavaScript durch die Token true und false re-
präsentiert. Die Verwendung dieser Token genügt auch in den meisten Fällen! Aber es
gibt auch Wahrheitsobjekte, die in gewissen Programmiersituationen statt der Token
verwendet werden können, und insbesondere sind die Token selbst vom Typ Boolean,
was auch von dem Operator typeof geliefert wird (boolean). Mit dem Konstruktor von
Boolean kann man auch die Token true und false erzeugen. Aber die Besonderheit ist,
dass man dabei statt der Token selbst auch andere Datentypen als Parameter übergeben
kann. Die Zahl 0, der Token null, eine leere Zeichenkette, der Token false selbst oder
auch der leere Konstruktor stehen für false, alle anderen Parameter in der Konstruktor-
methode repräsentieren den Wert true. Damit kann man zum Beispiel automatisierte
Entscheidungsstrukturen oder Rückgabewerte für Funktionen vereinfachen. Dennoch ist
die Anwendung von Wahrheitsobjekten eher selten. Der hauptsächliche Nutzen besteht
darin, dass man damit Wahrheitswerte sauber in ein objektorientiertes Konzept integrie-
ren kann.

10.4.5 Datumsoperationen mit Date

Einer der wichtigsten Objekttypen, die Sie selbst unter JavaScript erzeugen können, ist
ein Datumsobjekt. Dieses wird als Instanz von Date erzeugt. Ein Objekt vom Typ Date
stellt in JavaScript für alle Berechnungen mit Datum und Zeit Funktionalitäten bereit.
Dafür gibt es die unterschiedlichsten Anwendungen.

Der 1. Januar 1970, 0.00 Uhr ist der interne Zeitnullpunkt (The epoch), der in nahezu allen
Skript- und Programmiersprachen als Speicherungs- und Berechnungsbasis für alle Operationen
mit einem Datum dient. Ab diesem Zeitpunkt wird die Zeit numerisch gezählt. Die Einheit, in der
in JavaScript intern Zeit verwaltet bzw. berechnet wird, ist die Millisekunde. Da jedes Datum in-
tern in einer ganzen Zahl verwaltet wird, können Sie mit Datumsobjekten wie mit gewöhnlichen
Zahlen rechnen. Beachten Sie, dass die Methoden eines Datumsobjekts Werte nach dem anglo-
amerikanischen System liefern und etwa bei Monaten der Januar durch eine 0 und nicht wie ge-
wohnt eine 1 repräsentiert wird. Ebenso steht bei Wochentagen die 0 für einen Sonntag.
10.4 Native Objektdeklarationen in JavaScript 197

Tab. 10.5  Methoden eines Arrays


Methode Beschreibung
concat Verketten von Arrays. Parameter ist der Bezeichner des Arrays, das an das als Ad-
ressat der Botschaft angegebene Array (Bezeichner vor dem Punkt) angehängt wird.
Rückgabewert ist ein verbundenes Array aus beiden Arrays. Die Original-Arrays
werden nicht verändert.
join Verbinden aller Elemente im Array so, dass der Rückgabewert eine Zeichenkette
bildet. Das Original-Array wird nicht verändert. Die Methode erwartet als Parameter
die Angabe eines oder mehrerer Trennzeichen. Diese trennen dann die verbundenen
Einträge in der Zeichenkette.
pop Löschen des letzten Elements im Array. Das Array enthält damit ein Element weni-
ger und wurde nach dem Aufruf der Methode also explizit verändert. Rückgabewert
der Methode ist der Inhalt des gelöschten Elements, den Sie damit weiterverarbeiten
können.
push Erweitern des Arrays. Die Methode erwartet als Parameter ein oder mehrere an-
zuhängende Elemente. Vorteil gegenüber dem manuellen Hinzufügen ist, dass die
Elemente unmittelbar an das letzte vorhandene Element im Array angehängt werden.
Die Länge des erweiterten Arrays ist der Rückgabewert. Das Array wird von der Me-
thode explizit verändert.
reverse Umkehrung der Elementreihenfolge in einem Array. Das Original-Array wird von
der Methode damit explizit verändert.
shift Die Methode entfernt das erste Element aus einem Array. Nachfolgenden Elemente
im Original-Array rücken entsprechend eine Stelle nach vorne und das Array enthält
damit ein Element weniger. Rückgabewert der Methode ist der Inhalt des entfernten
Elements.
slice Extrahieren eines Teils aus einem Array, ohne das Original-Array zu verändern. Sie
erhalten das Ergebnis der Extraktion als Rückgabewert. Die Methode erwartet als
ersten Parameter die Indexnummer des ersten zu extrahierenden Elements und als
zweiten Parameter die Indexnummer des Elements, bis zu dem (exklusiv) die Ex-
traktion erfolgen soll. Als zweiter Parameter kann auch ein negativer Wert übergeben
werden. Dann gibt man den Grenzwert vom letzten Element des Arrays aus gesehen
an. Ein Wert von –1 bedeutet also, dass das vorletzte Element noch extrahiert wird,
das letzte Element nicht mehr.
splice Der Aufruf der Methode fügt ein oder mehrere neue Elemente in ein Array ein. Die
Besonderheit ist, dass man als Parameter die gewünschten Stellen angibt. Dabei wer-
den die eventuell vorhandenen Elemente überschrieben. Die Methode verändert also
das Original-Array. Als Parameter geben Sie die Indexnummer im Array an, ab der
das oder die neuen Elemente eingefügt werden sollen. Der zweite Parameter steht für
die Anzahl der zu ändernden Elemente und die folgenden Parameter sind die neuen
Elemente, die meist der Anzahl der einzufügenden Elemente entsprechen werden.
Sie sind aber optional. Wenn Sie nur die ersten beiden Parameter oder zu wenige
Parameter angeben, dient die Methode nur zum Löschen der Elemente, für die keine
Ersatzwerte spezifiziert werden. Notieren Sie mehr Elemente zum Austausch, als Sie
im zweiten Parameter angeben, werden überzählige Elemente ignoriert. Die Methode
liefert als Rückgabewert die ausgetauschten Elemente aus dem Original-Array.

(Fortsetzung)
198 10 Eingebaute Objektdeklarationen

Tab. 10.5 (Fortsetzung)


Methode Beschreibung
sort Die Methode sortiert die Elemente eines Arrays und verändert es damit. Wenn Sie
bei der Methode keinen Parameter übergeben, wird lexikalisch sortiert. Das be-
deutet insbesondere, dass auch numerische Werte intern in Zeichenketten verwandelt
und wie Zeichenketten sortiert werden. Es wird also keine numerische Sortierung
vorgenommen. Sollten Sie so eine Sortierung (oder eine Sortierung nach anderen
Kriterien) benötigen, müssen Sie eine eigene Vergleichsfunktion bauen, die Sie der
Methode als Parameter übergeben können. Dann ist es in der Regel aber einfacher,
wenn diese Funktion selbst bereits die Sortierung mit erledigt.
unshift Mit der Methode können Sie Elemente am Anfang eines Arrays einfügen. Das Array
wird um das oder die als Parameter angegebenen Elemente erweitert. Rückgabewert
der Methode ist das erste eingefügte Element.

Zur Erzeugung einer Objektinstanz von Date stehen mehrere Möglichkeiten in Form ver-
schiedener Konstruktoren zur Verfügung:

Beispiel

[Objektname] = new Date()


[Objektname] = new Date(Jahr,Monat,Tag)
[Objektname] = new Date(Jahr,Monat,Tag,Stunden,Minuten,Sekunden)
[Objektname] = new Date("Monat Tag, Jahr Stunden:Minuten:Sekunden")
[Objektname] = new Date(Millisekunden) ◄

Die erste Variante erzeugt eine Objektinstanz, in der das zum Zeitpunkt der Skriptaus-
führung aktuelle Datum und die aktuelle Uhrzeit (die Systemzeit des Rechners eines
Besuchers) gespeichert wird. Die zweite und die dritte Variante initialisieren das neue
Date-Objekt mit bestimmten vorgegebenen Werten (also einem bestimmten Datum und
im dritten Fall auch einer bestimmten Uhrzeit). Alle Initialisierungswerte (Monat, Stun-
den oder Minuten) müssen in Zahlenform angegeben werden. Die vierte Version ist im
deutschsprachigen Raum eher von geringem Interesse, denn Sie geben die Parameter als
Zeichenketten in englischer Schreibweise an. Die fünfte Variante ist dann interessant,
wenn Sie über eine Funktion oder Berechnung die Millisekunden eines Datumswerts
erhalten und daraus ein Datumsobjekt erzeugen wollen. Dementsprechend steht new
Date(0) genau für den internen Zeitnullpunkt.

[Link] Methoden von Date


Ein Objekt vom Typ Date besitzt eine ganze Reihe von sehr interessanten Methoden.
Hier ist eine Auswahl wichtiger Methoden (Tab. 10.6):
10.4 Native Objektdeklarationen in JavaScript 199

Tab. 10.6  Methoden von Date


Methoden Beschreibung
getDate Monatstag des Datumsobjekts als Zahl. Das liefert beispielsweise 3,
wenn in dem Datumsobjekt der 3.12.2015, 23:59:00, gespeichert ist.
getDay Wochentag des Datumsobjekts als Zahl. Das liefert beispielsweise 3,
wenn in dem Datumsobjekt implizit ein Mittwoch gespeichert ist. Die
möglichen Rückgabewerte sind 0 (Sonntag) bis 6 (Samstag).
getFullYear Abfrage des vollständigen Jahrs – also vierstellig.
getHours Die Stunden.
getSeconds Die Sekunden der im Datumsobjekt gespeicherten Uhrzeit.
getTime Anzahl Millisekunden als Zahl, die seit dem 01.01.1970, 0:00:00 bis zu
dem im Datumsobjekt gespeicherten Zeitpunkt vergangen sind.
getTimeZoneoffset Unterschied zwischen lokaler Zeit und Greenwich Mean Time (GMT) in
Anzahl Minuten zurück.
getUTCDate Monatstag nach UTC-Zeit.
getUTCFullYear Abfrage des vollen Jahres der UTC-Zeit.
getUTCHours Stundenteil der UTC-Uhrzeit.
getUTCMilliseconds Millisekunden der UTC-Uhrzeit.
getUTCMinutes Minuten der UTC-Uhrzeit.
getUTCMonth Monat der UTC-Uhrzeit.
getUTCSeconds Sekunden der UTC-Uhrzeit.
getYear Das Jahr. Man hatte sich darauf geeinigt, dass dieser Wert ab dem Jahr
1900 gerechnet wird. Das bedeutet, für das Jahr 1999 sollte die Methode
99 als Rückgabewert liefern. Leider haben sich in der Vergangenheit
einige Browser nicht an die Vereinbarung gehalten und die tatsächliche
Jahreszahl mit vier Stellen geliefert. Dies kann zu Komplikationen füh-
ren, die programmtechnisch berücksichtigt werden müssen. Oder man
nimmt gleich getFullYear.
parse Diese Methode ermittelt aus einer zu übergebenden Zeichenkette die
Anzahl Millisekunden, die zwischen dem 01.01.1970 0:00:00 und dem
übergebenen Zeitpunkt verstrichen sind. Die Zeichenkette muss dem
IETF-Standard folgen. Beispiele: "Wed, 2Oct 1991 23:59:00 GMT" oder
"Mon, 25Dec 1995 13:30:00 GMT + 0430".
setDate Hiermit ändert man den in dem Objekt gespeicherten Monatstag auf den
als Zahl zu übergebenden Monatstag.
setFullYear Setzen eines vollen Jahres.
setHours Stunden der Uhrzeit auf den als Zahl zu übergebenden Wert seu´tzen.
setMilliseconds Setzen der Millisekunden.
setMinutes Setzen der Minuten.
setMonth Setzt den Monat. Achtung – der Wert 0 steht für Januar.
setSeconds Setzen der Sekunden.

(Fortsetzung)
200 10 Eingebaute Objektdeklarationen

Tab. 10.6 (Fortsetzung)


Methoden Beschreibung
setTime Diese Methode ändert den kompletten Inhalt des Objekts auf einmal
durch Übergeben einer Zahl. Diese Zahl stellt die Anzahl der Milli-
sekunden seit dem 10.01.1970–0:00:00 Uhr dar.
setUTCDate Setzen des Monatstags für UTC-Zeit.
setUTCFullYear Setzen des vollen Jahres für UTC-Zeit.
setUTCHours Setzen der Stunden der UTC-Zeit.
setUTCMilliseconds Setzen der Millisekunden der UTC-Zeit.
setUTCMinutes Setzen der Minuten der UTC-Zeit.
setUTCMonth Setzen des Monats für UTC-Zeit.
setUTCSeconds Setzen der Sekunden der UTC-Zeit.
setYear Das Jahr setzen.
toGMTString Diese Methode wandelt die in dem Objekt gespeicherten Daten in eine
Zeichenkette nach dem IETF-Standard um.
toLocaleDateString Diese Methode wandelt die in dem Objekt gespeicherten Daten in eine
Zeichenkette nur mit dem Datum um.
toLocaleString Umwandeln von Datum und Uhrzeit in eine Zeichenkette. Unterschied
zu toGMTString ist, dass die zurückgegebene Zeichenkette die lokale
Uhrzeit berücksichtigt.
UTC Anzahl Millisekunden, die zwischen dem 1.1.1970–0:00:00 Uhr und dem
übergebenen Zeitpunkt verstrichen sind. Alle Parameter sind als Zahlen-
werte zu übergeben (UTC(Jahr, Monat, Tag [, Stunden] [, Min] [,Sek])).
Die Angabe der Datumswerte ist zwingend, die der Uhrzeitwerte optional.

Beachten Sie, dass ein Datumsobjekt immer Zahlen repräsentiert! Damit sind auf den ersten Blick
eigenartig anmutende Verhaltensweisen bei den Setter-Methoden für Datumseigenschaften wieder
logisch. Aber wie gesagt – im ersten Moment erscheinen sie oft eigenartig, weshalb sie hier be-
sprochen werden sollen. So können Sie mit setHours die Stunden in einem Datumsobjekt setzen.
Nur sollten nach dem Verständnis aus dem täglichen Leben die möglichen Werte zwischen 0 und 23
liegen. Man kann bei der Methode aber auch größere und kleinere Werte verwenden. Das Verhalten
von JavaScript ist jedoch einfach und folgerichtig, wenn man darüber nachdenkt. Ebenso werden
damit einige Datumsoperationen extrem vereinfacht, was das Verfahren letztendlich genial macht,
wenn man sich daran gewöhnt und der Aussagelogik des Bezeichners einer Methode etwas gelöst
hat. Wenn Sie beispielsweise setHours(25) angeben, ist das einfach 2 Uhr am Folgetag. Vollkommen
analog verhält es sich bei den anderen Methoden wie setMinutes, setMonth oder setSeconds. Be-
achten Sie wie gesagt, dass diese Methoden nur ein Intervall an Millisekunden beeinflussen und die
letztendlich damit berechnete Zahl immer einen sinnvollen Wert ab dem 1. Januar 1970 darstellt.

Wir werden jetzt einige weitere Übungen durchführen, in der diverse der oben be-
schriebenen Methoden ausprobiert werden können und die auch für die Praxis einige Re-
levanz haben.
10.4 Native Objektdeklarationen in JavaScript 201

[Link].1 Erzeugen von Datumsobjekten mit einem vorgegebenen Datum


Wie beschrieben gibt es verschiedene Varianten, um ein Datumsobjekt mit einem vor-
gegebenen Datum zu erzeugen. Die sehen Sie jetzt in der Anwendung, wobei ein paar
Besonderheiten demonstriert werden. Erzeugen wir als Beispiel die nachfolgende Datei
([Link]):

"use strict";
var a = new Date(2024, 10, 5, 12, 45, 59, 4);
var b = new Date(2024, 10, 5, 12, 45, 59);
[Link](new Date());
[Link]("new Date(\"october 03, 2028 12:11:03\") " +
new Date("october 03, 2028 12:11:03") );
[Link]("new Date(2026, 10, 5) " + new Date(2026, 10, 5));
[Link]("a: new Date(2014, 10, 5, 12, 45, 59, 4) " + a);
[Link]("b: new Date(2014, 10, 5, 12, 45, 59) " + b);
[Link]("(a - b): new Date(2026, 10, 5, 12, 45, 59, 4) " + (a -
b));

   "- new Date(2026, 10, 5, 12, 45, 59) " +

Zuerst erzeugen wir ein Datumsobjekt a mithilfe des Konstruktors mit sieben numeri-
schen Parametern und dann ein Datumsobjekt b mit dem Konstruktor mit sechs Para-
metern. Wenn die jeweiligen Werte ausgegeben werden und scheinen sie sich nicht
wesentlich zu unterscheiden. Aber die Ausgabe kann in die Irre führen. Die Ausgabe, in
der wir die Werte der beiden Datumsobjekte voneinander abziehen, zeigt, dass sie sich
sehr wohl unterscheiden. Weiter haben wir in dem Listing einen Konstruktor ohne Para-
meter für das aktuelle Systemdatum, mit dem String-Parameter und einen Konstruktor
mit drei numerischen Parametern verwendet.

[Link].2 Festlegen des Ablaufdatums für den Inhalt einer Webseite


Wir werden in dem Beispiel auf den Browser bzw. die Webseite im Browser zugreifen
und dabei etwas vorgreifen. Aber eine wichtige Anwendung für die Differenzbildung
zwischen zwei Datumsobjekten ist die Festlegung eines Ablaufdatums für einen be-
stimmten Inhalt in einer Webseite oder deren Aussehen. Denken Sie zum Beispiel an die
Ankündigung einer Veranstaltung, die nach dem Überschreiten des Termins für die Ver-
anstaltung nicht mehr angezeigt werden soll. Stattdessen können Sie einen alternativen
Inhalt anzeigen. Um so ein Ablaufdatum zu realisieren, brauchen Sie bloß ein Datums-
objekt mit dem Termin des Ablaufdatums zu erzeugen und beim Laden der Webseite das
Systemdatum des Besuchers abzufragen. Daraus erzeugen Sie ein zweites Datumsobjekt
und bilden die Differenz. Je nachdem, ob die Differenz kleiner oder größer dem Wert 0
ist, zeigen Sie unterschiedliche Inhalte an.
202 10 Eingebaute Objektdeklarationen

 Sie können natürlich ganz einfach die Millisekunden vergleichen, aber auch
mit den Methoden zur Bestimmung von einzelnen Bestandteilen des Datums
wie den Tagen, Stunden oder Minuten arbeiten.

Spielen wir ein Beispiel durch, bei dem eine Schulungsveranstaltung ankündigt wird.
Beim Erreichen des Ablaufdatums wird eine Meldung angezeigt, dass die Schulung be-
reits läuft. Wenn der Zeitraum für die Schulung vorbei ist, sehen Sie eine weitere Mel-
dung ([Link]):


   <body><script>
     var beginn = new Date(2025, 11, 21);
     var ende = new Date(2025, 11, 23);
     var besuch = new Date();
    if ((beginn - besuch) > 0) {
      [Link](
        "<h4><a href = '[Link] Schulung</a></h4>");
    } else if ((ende - besuch) > 0) {
      [Link]("<h4>Die Schulung wird gerade durchgeführt.</h4>");
}
    } else {
        [Link]("<h4>Die Schulung ist beendet.</h4>");
    }
  </script></body></html>

Zuerst erzeugen wir ein Datumsobjekt beginn, das den Ablauftermin der Zeit vor dem
Termin repräsentieren soll. Danach wird ein weiteres Datumsobjekt angelegt, das den
Ablauftermin der Veranstaltung angibt, und als drittes Datumsobjekt wird der Besuchs-
termin der Webseite – das aktuelle Systemdatum – erzeugt und über besuch bereit-
gestellt. In der if-else-Anweisung wird mit der Differenz zwischen zwei Datumsobjekten
überprüft, ob ein bestimmter Termin zum Zeitpunkt des Besuchs erreicht ist oder nicht.
Dabei vergleichen wir jeweils ganz einfach den Wert der Millisekunden der beiden
Datumsobjekte. In Abhängigkeit von dem Wert zeigen wir einen von drei möglichen
Texten an, der mit [Link] in die Webseite geschrieben wird.

[Link].3 Datumsobjekte dynamisch setzen


Setzen wir nun noch ein paar Datumsobjekte unter Verwendung von Parameterwerten,
die erst einmal unlogisch erscheinen ([Link]).

"use strict";
var a = new Date();
[Link](15);
[Link](42)
10.4 Native Objektdeklarationen in JavaScript 203

[Link](a);
var b = new Date(2024,16,34);
[Link](b);

Das Datumsobjekt a mit dem aktuellen Systemdatum wird hinsichtlich der Monate als
auch Stunden nach der Erzeugung geändert. Beachten Sie die bewusste „Sprengung“
des jeweilige Ranges. Die naive Annahme, dass sich die Stunden eines Tages zwischen
den Werten 0 und 23 bewegen müssen, wird widerlegt. Größere Zahlen erweitern die
Datumsangabe einfach auf den folgenden Tag oder auch im Grunde beliebig viele Tage
weiter. Vollkommen analog ist das bei den anderen Angaben zu Minuten, Sekunden etc.
Es ist nur ein Multiplikationsfaktor, der eine gewisse Anzahl an Millisekunden fest-
legt. Aus der Summe aller Werte kann man die Gesamtheit der Millisekunden seit dem
01.01.1970 bestimmen und das ist dann irgendein Datum. Auch der Konstruktor von
Date ist hinsichtlich der Parameter nicht künstlich beschränkt. Das Datum von b ist ein-
fach 02.06.2025. Der Monat 16 ist Mai und 34 Tage im Mai erzeugen den „Überlauf“ in
den Juni.

 Eine ebenfalls interessante Anwendung der Differenzbildung zweier Datums-


objekte stellt die Berechnung einer Zeitspanne bis zu einem bestimmten Termin
dar. Der Unterschied zum Vergleich mit einem Ablauftermin ist nicht sonderlich
groß, aber während es beim Vergleich mit einem Ablauftermin nur um größer
oder kleiner geht, interessiert hier der tatsächliche Wert des Vergleichs. Oft kom-
biniert man diese Information auch mit dem dynamischen Anzeigen von In-
halt bis zu einem Ablauftermin. Auch veränderte Inhalte aufgrund des Datums
kann man mit clientseitigem JavaScript generieren, wobei das heutzutage meist
serverseitig gemacht wird. Wenn Sie einem Besucher eine aktuelle Webseite
präsentieren wollen, können Sie Teile des Inhalts einer Webseite aufgrund eines
Datums dynamisch schreiben. Sie können beispielsweise einem Besucher ab-
hängig von einer Stunde oder gar einer Sekunde unterschiedliche Inhalte prä-
sentieren und damit die Webseite für wiederkehrende Besucher interessant hal-
ten. In die gleiche Richtung ziele eine optische Veränderung einer Seite je nach
Zeitpunkt, wo Sie Sie dynamisch nur das Layout einer Webseite mit Style Sheets
aufgrund des Zeitpunkts des Besuchs verändern. Ganz einfach geht das, indem
Sie einfach mehrere externe Style-Sheet-Dateien bereitstelle und diese zeit-
gesteuert mit JavaScript einbinden. Dazu scheiben Sie den style-Tag dynamisch
mit [Link], wie es oben angewendet wurde.

10.4.6 String-Operationen

Objekte vom Typ String zählen ebenfalls zu den nativen JavaScript-Objekten. Sie re-
präsentieren Zeichenketten, aber durch die Repräsentation als Objekte haben Sie damit
204 10 Eingebaute Objektdeklarationen

natürlich wieder diverse Member zum Umgang mit diesen Zeichenketten zur Verfügung.
Insbesondere können Sie Zeichenketten direkt über deren Methoden manipulieren. Um
die explizite Erstellung einer Instanz von einem String mit dem Aufruf eines Konstruk-
tors brauchen Sie sich meist nicht eigens zu kümmern, da eine Zuweisung der folgenden
Form automatisch ein String-Objekt erzeugt:

Beispiel

var text = "Das ist ein Text"; ◄

Sie erzeugen hiermit implizit das Objekt über eine deklarative Notation. Eine Ver-
wendung des new-Schlüsselworts in der folgenden Form zur expliziten Erzeugung eines
Objekts über einen Konstruktor funktioniert aber auch, obwohl das vollkommen unüb-
lich ist:

Beispiel

var text = new String("Das ist ein Text"); ◄

Die Variable, die auf eine Zeichenkette verweist, ist Ihr Zugang zu den Member. Wie
schon erwähnt, können Sie aber auch direkt auf String-Literale wie auf jedes andere
Objekt zugreifen. Ein String-Objekt verfügt nur über eine wichtige Eigenschaft. Über
length erhalten Sie die Anzahl der Zeichen bzw. die Länge in einer Zeichenkette.

[Link] Methoden von Zeichenketten


Dafür gibt es eine ganze Menge an Methoden, die Sie teils in der nachfolgenden Ta-
belle finden. Allerdings verlieren diejenigen Methoden von String-Objekten rapide an
Bedeutung, die sich auf optische HTML-Effekte beziehen. Hierzu verwendet man schon
lange CSS. Wir geben Sie deshalb hier gar nicht mehr an, obwohl sie im Prinzip noch
funktionieren (Tab. 10.7).
Beispiele zu String-Methoden werden wir im Zusammenhang mit regulären Aus-
drücken durchspielen.

10.4.7 Objekte vom Typ Function

Allgemein erstellen Sie konventionell eine Funktion in JavaScript, indem Sie das
Schlüsselwort function, den Namen, eventuelle Parameter und eine Implementierung no-
tieren. In diesem Fall haben Sie erst einmal nichts mit Objekten zu tun. Es gibt aber auch
den Fall, dass Sie eine Funktion in Objektform haben möchten. Und dies geht über die
Klasse Function (beachten Sie die Großschreibung!). Damit können Sie eine Funktion
überall verwenden, wenn Sie ein Objekt benötigen und mit einer klassischen Funktion die
notwendigen Bedingungen für eine Anwendung nicht erfüllt werden. Ebenso haben Sie
10.4 Native Objektdeklarationen in JavaScript 205

Tab. 10.7  Wichtige Methoden von String


Methode Beschreibung
charAt Rückgabe des Zeichens, das in der Zeichenkette an der Stelle steht, die im
Parameter als Index übergeben wird. Die Zählung beginnt mit 0
charCodeAt Die Methode liefert eine Zahl zurück, die dem Unicode-Wert des Zeichens ent-
spricht, dessen Index in der Zeichenkette mit dem Parameter spezifiziert wird
Concat Für die String-Verkettung haben Sie in JavaScript (mindestens) zwei sinnvolle
Möglichkeiten. Sie können beispielsweise den Plusoperator zum Verketten
von Strings verwenden. Dieser hat den zusätzlichen Effekt, dass eventuell
mit einem String verkettete primitive Datentypen automatisch vor der Ver-
bindung in Strings verwandelt werden. Alternativ können Sie diese Methode
verwenden, die zwei oder mehr Strings zu einem neuen String verbindet.
Genau genommen wird beziehungsweise werden der oder die als Parameter
angegebenen Strings an den vorangestellten String angehängt
fromCharCode Wenn Sie die Unicode-Sequenz eines Zeichens kennen, können Sie daraus mit
dieser Methode als Rückgabewert einen String mit dem zugehörigen Zeichen
erzeugen. Die Methode unterscheidet sich in der Anwendung von zahlreichen
anderen Methoden in der Verarbeitung von Strings, denn Sie stellen beim
Aufruf keine Zeichenkette oder ein String-Literal als Objekt voran, sondern
explizit den Namen der Klasse String (es ist also eine Klassenmethode).
Übergeben Sie der Methode mehrere, durch Kommata getrennte Unicode-
Sequenzen als Parameter, wird der entstehende String entsprechend mehr Zei-
chen enthalten
indexOf Mit der Methode wird das erste Vorkommen eines Zeichens oder einer
Zeichenkette innerhalb der vorangestellten Zeichenkette ermittelt. Die Me-
thode kann auch mit regulären Ausdrücken umgehen, die wir in dem Kapitel
noch ausführlich behandeln. Rückgabe der Methode ist die Stelle, an der
das Zeichen in der Zeichenkette steht. Die Zählung beginnt bei 0. Optional
ist es möglich, die Funktion in einem zweiten Parameter anzuweisen, ab der
wievielten Stelle in der Zeichenkette die Suche beginnen soll. Besonders in-
teressant ist der Rückgabewert -1 der entsteht, wenn eine Zeichenkette nicht
gefunden wird
lastIndexOf Hier wird das letzte Vorkommen eines Zeichens oder einer Zeichenkette inner-
halb der vorangestellten Zeichenkette ermittelt. Die Methode kann ebenfalls
mit regulären Ausdrücken umgehen. Die Rückgabe ist die Stelle, an der das
Zeichen in der Zeichenkette steht. Die Zählung beginnt bei 0. Optional ist es
möglich, die Funktion in einem zweiten Parameter anzuweisen, ab der wie-
vielten Stelle in der Zeichenkette sie mit der Suche beginnen soll. Auch hier
gibt es den Rückgabewert –1, wenn eine Zeichenkette nicht gefunden wird
Link Die Methode erzeugt einen Verweis. Das Verweisziel, zum Beispiel ein Datei-
name oder eine URL-Adresse, wird als Parameter übergeben. Den Verweistext
müssen Sie als Variable definieren
Match Mit dieser Methode können Sie eine Zeichenkette mithilfe eines regulären
Ausdrucks als Parameter durchsuchen. Der Rückgabewert ist die Zeichenfolge,
auf die der reguläre Ausdruck passt

(Fortsetzung)
206 10 Eingebaute Objektdeklarationen

Tab. 10.7 (Fortsetzung)


Methode Beschreibung
Replace Die Methode ersetzt in Zeichenketten den zuerst angegebenen Ausdruck durch
den zweiten Ausdruck. Die Methode kann mit regulären Ausdrücken umgehen
Search Die Methode durchsucht den String nach der als Parameter angegebenen
Zeichenkette. Die Methode kann mit regulären Ausdrücken umgehen. Falls die
Zeichenkette nicht gefunden wird, wird der Wert –1 zurückgeliefert, sonst ein
davon verschiedener Wert. Die Nummerierung der Positionen beginnt bei 0
slice Eine Möglichkeit zur Extrahierung von einem Teilstring ist der Einsatz dieser
Methode, die man auch bei Arrays findet. Sie erwartet als ersten Parameter den
Index des ersten zu extrahierenden Zeichens und als optionalen zweiten Para-
meter den Index des letzten Zeichens. Fehlt der zweite Index, wird bis zum
Ende des Strings extrahiert. Ist der zweite Index negativ, wird ab dem Ende
des Strings rückwärts gezählt, um das letzte Zeichen der Extrahierung zu be-
stimmen
Split Die String-Klasse stellt über die Methode eine Möglichkeit bereit, eine
Zeichenkette an einem definierten Trennzeichen in ein Array aufzuspalten. Die
Methode erwartet als ersten Parameter das Zeichen, an dem die Aufteilung
eines Strings in Teilsegmente erfolgen soll, und trennt einen vorangestellten
String in ein Array mit Teilstrings, indem an dem angegebenen Separator ge-
trennt wird. Wird der Separator nicht vorgefunden oder fehlt er, wird ein Array
mit einem Element und dem vollständigen String zurückgegeben. Die Methode
kann bei der Angabe des Separators mit regulären Ausdrücken umgehen. Der
Methode kann als zweiter Parameter eine maximale Anzahl von Trennungen
angegeben werden
Substring Diese Methode liefert eine Zeichenkette zurück, die einen Teil innerhalb einer
vorangestellten Zeichenkette darstellt. Als Parameter wird der Funktion als
erster Parameter der Beginn und als zweiter Parameter das Ende der zurückzu-
gebenden Zeichenkette übergeben. Das erste Zeichen hat den Wert 0. Es han-
delt sich hierbei also um eine Alternative zu slice()
toLowerCase Umwandeln aller Zeichen in der vorangestellten Zeichenkette in Kleinbuch-
staben
toUpperCase Die Methode wandelt alle in der vorangestellten Zeichenkette enthaltenen
Kleinbuchstaben in Großbuchstaben um

dann natürlich alle grundlegenden Member zur Verfügung, die jedes Objekt bereitstellt.
Sinnvoll ist so ein Function-Objekt beispielsweise im Zusammenhang mit einer variab-
len Parameteranzahl von einer Funktion, wobei auch eine klassisch erzeugte Funktion hier
verwendet werden kann. Die hauptsächliche Bedeutung von Objekten vom Typ Function
ist jedoch, dass Sie damit Funktionen formal sauber in eine objektorientierte Konzeption
integrieren können, wobei die Idee des globalen Objekts das Konzept mehr oder weni-
ger obsolet macht. Die Erstellung eines Objekts vom Typ Function erfolgt wie üblich mit
einem Konstruktor. Schema für die Erstellung eines Objekts vom Typ Function;
10.4 Native Objektdeklarationen in JavaScript 207

Beispiel

new Function([Parameterliste]) ◄

In der Regel wird der Verweis auf das Objekt natürlich wieder in einer Variablen ge-
speichert, denn sonst könnte man es nach der Erstellung natürlich nicht weiterverwenden
(sprich die Funktion aufrufen). Die Variable wird damit zu einer Funktionsreferenz!

Objekte dieses Typs sind in der Praxis eher unwichtig, denn man verwendet stattdessen klassische
Notation von (meist anonymen) Funktionen oder Lambda-Ausdrücken. Aus Sicht der Performance
eines Skripts ist das Erstellen von Funktionsobjekten zudem nicht zu empfehlen. Mehr wollen wir
zu dem Thema deshalb nicht ausführen.

10.4.8 Native Bildobjekte

In JavaScript können Sie mit Image und dem Konstruktor über new Image() Bildobjekte
erzeugen, denen als Eigenschaften alle Attribute zur Verfügung stehen, die Sie vom
<img>-Element kennen. Mehr braucht man dazu nicht zu sagen. Beachten Sie jedoch,
dass die Bildobjekte nach ihrer Erzeugung noch nicht der Webseite hinzugefügt oder
sonst irgendwo angezeigt wurden. Das müssen Sie selbst machen. Wir lernen die Metho-
den zum Manipulieren des DOM im nächsten Kapitel kennen.

10.4.9 Reguläre Ausdrücke

Über die String-Klasse stehen Ihnen mehrere Techniken zur Verfügung, mit denen Sie
in einer Zeichenkette einzelne Zeichen oder mehrere Zeichen suchen und/oder ersetzen
können. Dazu müssen Sie den entsprechenden Methoden einfach den zu durchsuchenden
String voranstellen und entsprechend angeben, was Sie suchen oder ersetzen wollen. Die
Möglichkeiten zur Suche in Texten gehen aber weiter, wenn Sie sogenannte reguläre Aus-
drücke (regular expressions) einsetzen. Diese basieren entweder direkt auf einem Objekt
vom Typ RegExp bzw. der Objektdeklaration selbst oder speziell formatierten Token.

[Link] Was sind reguläre Ausdrücke?


Reguläre Ausdrücke kommen in den meisten Skript- und Programmiersprachen als auch
einigen Textverarbeitungsprogrammen vor (mit weitgehend gleicher Syntax). Sie sind
sehr mächtig und dienen grundsätzlich dazu, Suchausdrücke mit Platzhaltern und kleinen
Programmieranweisungen zu formulieren, um darüber nicht nur nach konstanten Werten,
sondern viel flexibler in Zeichenketten zu suchen beziehungsweise bestimmte Operatio-
nen auszuführen. Solche regulären Ausdrücke werden unter anderem von vielen der klas-
sischen Suchmethoden in einer Zeichenkette unterstützt. Das umfasst die search- sowie
replace- oder die match-Methode.
208 10 Eingebaute Objektdeklarationen

 Reguläre Ausdrücke sind nicht trivial. Wir werden hier die Möglichkeiten nur
anreißen können. In der Anfangsphase Ihrer JavaScript-Karriere werden Sie
mit regulären Ausdrücken weniger in Berührung kommen und es gibt zahl-
reiche professionelle JavaScript-Programmierer, die niemals damit arbeiten.
Dennoch – sie können in manchen Fällen hilfreich sein und Skripte erheblich
kompakter machen.

[Link] Pattern
Suchmuster (sogenannte Pattern) werden bei der direkten Notation von regulären Aus-
drücken in einfache Schrägstriche /…/ eingeschlossen (keine Hochkommata außen
herum). Darin enthalten ist der zu suchende Ausdruck, der aus Suchtext und Steuer-
zeichen bestehen kann. So könnte eine Variable einen regulären Suchausdruck in Schräg-
strichen samt einer Modifikation zugewiesen bekommen.

Beispiel

regausdruck = /bin/i; ◄

Diese Notation bildet also vom beginnenden / bis zum abschließenden / einen Token und
stellt damit eine deklarative Erzeugung eines Objekts vom Typ RegExp dar. Gegebenen-
falls kann man – wie im Beispiel – mit einem Modifikator den Ausdruck genauer spezi-
fizieren. Bei der Formulierung eines Suchausdrucks über ein Objekt vom Typ RegExp
verzichten Sie auf die Schrägstriche und notieren als Parameter des Konstruktors eine
explizite Zeichenkette. Um dieses zu verwenden, erzeugen Sie mit dem Konstruktor
sowie einem Suchpattern in Hochkommata eine Instanz. Egal, wie Sie das Objekt er-
zeugen – Sie können anschließend die gewünschten Member des RegExp-Objekts an-
wenden oder das Objekt an eine Methode übergeben, die damit umgehen kann. Beispiele
für eine Erzeugung eines Suchmusters über den Konstruktor:

Beispiel

var regausdruck = new RegExp("in$"); ◄

 Bei Eingabe der Steuerzeichen in einem Pattern ist unbedingt auf Groß- und Klein-
schreibung zu achten. Ebenso wird bei einem Pattern in der Grundeinstellung beim
Suchtext zwischen Groß- und Kleinschreibung unterschieden, wenn Sie dies nicht
mit einer entsprechenden Option ausschalten.

[Link] Klammern, Steuerzeichen, Modifikatoren und Optionen für


reguläre Ausdrücke
Wir werden an der Stelle reguläre Ausdrücke keineswegs vollständig behandeln, aber ein
paar Erläuterungen zu wichtigen Steuerzeichen, Modifikatoren und Optionen sehen, mit
10.4 Native Objektdeklarationen in JavaScript 209

denen Sie einen regulären Ausdruck formulieren können. Steuerzeichen und Optionen
können in einem Ausdruck kombiniert werden. Sofern keine Widersprüche ausgelöst
werden, kann man auch mit dem Pipe-Symbol | als Oder-Verbindung mehrere Ausdrücke
in einem Pattern verwenden.

[Link].1 Eckige und runde Klammern – Brackets


In eckigen Klammern folgt eine Auflistung von Zeichen, die für ein konkretes Zeichen
an einer Stelle erlaubt sind. Insgesamt steht der gesamte geklammerte Ausdruck immer
nur für genau ein Zeichen. Mehrere rechteckige Klammern in einem Suchausdruck ent-
sprechen damit mehreren Zeichen, ebenso mehrere Zeichen oder Steuerzeichen, die nicht
in eckigen Klammern notiert werden. Das sind zwei äquivalente Beispiele für ein Such-
muster, bei dem an der Stelle eine Zahl zwischen 1 und 5 stehen darf.

Beispiel

[12345]
[1–5] ◄

Wenn eine Lücke in einer Sequenz notwendig ist, kann man das so formulieren, wie bei
dem Pattern, bei dem an der Stelle kann eine Zahl zwischen 1 und 3 oder 7 bis 9 stehen
darf.

Beispiel

[1–37–9] ◄

Selbstverständlich kann man so etwas auch für Buchstaben formulieren. Das ist ein Pat-
tern für einen zweistelligen Ausdruck, bei dem Zahlen und Kleinbuchstaben von a bis f
als Treffer erlaubt sind.

Beispiel

[0-9a-f][0-9a-f] ◄

Das wäre ein Beispiel für ein vierstelliges Suchpattern. Zuerst kommt eine Zahl, dann
ein Kleinbuchstabe gefolgt von zwei beliebigen alphanumerischen Zeichen.

Beispiel

[0–9][a-z]\w\w ◄

Mit dem Zeichen ^ können Sie auch eine Nicht-Bedingung formulieren. Das ist ein Pat-
tern, dass alle Zeichen außer den in den Klammern erlaubt.
210 10 Eingebaute Objektdeklarationen

Beispiel

[^abc] ◄

Wenn Sie eine Auswahl in runde Klammern notieren und dazwischen das |-Zeichen set-
zen, dann geben Sie eine Oder-Beziehung an. So formulieren Sie ein Pattern, bei dem
entweder das eine oder das andere Zeichen erlaubt ist.

Beispiel

(x|y) ◄

[Link].2 Steuerzeichen, Modifikatoren und Optionen


Es gibt bei regulären Ausdrücken Steuerzeichen, die auch Meta-Zeichen (Metacharac-
ters) genannt werden, weil sie Zeichen eine spezielle Zusatzbedeutung geben. Die wer-
den in zwei Slashs eingeschlossen. So ist /\d/ die Notation für die Suche nach einer be-
liebigen Zahl(achten Sie auf die Kleinschreibung) oder /\D/ (achten Sie auf die Groß-
schreibung) die Suche nach einem beliebigen Zeichen, das keine Ziffer ist.
Mit Modifikatoren lassen sich Suchanfragen in Hinsicht der Art einer Suche genauer
modifizieren. Sie werden in JavaScript gegebenenfalls im Token dem Abschluss-Slash
nachgestellt und im Konstruktor als zweiter Parameter verwendet (etwa so: new Re-
gExp("i.", "g");). Auch die Methoden eines regulären Objekts können Modifikatoren ver-
wenden. Damit kann man etwa über die Notation eines nachgestellten g nach dem Such-
string so oft suchen, wie ein Suchpattern in dem gesamten zu durchsuchenden Bereich
vorkommt. Mit anderen Worten – die gesamte Zeichenkette wird durchsucht oder mit der
Notation eines nachgestellten i kann man Klein- und Großschreibung ignorieren.
Mit Optionen bzw. Quantifizierer (Quantifiers) kann man Suchpattern so formulieren,
das über die Zeichen hinaus die Anzahl genauer beschrieben werden kann. Die Nota-
tion des Sterns bedeutet, dass das letzte Zeichen vor dem Stern kein Mal oder beliebig
oft hintereinanderstehen kann. Bei einem ? kann ein vorangestelltes Zeichen kann, muss
aber nicht, also ein optionales Zeichen. Über die Notation {min, max} (etwa a{1,3})
kann man die Angabe einer Mindestanzahl und einer Maximalanzahl, wie oft ein voran-
gestelltes Zeichen auftreten muss bzw. kann, vorgeben.
Es gibt eine Vielzahl an solchen Spezialisierungen der Ausdrücke, wofür aber auf spe-
zielle Literatur verwiesen sei.

[Link] RegExp-Methoden und -Eigenschaften


Nun haben Objekte von Typ RegExp eine ganze Reihe an Member zur Verfügung. Wir
wollen uns auf die boolesche Methode test() beschränken. Damit können Sie überprüfen,
ob ein regulärer Ausdruck zu Treffern führt oder nicht.
10.4 Native Objektdeklarationen in JavaScript 211

[Link] Beispiele für die Anwendung von regulären Ausdrücken


Der Umgang mit regulären Ausdrücken ist wie gesagt recht komplex und kann hier kaum
vollständig behandelt werden. Wir wollen uns aber einige Beispiele ansehen, um den
grundsätzlichen Umgang zu verdeutlichen.

[Link].1 Eine Zeichenklasse literal verwenden


Hier ist ein JavaScript-Beispiel ([Link]), das einen regulären Ausdruck verwendet, der
eine Zeichenklasse mit den gewünschten Zeichen enthält. Die Suche erfolgt in dem
String über dessen match-Methode.

"use strict";
function sucheErstesVorkommen(text) {
  // Suche nach dem ersten Vorkommen von a, d, g, Z oder 8
  var match = [Link](/[adgZ8]/);
  // Überprüfe, ob ein Treffer gefunden wurde
  if (match) {
    return match[0];
  } else {
    return "Kein Treffer gefunden";
  }
}
// Beispieltext
var text = "Das ist ein Beispieltext mit Zahlen 123 und Buchstaben a,
b und c";
var ergebnis = sucheErstesVorkommen(text);
[Link]("Erstes Vorkommen:", ergebnis);

In diesem Beispiel wird der reguläre Ausdruck [adgZ8] verwendet, um nach dem ers-
ten Vorkommen eines der Zeichen "a", "d", "g", "Z" oder "8" im Text zu suchen. Die
match-Methode gibt ein Array mit den gefundenen Zeichen zurück, und match[0] gibt
das erste gefundene Zeichen zurück. Wenn kein Treffer gefunden wird, wird eine pas-
sende zurückgegeben.

[Link].2 Eine Zeichenklasse mit dem Konstruktor verwenden


Hier ist eine Abwandlung des Beispiels ([Link]), das den Konstruktor verwendet, um
den regulären Ausdruck zu erstellen:

function sucheErstesVorkommen(text) {
  // Erstelle einen regulären Ausdruck mit dem Konstruktor new RegExp
  var regex = new RegExp("[adgZ8]");
  // Suche nach dem ersten Vorkommen des regulären Ausdrucks im Text
  var match = [Link](regex);

212 10 Eingebaute Objektdeklarationen

Hier wird der reguläre Ausdruck [adgZ8] als Zeichenkette an den Konstruktor new Re-
gExp übergeben, um ein Objekt zu erstellen. Dann wird dieses verwendet, um nach dem
ersten Vorkommen im Text zu suchen. Das übrige Verfahren bleibt gleich.

[Link].3 Alle Zahlen in einem Text durch "*" ersetzen


Hier ist ein einfaches JavaScript, das alle Zahlen in einem Text durch "*" ersetzt (reg3.
js):

"use strict";
function ersetzeZahlen(text) {
  return [Link](/\d+/g, '*');
}
// Beispieltext
var text = "Dies ist eine Zahl: 123 und eine andere Zahl: 456. Auch
das ist eine Zahl: 7890";
var neuerText = ersetzeZahlen(text);
[Link](neuerText);

In diesem Skript wird die replace-Methode mit einer RegExp verwendet, die nach einer
oder mehreren aufeinanderfolgenden Ziffern sucht (\d+ ). Das Flag g bewirkt, dass die
Suche global im gesamten Text durchgeführt wird. Alle gefundenen Zahlen werden dann
durch "*" ersetzt.

[Link].4 Test auf eine gültige E-Mail-Adresse


Wir wollen nun reguläre Ausdrücke zur Analyse einer E-Mail verwenden. Ein Hauptein-
satzzweck von JavaScript ist die Überprüfung von Benutzereingaben in Webformularen.
Wir wollen in diesem Beispiel mit regulären Ausdrücken überprüfen, ob die Benutzer-
eingabe einer E-Mail-Adresse in einem freien Eingabefeld eines Webformulars das zwin-
gend notwendige Zeichen @ enthält und davor mindestens ein und dahinter mindestens
drei Zeichen. Das erste Zeichen vor dem @ muss zudem ein Buchstabe sein. Zum Ein-
satz kommt in diesem Beispiel die RegExp-Methode test. Wir reduzieren das Beispiel auf
den reinen JavaScript-Teil ([Link]). Die Webseite muss bloß ein übliches Formular zur
Eingabe des Textes enthalten, der hier als Parameter der Funktion übergeben wird.

function testEMail(feld) {
  var korrekt = true;
  var regausdruck = new RegExp('[a-zA-Z][0-9a-zA-Z]*@.{3}');
  if (![Link]([Link])) korrekt = false;
  return korrekt;
}

Der reguläre Ausdruck beginnt mit einer Klammerangabe, dass das erste Zeichen zwin-
gend und ein Buchstabe sein muss. Das zweite Zeichen ist optional, was mit dem Stern
10.4 Native Objektdeklarationen in JavaScript 213

angegeben wird. Der Klammerausdruck erlaubt wieder Buchstaben oder Zahlen. Nun
folgt zwingend das Zeichen @. Der nachstehende Punkt erlaubt ein beliebiges Zeichen.
Die Angabe in den geschweiften Klammern legt fest, dass mindestens drei beliebige Zei-
chen dort stehen.
Dann wird mit der Methode test geprüft, ob der Wert in dem Parameter den Ver-
gleichswert enthält. Falls ja, liefert die Methode true und im Fehlerfall false.
Der DOM – das Mysterium der
JavaScript-Welt 11

Sie werden oft Objekte erzeugen oder vorhandene Objekte und Typen verwenden,
wie wir es in den letzten Kapiteln gesehen haben. So gut wie immer werden Sie
Objekte nutzen, die Ihnen automatisch zur Verfügung gestellt werden. Entweder
von einer Laufzeitumgebung wie [Link] oder vom Browser. Browser haben
wir bisher eher am Rande verwendet, aber historisch ist der Browser das „natür-
liche“ Habitat von JavaScript gewesen und auch heute noch die Umgebung, wo
das meiste JavaScript aufgeführt wird. Browser wurden auch bisher nicht ignoriert,
sondern fast alle bisherigen Ausführung gelten auch da. In dem Kapitel kommen
wir aber zu speziellen Objekten, die unter [Link] oder allgemein serverseitiger
Umgebung nicht verfügbar sind, sondern explizit nur im Browser. Diese Objekte
basieren auf einem Objektmodell, das Strukturen von weitgehend beliebigen
baumartig aufgebauten Dokumenten beschreibt. Dieses nennt sich DOM – das Do-
cument Object Model – und wird beim W3C standardisiert. Diese Objektbibliothek
kann mittels diverser Techniken genutzt werden, sowohl aus Programmier- und
Skriptsprachen als auch aus Anwendungen heraus. Dem Zugriff auf eine Webseite
unter diesem Objektgesichtspunkt liegt mit DOM ein globales Konzept zugrunde,
das sowohl eine plattform- als auch programmiersprachenübergreifende Schnitt-
stelle bezeichnet.

11.1 Was ist das DOM-Konzept?

DOM bezeichnet eine Programmier-API für Dokumente, die stark der Struktur der von
ihm modellierten Dokumente in Form eines Baums ähnelt. Der Kern des DOM-Konzepts
basierte anfänglich auf einer Überlegung zum Aufbau einer Webseite aus HTML. In

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 215
Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
216 11 Der DOM – das Mysterium der JavaScript-Welt

d­ ieser ersten Überlegung wird eine HTML-Seite nicht als statisch aufgebaute, fertige und
nicht unterscheidbare Einheit, sondern als differenzierbare Struktur betrachtet. Damit
sind deren einzelne Bestandteile Programmen und Skripten dynamisch zugänglich, wenn
es eine Syntax für einen Zugriff gibt. Dieser Ansatz, den ursprünglich im Wesentlichen
Netscape inklusive seiner Layer-Technologie vorangetrieben hat, ermöglicht die indivi-
duelle Behandlung von Bestandteilen einer Webseite auch dann, wenn die Webseite be-
reits in den Browser geladen ist. Allerdings konnten sich die ersten Umsetzungen solch
eines Konzepts nicht einheitlich in den verschiedenen Browsern durchsetzen, weshalb
Netscape die Verantwortung für die Standardisierung an das W3C übertragen hat. Die
ersten Varianten des DOM-Konzepts sind daher als Versuche des W3C zu verstehen, die
verschiedenen proprietären Zugriffstechniken auf Teile einer Webseite zusammenzu-
führen, die sich während der Zeit der Browserkriege gebildet haben, und sie mit einem
einheitlichen Konzept abzulösen. Um eine präzise, sprachunabhängige Spezifikation
der DOM-Schnittstellen bereitzustellen, ist die Spezifikationen beim W3C in OMG IdL
definiert. Zusätzlich zur OMG IdL-Spezifikation gibt es Sprachbindungen für Java und
ECMAScript. Das DOM-Konzept beinhaltet mittlerweile verschiedene Teilaspekte. Es
veranlasst beispielsweise einen Browser, eine Webseite zwar wie eine gewöhnliche Text-
datei zu parsen, entsprechende HTML-Anweisungen auszuführen und die Seite darzu-
stellen. Darüber hinaus wird der Browser jedoch beim Laden der Webseite alle ihm im
Rahmen des Konzepts bekannten und einzeln identifizierbaren Elemente einer Webseite
bezüglich ihres Typs, ihrer relevanten Eigenschaften/Methoden und ihrer Position inner-
halb der Webseite indizieren. Wenn man es anders ausdrücken möchte – aus den Elemen-
ten der Webseite werden Objekte gebildet, sobald diese identifiziert wurden, Referenzen
darauf gespeichert und zugänglich gemacht. Das DOM-Konzept ist mittlerweile auch auf
andere baumartige Dokumente wie etwa XML anwendbar. In der DOM-Spezifikation
wird der Begriff „Dokument“ im weitesten Sinne verwendet. Man kann Dokumente er-
stellen, durch ihre Struktur navigieren und Elemente und Inhalte hinzufügen, ändern oder
löschen. Alles, was in einem HTML- oder XML-Dokument gefunden wird, kann mit
dem Document Object Model aufgerufen, geändert, gelöscht oder hinzugefügt werden,
mit wenigen Ausnahmen.

11.1.1 Grundsätzliches zum Weg durch den DOM

Die Baumstruktur eines Dokuments aus Sicht des DOM-Konzepts wird im Haupt-
speicher des Rechners beim Laden eines Dokuments aufgebaut. Ein solcher wird beim
Verlassen der Seite wieder gelöscht. Ähnliche Elemente werden bei der Indizierung auf
gemeinsame dynamische Datenstapel (Stacks) für das Dokument abgelegt. Wer jetzt
an automatisch indizierte Arrays denkt, ist auf dem richtigen Weg. Auf diese Weise hat
ein Browser nach dem Laden der Webseite genaue Kenntnis über alle relevanten Daten
11.1 Was ist das DOM-Konzept? 217

sämtlicher eigenständig für ihn ansprechbarer Elemente (Objekte) in der Webseite. Wel-
che das jedoch sind und was er damit anstellen kann, das hat sich in der Vergangenheit je
nach Browser unterschieden. Diese Unterschiede nehmen aber massiv ab.

[Link] Knoten und Verwandtschaftsbeziehungen


Lassen Sie uns noch eine andere Sicht auf dem Baum einer Webseite diskutieren. Ge-
rade wenn Sie bereits XML kennen, wird Ihnen der Begriff der Knoten (nodes) ver-
traut sein. Diese bezeichnen in Hinsicht auf einen Baum die enthaltenen Elemente, aber
auch andere Bestandteile eines Dokuments wie Attribute, Kommentare oder Textinhalte.
Knotenstrukturen in einem Baum erlauben es, auch Arten von Beziehungen zu anderen
Knoten zu definieren. Jeder Baum entspringt zuerst einmal einem Wurzelknoten (root).
Im Fall einer Webseite ist das das Element html. Auch ein Teilbaum hat für seine spe-
zielle Teilstruktur jeweils einen entsprechenden Wurzelknoten, etwa bei einer Tabelle das
table-Element oder bei einer Aufzählungsliste das Element ul. Untergeordnete Knoten
werden als Kinder (children) oder – in tieferen Ebenen – als Nachfolger bezeichnet.
Knoten auf derselben Ebene sind Geschwister (siblings) und übergeordnete Element-
knoten Eltern (parent) bzw. Vorfahren, wenn es um mehrere Ebenen geht. Ausgehend
vom Wurzelknoten ist der Weg zu jedem anderen Knoten im Baum über diese Verwandt-
schaftsbeziehungen beschreibbar.

[Link] Wichtige Knotentypen bei Webseiten


Das DOM-Konzept stellt eine Reihe an Knotentypen zur Verfügung, die teils in Web-
seiten keine Rolle spielen, dafür aber etwa unter XML. Wichtige Knotentypen für Web-
seiten im DOM-Konzept sind folgende:

• Ein Dokumentknoten repräsentiert die gesamte Baumstruktur.


• Ein Elementknoten entspricht exakt einem Element in HTML oder XML.
• Ein Attributknoten entspricht einem Attribut in HTML oder XML, wobei Attribut-
knoten im DOM-Konzept und in dessen verschiedenen Versionen bzw. dessen tat-
sächlicher Umsetzung etwas inkonsistent umgesetzt werden. Das betrifft die Ein-
ordnung als Kindelemente von Elementknoten, aber für Zugriffe aus JavaScript hat
das meist nur indirekte Folgen.
• Ein Textknoten repräsentiert den textuellen Inhalt eines Elements.

Auf die Details kommen wir noch zu sprechen.


Die meisten ansprechbaren Elemente einer Webseite können bei Bedarf während der
„Lebenszeit“ der Webseite aktualisiert werden, etwa wenn mittels eines Skripts die Posi-
tion eines Elements in der Webseite oder dessen Inhalt verändert oder über Style Sheets
nach dem vollständigen Laden der Webseite das Layout eines Elements dynamisch um-
gewandelt wird. Das werden wir in dem Kapitel alles sehen.
218 11 Der DOM – das Mysterium der JavaScript-Welt

11.2 Wichtige DOM-Objekte

Im DOM-Konzept stehen mehrere Objekte für eine Verwendung in einer Webseite als
auch dem umgebenden Browser zur Verfügung. Beachten Sie, dass nicht alle Brow-
ser sämtliche Objekte unterstützen, Objekte obsolet werden können und es auch in der
Art der Unterstützung zahlreiche Unterschiede gibt. Man kann die DOM-Objekte in all-
gemein (Tab. 11.1) und spezifisch für bestimmte HTML-Elemente unterteilen (Tab. 11.2).

Es gibt im Rahmen des DOM-Konzepts sogenannte Objektfelder. Es sind Arrays mit DOM-
Objekten eines spezifischen Typs als Inhalt. Charakteristisch dafür ist, dass die darin enthaltenen
Objekte wie bei „normalen“ Arrays über einen Feldnamen sowie eine Indexnummer identifiziert
werden. Sie können aber auch als Index einen Namen verwenden, wenn es ein name-Attribut gibt.
Ansonsten ist die Anwendung von Member vollkommen identisch wie bei anderen Objekten. Die
nachfolgenden DOM-Objekte für die Repräsentation von Bestandteilen einer Webseite stehen
meist über Objektfelder bereit. Diese Objektfelder, die Sie in JavaScript verwenden, entstehen
automatisch, wenn eine Webseite geladen wird und Objekte eines bestimmten Typs darin enthalten
sind. Beachten Sie, dass sich die Namen der Objektfelder aus den Namen der enthaltenen Objekt-
typen ergeben und immer nur ein s (für die Pluralform) angefügt wird. Ein Array mit Objekten
vom Typ image heißt also beispielsweise images.

Tab. 11.1  Die im DOM-Konzept verfügbaren allgemeinen Objekte im Überblick


Objekte Beschreibung
document Dieses Objekt repräsentiert die Webseite selbst. Dies ist das zentrale Objekt,
von dem aus die Zugriffe auf die Webseite oder Bestandteile davon aus-
gehen.
event Ein Objekt, das bei Ereignissen in einer Webseite erzeugt wird und für die
(zentrale) Ereignisbehandlung unter JavaScript genutzt werden kann.
history Historie des Browsers. Dieses Objekt enthält Informationen über die URLs,
die ein Anwender besucht hat.
location Ein Objekt, das Informationen über die aktuelle URL der Seite enthält und
den Zugriff auf verschiedene Teile der URL ermöglicht.
navigator Die Objektrepräsentation mit Informationen über den verwendeten Browser
des Besuchers.
screen Informationen über den verwendeten Bildschirm und die Grafikein-
stellungen des Besuchers.
window Das globale Objekt im Browser-DOM. Es repräsentiert das Browserfenster
selbst und bietet Zugriff auf dessen Member. Dieses Objekt enthält also
Statusinformationen über das gesamte Browserfenster. Jedes Fenster eines
Browsers verwendet sein eigenes window-Objekt, was das höchste Objekt in
der Objekthierarchie der Objekte ist, welche optisch sichtbare Elemente in
dem Browser direkt betreffen.
XMLHttpRequest Ein Objekt, das Ajax-Anfragen ermöglicht, um Daten vom Server im Hinter-
grund zu laden, ohne die Seite neu laden zu müssen.
11.2 Wichtige DOM-Objekte 219

Nachfolgend sehen Sie weitere DOM-Objekte, die Teile einer HTML-Seite repräsentieren.
Wenn diese als Objektfelder geführt werden, erhalten Sie Zugriff auf die einzelnen Ele-
mente anhand ihres Namens oder Index und das Objektfeld oder mit den üblichen Zugriffs-
techniken auf einzelne Objekte innerhalb der Webseite, die wir noch behandeln.

Hintergrundinformation
Eher selten genutzt werden die Objekte text (Repräsentation des Texts innerhalb eines HTML-Ele-
ments), comment (Kommentar im HTML-Dokument), stylesheet (CSS-Stylesheet im Dokument),
table, row und cell (Tabellen und Strukturen), documentType (Doctype eines HTML-Dokuments)
und documentFragment (temporäres Dokumentobjekt).
Dazu gibt es auch einige veraltete Objekte. Ein paar sollten Sie der Vollständigkeit halber vom
Namen noch kennen.

Tab. 11.2  Unter dem DOM-Konzept verfügbare Objekte, die Teile der Webseite repräsentieren
Objekte Beschreibung
anchor Eine Referenz auf einen Verweisanker in einer Webseite, die über das Objektfeld an-
chors zugänglich ist.
attribute Repräsentiert ein Attribut eines HTML-Elements und die Sammlung aller Attribute
wird in einem Objektfeld bereitgestellt.
element Ein Objekt vom Typ element bezieht sich auf die Sammlung von HTML-Elementen
innerhalb eines Formulars. Es ist ein Eigenschaftsobjekt des Formularelements und
ermöglicht den Zugriff auf die einzelnen Formularelemente anhand ihres Namens
oder Index und das Objektfeld elements.
form Eine Referenz auf ein Objekt, das ein Formular in einer HTML-Seite repräsentiert.
Es wird unter forms gesammelt und enthält selbst wieder Steuerelemente wie Ein-
gabefelder, Schaltflächen usw., die für die Interaktion mit Besuchern und auch zum
Sammeln von Daten zur Serverübermittlung genutzt werden.
frame Eine Referenz auf einen Frame in einer HTML-Seite. Alle Frames werden unter
frames gesammelt.
image Eine Referenz auf eine Grafik in einer Webseite (images).
link Eine Repräsentation von einem Hyperlink (links).
node Objekte vom Typ node sind ein Sonderfall im DOM. Es gibt einmal kein Objektfeld,
sondern es wird immer nur genau Objekt damit bezeichnet. Dieses Objekt stellt den
Zugang zu jedem einzelnen Element in einem baumartig strukturierten Dokument
zur Verfügung. Es ist das Schlüsselelement für den Einsatz von modernem DHTML
und RIAs allgemein. Beachten Sie, dass Sie den Token node für einen Zugriff auf
ein node-Objekt nicht (!) hinschreiben. Es gibt sogar gar keinen Token node! Sie
erhalten einen Knoten auf eine der im Buch beschriebenen Zugriffsarten (etwa mit
getElementById() oder über ein Objektfeld) und werden damit indirekt auf ein node-
Objekt zugreifen.
option Das Objekt ist eine Eigenschaft einer Auswahlliste. Es repräsentiert die Sammlung
von <option>-Elementen innerhalb des <select>-Elements und wird unter options
zusammengefasst.
220 11 Der DOM – das Mysterium der JavaScript-Welt

• Microsoft hatte vor vielen Jahren ein Objekt all eingeführt, mit dem der direkte Zugriff auf alle
Elemente einer Webseite in seinem Browser möglich war. Es wurde durch Objekte vom Typ
node ersetzt.
• Zu den Hochzeiten von Java-Applets war das Objekt applet als Referenz auf ein Java-Applet in
einer Webseite verfügbar.
• Historisch ist das Objekt layer von Netscape als Repräsentation eines Layers in einer Webseite
eine Art frühe Version von node.
• Mit plugin gab/gibt es ein Objekt, das die vorhandenen Plug-Ins in einem Browser repräsen-
tiert. Die Verwendung von Plug-Ins ist heutzutage jedoch rückläufig, da moderne Webtechno-
logien wie HTML5 viele ihrer Funktionen übernommen haben.

Einige der DOM-Objekte sind weitgehend historisch von Interesse, während die Be-
deutung anderer in der modernen Webprogrammierung schwindet. Zwei DOM-Objekte
hingegen zeichnen sich durch eine wachsende Bedeutung aus – document und node. Die
beiden DOM-Objekte bilden den Kern fast aller modernen RIAs. Dazu ist window als
globales Objekt herausgehoben.

11.2.1 Objekthierarchien im DOM

Objekte im DOM-Konzept sind in der Regel in Form einer Objekthierarchie verfügbar.


Wenn ein Objekt einem anderen untergeordnet ist, notiert man das bei einem Zugriff
über JavaScript wie üblich in der Punktnotation, indem man erst den Namen des oberen
Objekts und dann den des darunter angesiedelten Objekts notiert. Wenn man beispiels-
weise eine Webseite nimmt, ist sie über das Objekt document verfügbar. Da sich die
Webseite in einem Browserfenster befindet und dieses im Rahmen des DOM-Konzepts
als window-Objekt (das globale Objekt im Browserkontext) ansprechbar ist, erfolgt der
vollständig qualifizierte Zugriff über [Link]. Damit ist window das Parent-
Element von document und document umgekehrt das Child-Element von window. Ein
Formular in einer Webseite ist über ein document untergeordnetes Objekt (oder genauer
ein Array aus Objekten – ein Objektfeld) mit Namen forms und einem Index verfügbar.
Damit ist es ein Kindelement von document. Formularelemente sind nun wieder über das
dem forms-Objektfeld untergeordnete Objektfeld elements verfügbar. Also sind alle Ele-
mente in dem Array Kindelemente eines Formulars. Wenn man im ersten Formular auf
einer Webseite das zweite Element ansprechen will, geht das über die Punktnotation so:

Beispiel

[Link][0].elements[1] ◄

Das Verfahren setzt sich analog auf Member der Formularelemente fort. Vollkommen
analog der Situation bei selbst erzeugten Objekten stellt jedes Objekt im DOM seine
11.3 Eventhandling 221

spezifischen Eigenschaften und objektgebundenen Methoden über die Punktnotation


­bereit.

 Sie können beim Zugriff auf den DOM-Baum meist auf das Voranstellen von
window verzichten. Statt [Link]() notiert man in der Praxis einfach
prompt() oder oder document statt [Link]. Wir werden im Buch so
gut wie immer auf die Notation von window verzichten.

11.3 Eventhandling

Eventhandler in JavaScript haben wir bisher in einigen Beispielen beiläufig verwendet.


Ist Ihnen dabei aufgefallen, wie wir das gemacht haben? Ein Eventhandler-Ereignis wird
in JavaScript als Eigenschaft von einem Objekt zur Verfügung gestellt, für das ein Er-
eignis überwacht werden soll. Das Objekt repräsentiert ein Element der Webseite oder
die gesamte Webseite selbst. Wenn man also die Sache genau fassen möchte, gehören
die Eventhandler, die Sie unter JavaScript verwenden, direkt zum DOM-Konzept oder zu
anderen Objekten, die auf Zustandsänderungen reagieren können. Der Einfachheit halber
reden wir dennoch weiter von der Ereignisbehandlung unter JavaScript. Betrachten wir
noch einmal, wie der Zugriff auf Objektattribute erfolgt – wie üblich über die Punkt-
notation. Beispiel:

Beispiel

[Link]=meinefunktion; ◄

In diesem Beispiel sehen Sie den Zugriff auf eine Eigenschaft onclick eines Objekts mit
Namen document. Streng formal ist das nichts Neues. Aber dabei habe ich Ihnen einen
JavaScript-Eventhandler „untergejubelt". Denn nichts Anderes ist der Einsatz eines sol-
chen Eventhandlers unter JavaScript selbst – eine Zuweisung einer Funktionsreferenz zu
einer passenden Eigenschaft des Objekts, bei dem das Auslösen des Ereignisses über-
wacht werden soll. Der Name der Eigenschaft ist äquivalent zum Bezeichner des HTML-
Ereignisattributs, wenn beide vorhanden sind. Alternativ können Sie in so einer Situation
natürlich auch eine anonyme Funktion verwenden. Beispiel:

Beispiel

[Link]=function() { … }; ◄

Früher hat man Eventhandler als Attribute bei HTML-Tags angeben. Das ist heutzutage nicht mehr
üblich und mit vielen Nachteilen verbunden. Ereignisbehandlung sollte nur mit JavaScript vor-
genommen werden.
222 11 Der DOM – das Mysterium der JavaScript-Welt

11.3.1 Verschiedene Ereignisse

Die verfügbaren Ereignisse, auf die Sie unter JavaScript reagieren können, umfassen ver-
schiedene Stituationen, die im Laufe des „Lebens“ einer Webseite im Browser auftreten
können. Wir gehen sie nur ganz kurz durch, denn die grundsätzliche Art der Reaktion ist
immer identisch – man bindet einen Callback an ein bestimmtes Ereignis.

[Link] Laden und Verlassen der Webseite


Die Reaktion auf das Laden und Verlassen der Webseite wird mit onload und onunload
ausgelöst. Allerdings sollte man aufpassen, bei welchem Objekt der JavaScript-Event-
handler in der Regel notiert wird – beim window-Objekt:

Beispiel

[Link]=Callback; ◄

Der Eventhandler kann theoretisch auch bei einigen anderen Elementen verwendet wer-
den. Das macht man in der Praxis aber so gut wie gar nicht und kann sogar Probleme
bereiten.

 Worauf Sie achten müssen, ist, dass Sie onload nicht zu früh verwenden. Wie
schon erwähnt, sind Zugriffe auf Bestandteile des DOM ein massives Problem,
wenn sie erfolgen, bevor der DOM vollständig aufgebaut ist. Diverse Frameworks
im Umfeld von JavaScript und des DOM unternehmen massive Anstrengungen,
um diesen Problemen Herr zu werden. Die meisten Dokumentationen der Frame-
works weisen deutlich darauf hin, dass sie von der Verwendung des onload-Event-
handlers abraten, weil dieser in einigen Browsern (hauptsächlich alte Versionen
des Internet Explorers) fehlerhaft implementiert ist. Die Probleme nehmen in
neueren Browsern ab. Der Aufruf einer Initialisierungsfunktion ganz am Ende der
Webseite ist als Alternative dennoch zuverlässiger.

Die Anwendung des Eventhandlers onunload ist das Parallelereignis zum Laden einer
Seite, wenn eine Seite wieder aus dem Speicher entfernt wird, etwa wenn eine neue Seite
geladen wird. Mehr gibt es eigentlich dazu gar nicht zu sagen, nur sollten Sie beachten,
dass die Ausführung des Eventhandlers nicht garantiert werden kann. Zum Beispiel kann
ein Anwender den Browser einfach abschießen.

[Link] Mausereignisse
Selbstverständlich sind Klicks auf Referenzen mit der Maus Ereignisse und ideale Kan-
didaten, um ein JavaScript aufzurufen. Ebenso gibt es zum Überstreichen eines Elements
einer Webseite mit dem Mauszeiger nichts Spannendes zu sagen, zumindest nicht so
lange, wie wir nicht das Ereignisobjekt verwerten, um etwa die Position des Mauszeigers
zu verwenden. Dieses werden wir bei der Behandlung des Ereignisobjekts tun.
11.3 Eventhandling 223

[Link] Tastaturereignisse
Die Reaktion auf das Betätigen einer (beliebigen) Taste auf der Tastatur bildet einen
weiteren Kandidaten für aufzufangende Ereignisse und auch hierzu gibt es nichts Be-
sonderes zu sagen, solange wir nicht das Ereignisobjekt verwerten.

[Link] Formularereignisse
Es gibt eine Reihe von Ereignissen, die hauptsächlich bzw. ausschließlich im Zu-
sammenhang mit Webformularen sinnvoll sind. Das sind zum einen die Änderung, die
Aktivierung und das Verlassen eines Elements in einer Webseite. Diese Aktionen sind
nahezu ausschließlich bei Elementen zur Entgegennahme von Benutzereingaben sinn-
voll. Zum anderen geht es um das Verschicken von Formulareingaben bzw. das Zurück-
setzen des Formulars. Gerade im Zusammenhang mit Formularen zeigen JavaScript-
Eventhandler ihre Stärken. Wir werden Webformulare in einem eigenen Kapitel noch
ausführlich behandeln und dort nur mit JavaScript-Eventhandlern arbeiten.

[Link] Weitere Ereignisse
In JavaScript stehen Ihnen eine Reihe weiterer Eventhandler wie onselect, onerror oder
onabort zur Verfügung und die ganzen Eventhandler in spezifischen Situationen wie dem
Nachladen von Daten und für die es oft in HTML kein Gegenstück gibt.

11.3.2 Grundlagen zu Ereignisobjekten

Hinter der Ereignisbehandlung in JavaScript liegt ein spezielles Objekt – das Ereignis-
objekt bzw. Event-Objekt, das ein Mitteilungsobjekt mit Informationen über die Art des
ausgelösten Ereignisses darstellt. Ereignisobjekte werden vom Browser unablässig er-
zeugt und ein solches Objekt kann zur Reaktion auf Ereignisse gezielt verwertet werden.
Dabei dürfen Sie nicht nur an die wenigen offensichtlichen Ereignisse wie den Klick des
Besuchers mit der Maus oder eine Tastatureingabe denken. Einfaches Verschieben des
Mauszeigers über den Bereich des Browsers findet quasi permanent statt. Da können in
kürzester Zeit Tausende von Ereignisobjekten entstehen. Jedes Verschieben einer Maus-
zeigerposition um wenige Pixel bewirkt in der Regel bereits ein eigenständiges Ereignis-
objekt. Oder denken Sie an das Scrollen des Textes. Auch hier kann das Scrollen eines
Textes bereits um wenige Millimeter Ereignisobjekte generieren. Das Ereignisobjekt
stellt dazu eine Reihe interessanter Eigenschaften mit spezifischen Informationen sowie
diverse Methoden bereit. Betrachten wir als Beispiel die Situation, wenn ein Anwender
mit der Maus in irgendeinen Bereich der Webseite klickt. Bei einem Mausklick erzeugt
der Browser ein Ereignisobjekt, das unter anderem folgende Informationen enthält:

• die verwendete Maustaste,


• eventuell gedrückte Zusatztasten ((STRG), (ALT), (CAPSLOCK)),
• die Koordinaten des Klicks.
224 11 Der DOM – das Mysterium der JavaScript-Welt

Andere Ereignisobjekte, die bei weiteren Ereignissen erzeugt werden, beinhalten natür-
lich andere Informationen, die dem Ereignis angepasst sind. So steht beispielsweise bei
einem Tastendruck die gedrückte Taste als abzufragende Information bereit. Allgemein
beinhaltet ein Ereignisobjekt zahlreiche sinnvolle Informationen, die Sie zur Erstellung
gut angepasster Applikationen nutzen können.

[Link] Blubbern und die Bubble-Phase


Es gibt im Zusammenhang mit der Behandlung des Ereignisobjekts den Begriff der Bub-
ble-Events beziehungsweise der Bubble-Phase. Dieser Begriff klingt vielleicht komisch,
ist aber grundlegend. Wie erwähnt erzeugt ein Browser unablässig Ereignisobjekte, die
entstehen und wieder vergehen. Die meisten weden nicht explizit wahrgenommen/ver-
wertet, sondern schlicht und einfach ignoriert. Oder warum sollte man – außer in spe-
ziellen Fällen – auf jeden Millimeter einer Mausbewegung reagieren? Aber auf manche
Ereignisse will man gezielt reagieren.

[Link].1 Wer ist für die Behandlung zuständig?


Wenn nun ein Ereignis bei einem Knoten in einer Baumstruktur wie dem DOM-Baum
auftritt, stellt sich die Frage, welches der Objekte im Baum nun für ein aufgetretenes
Ereignis zuständig ist. Knoten sind immerhin im Baum oft tief ineinander verschachtelt.
Wenn ein Anwender auf ein Bild in einer Webseite klickt, hat er ja auch gleichzeitig
auf die Webseite selbst geklickt. Ist die Webseite oder das Bild für die Behandlung zu-
ständig? Und wie erfährt der Knoten der Webseite gegebenenfalls davon, dass auf den
untergeordneten Knoten des Bildes ein Klick erfolgt ist. Oder was soll passieren, wenn
mehrere ineinander geschachtelte Objekte den gleichen Eventhandler besitzen und des-
halb auf das Ereignis reagieren könnten?

[Link].2 Der Weg durch den DOM – Event-Bubbling


Die Probleme, beispielsweise Fragen, werden über das sogenannte Event-Bubbling
gelöst. Ein Ereignis wird bei diesem Konzept immer zuerst im innersten Element be-
handelt, bei dem es aufgetreten ist – sofern es dort einen geeigneten Eventhandler gibt!

 Ein behandeltes Ereignisobjekt wird nach der Handhabung in einem Handler sofort
vernichtet.

Das innerste Element ist in der Hierarchie des DOM-Baums am weitesten von der Wur-
zel entfernt. Hat dieses Element keinen geeigneten Eventhandler, wird das Ereignisobjekt
an das nächsthöhere Objekt im DOM-Baum (dem direkten Vorfahren) weitergeleitet usw.
Es steigt wie eine Blase (Bubble) über alle Vorfahren hinauf bis zur Wurzel. Es blubbert
durch den Baum, bis es behandelt wird.

Nicht alle Typen von Ereignisobjekten durchlaufen die Bubble-Phase. Details muss man in speziel-
len Quellen bei Bedarf eruieren.
11.3 Eventhandling 225

Wenn es bis zum Wurzelelement keine Behandlung des Ereignisobjekts gab, wird das
Ereignisobjekt bei einer Variante des Event-Bubblings vernichtet. Bei einer alternativen
Variante wird ein unbehandeltes Ereignisobjekt nach Erreichen der Wurzel wieder bis
zum auslösenden Element zurückblubbern und kann auf dem Weg zurück behandelt
werden. Das kann interessant sein, wenn man mehrere Ereignisobjekte sammeln und ge-
meinsam behandeln möchte.

[Link] Das Ereignisobjekt verwenden


Das Vorhandensein eines solchen Ereignisobjekts ist schön und gut. Nur stellt sich die
Frage, wie Sie das Ereignisobjekt wirklich nutzen können? Die Antwort ist einfach und
kompliziert zugleich. Der anspruchslose Teil der Lösung ist, dass man oft nur das Auf-
treten des Ereignisses beachtet und das Objekt gar nicht benötigt. Falls man doch das
Eventobjekt auswerten will, steht zum Zugriff auf Eigenschaften eines Ereignisobjekts
wie üblich die Punktnotation bereit. Der unangenehme Teil ist, dass man bezüglich des
Ereignisobjekts viele Jahre zwischen der Microsoft-Welt und dem Konzept in anderen
Browsern trennen musste. Diese Probleme sind jedoch mittlerweile behoben und das ur-
sprünglich von Netscape entwickelte Ereignismodell hat sich als Standardvariante etab-
liert, die auch Microsoft unterstützt.

 Der erste Parameter eines Callbacks, der an ein Ereignis gebunden wird, enthält
immer die Referenz auf das Ereignisobjekt.

Hier sind ein paar Eigenschaften, die Sie bei einem Ereignisobjekt auswerten können
(Tab. 11.3).

Beachten Sie, dass wir in dem Kapitel meist mit internen JavaScripts arbeiten. Das ist für die Er-
klärung oft leichter und wird aus didaktischen Gründen gemacht. In der Praxis sollten Sie so gut
wie immer (!) mit externen JavaScripten arbeiten. Zudem werden wir wiederholende Passagen im
Quellcode und im kontext uninteressante Teile (insbesondere zu HTML und CSS) weglassen und
den Quellcode um Zeilenumbrüche und Leerzeichen teils reduzieren, um Platz zu sparen.

Tab. 11.3  Eigenschaften, die beim Ereignisobjekt auszuwerten sind


Eigenschaft Beschreibung
pageX und pageY Die Koordinatenangaben relativ zum Fenster.
screenX und screenY Die Bildschirmkoordinaten.
Which Der Tastaturcode bzw. der Maustastencode. Der Wert 1 steht für die linke
Maustaste, 2 für die mittlere und 3 für die rechte Maustaste.
type Die Art des Ereignisses.
226 11 Der DOM – das Mysterium der JavaScript-Welt

Abb. 11.1 Eine Taste wurde


gedrückt

[Link].1 Ein Beispiel zur Auswertung des Tastaturcodes


Im ersten Beispiel soll die folgende einfache Situation realisiert werden: Der Besucher
drückt eine beliebige Taste und der Tastaturcode der gedrückten Taste wird mittels con-
[Link] in der Konsole angezeigt1. Das ist die HTML-Seite (event_1.html), in der die Er-
eignisbehandlung per Funktionsreferenz an das window-Objekt gebunden wird:


  <body><h1>Drücken Sie eine beliebige Taste!</h1></body>
  <script>
    [Link] = function(ev) {
     [Link]("Gedrückter Tastencode: " + [Link]);
    }
  </script></html>

Im Skript wird das Ereignis onkeypress beim window-Objekt überwacht (Drücken


irgendeiner Taste). Wenn der Anwender eine Taste drückt, wird die anonyme Callback-
Funktion aufgerufen.
Erster Parameter der Funktion ist eine Referenz auf das Ereignisobjekt. Das gilt wie
schon erwähnt auch bei der Notation einer anonymen Funktion, die einem Eventhandler
zugewiesen wird. Die Eigenschaft which des Ereignisobjekts enthält den Tastaturcode. In
der Funktion wird der aktuelle Wert bei jedem Tastendruck ausgegeben (Abb. 11.1). Dies
ist der numerische Code, der über die Eigenschaft which verfügbar ist.

[Link].2 Informationen zu einem Mausklick auswerten


Schauen wir uns noch ein Beispiel mit der Auswertung anderer Eigenschaften des Er-
eignisobjekts an. Hier ist zuerst die HTML-Datei (event_2.html), in der dieses Mal die
Ereignisbehandlung per Funktionsreferenz an das document-Objekt gebunden wird und
auf das Loslassen der Maustaste reagieren soll. Zu dem Zeitpunkt stehen spezifische In-
formationen über den Mausklick zur Verfügung:

1 Wennwir die Zugriffstechniken auf Inhalte der Webseite genauer behandelt haben, werden diese
Ausgaben innerhalb der Webseite erfolgen.
11.3 Eventhandling 227


      <h1>Klicken Sie in die Webseite</h1></body>
  <script language="JavaScript">
     function pos(ev) {
        var text = "Koordinatenangaben relativ zum Fenster (pageX und
pageY): " + [Link] + ", " + [Link] + ".\nBildschirmkoordinaten
(screenX und screenY): " + [Link] + ", " + [Link] + ".\nTasta-
turcode bzw. Maustastencode (which): " + [Link] + ".\nArt des Ereig-
nisses (type): " + [Link];
      [Link](text);
    }
    [Link] = pos;
  </script></body></html>

In dem Beispiel wird der Eventhandler onmouseup überwacht und über die Funktions-
referenz die Funktion pos referenziert. In der Funktion setzen wir einen Antworttext mit
diversen Werten aus dem Ereignisobjekt zusammen. Diese werden wieder in die Konsole
geschrieben (Abb. 11.2).

[Link] Globale Ereignisbehandlung
Man kann eine globale Ereignisbehandlung aufbauen, wenn man das möchte. Dabei mo-
dularisiert bzw. kapselt man die Ereignisbehandlung weiter und verzichtet sogar gänz-
lich auf Eventhandler. Im Gegensatz zum Aufruf eines Eventhandlers per JavaScript bei
einem – aufgrund der DOM-Objektrepräsentation eines HTML-Elements verfügbaren –
Objektgegenstück zu einem HTML-Element können Sie den Reaktionsmechanismus
losgelöst von einem einzelnen HTML-Tag oder einer per JavaScript bei einem Element
aufgerufenen Aktion implementieren. Es handelt sich hier um die direkte Verwertung
des Ereignisobjekts in passenden Methoden. In der Vergangenheit gab es auch hier

Abb. 11.2 Ein Klick in die Webseite


228 11 Der DOM – das Mysterium der JavaScript-Welt

i­nkompatible Implementierungen, aber das Problem ist verschwunden. Die Registrierung


von Listenern bei einem bestimmten Objekt funktioniert mittlerweile zuverlässig und
einheitlich. Das Konzept funktioniert von der Idee her so:

• Ein Ereignis tritt bei einem Quellobjekt (der sogenannten Event Source) auf.
• Das entstandene Ereignisobjekt wird an einen registrierten Zuhörer (besagtem Event
Listener oder kurz Listener) weitergeleitet.
• Erst durch den Listener wird über die konkrete Ereignisbehandlung entschieden und
die Reaktion auch ausgelöst.

Im DOM-Eventmodell der Version 2 wird zur Registrierung eines Listeners eine Me-
thode addEventListener definiert. Die schematische Syntax sieht so aus:

Beispiel

[Link](eventTyp, anweisung, wann) ◄

Der Ereignistyp ist einer der Token, die wir auch bisher schon als Eventhandler ver-
wenden, allerdings ohne vorangestelltes on, also etwa click oder mouseover. Die An-
weisung ist einfach ein Callback und der dritte Parameter ist ein Boolean-Wert, der fest-
legt, ob das Ereignisobjekt auf dem Hin- (true) oder Rückweg (false) durch die Ereignis-
hierarchie (Stichwort Bubble-Phase) abgefangen werden soll. Beispiele:

Beispiel

[Link](click, kBody, false);


[Link](mouseover, kSpan, true); ◄

Das ganze Verfahren ist leistungsfähig, sauber und modular zu strukturieren, gut lesbar
und wartbar. In dem Beispiel (event_3.html) greifen wir etwas vor und selektieren so-
wohl das Bild als auch den Button über eine Id, um daran die Ereignisbehandlung für
einen Mausklick jeweils zu binden (Abb. 11.3).


  <body><h1>Klicken Sie auf das Bild und auf die Schaltfläche</h1>
     <img id="b1" src="images/[Link]" /><br />
     <button id="bt1">Klick da</button><hr /></body>
  <script>
     function kDiv() { [Link]("Klick auf den Bildbereich"); }
     function kH1() { [Link]("Klick auf den Button"); }
     [Link]('b1').addEventListener("click", kDiv, false);
     [Link]('bt1').addEventListener("click", kH1, true);
  </script></html>
11.4 Generelle Zugriffsmöglichkeiten auf DOM-Objekte 229

Abb. 11.3 Klick Bild und


Button

11.4 Generelle Zugriffsmöglichkeiten auf DOM-Objekte

Wie wir gerade gesehen haben, können Sie aus JavaScript heraus im Rahmen des DOM-
Modells auf die einzelnen Elemente einer Webseite zugreifen. Dazu gibt es verschiedene
Möglichkeiten, die aber immer das gleiche Objekt referenzieren und unabhängig von
der Art des Zugriffs die gleichen Member bereitstellen. So gibt es neben den gerade be-
handelten Zugriffen über einen Objektnamen für Standardobjekte des DOM oder ein
Objektfeld noch die folgenden Methoden:

• getElementById
• getElementsByTagName
• getElementsByName

Die Methoden werden sowohl von dem DOM-Objekt document (alle drei) als auch von
node (getElementsByTagName) bereitgestellt. Ebenso gibt es in modernen Browsern
mit Unterstützung von HTML5 und dem neuen DOM5 den Zugriff über einen Klassen-
namen (getElementsByClassName). Die verschiedenen Zugriffsmethoden können in
einem verschachtelten Zugriff problemlos gemischt werden, wenn das sinnvoll ist und
alle Zielbrowser den Weg unterstützen.
230 11 Der DOM – das Mysterium der JavaScript-Welt

11.4.1 Der Zugriff über einen Namen – getElementsByName

Wenn Sie für den Zugriff auf ein Element der Webseite die Methode [Link]-
mentsByName verwenden wollen, müssen Sie den Namen des Elements (in der Regel
als HTML-Attribut name beim Tag notiert) als String-Parameter angeben. Allerdings
müssen Sie für den Zugriff auf ein konkretes Element noch einen Index angeben, da die
Methode ein Array als Rückgabewert liefert. Beachten Sie den Plural im Bezeichner der
Methode. Beispiel:

Beispiel

[Link](passwort)[0] ◄

Alternativ kann man den Namen auch direkt angeben, wenn er eindeutig ist. Das ist aber
nicht immer zuverlässig.

Beispiel

[Link] ◄

 Der Zugriff über einen Namen bietet sich bei klassischen Webformularen an,
denn um die eingegebenen Daten in den Eingabefeldern des Formulars an
den Server schicken zu können, müssen diese Eingabefelder sowieso einen
Namen besitzen. Umgekehrt gilt aber auch, dass der Zugriff über Namen auch
nur bei Formularen zuverlässig funktioniert. Bei anderen Elementen kann es
Komplikationen geben.

Erstellen wir ein Beispiel ([Link]):


  <body><h1>Zugriff auf DOM-Elemente</h1>
     <form name="formular1"><input name="user"/> User<br/>
       <input type="password" name="passwort"/> Passwort<br/>
       <input type="button" value="OK" name="ok" /></form>
     <script>
       [Link]("ok")[0].onclick=function(){
         alert([Link] + "\n" +
           [Link]("passwort")[0].value);
       };
   </script></body></html>

Die Elemente in dem Formular sowie das Formular selbst besitzen ein name-Attribut.
Für den Zugriff auf den User notieren wir die direkte Punktnotation mit Namen und für
die beiden anderen Zugriffe im Skript [Link]. Beachten Sie,
11.4 Generelle Zugriffsmöglichkeiten auf DOM-Objekte 231

dass wir damit für jeden Namen ein Array erhalten und deshalb immer einen Index an-
geben müssen. Auch wenn es jeweils nur ein Element gibt, das zu einem spezifischen
Namen von der Methode geliefert wird.

11.4.2 Zugriff über eine Id – getElementById

Sollte für ein Element im HTML-Tag ein id-Attribut spezifiziert sein, können Sie mit
[Link] darauf zugreifen. Sie geben als String-Parameter einfach die
Id an. Beispiel:

Beispiel

[Link](pw) ◄

Diese Variante des Zugriffs ist sehr zuverlässig und beim Zugriff auf genau ein Element
in der Webseite ideal.

 Gerade bei komplexeren RIAs ist der Zugriff über eine Id fast immer die beste
Wahl. Nur damit haben Sie auch bei Umstrukturierungen des DOM-Baums
eine vollständige Kontrolle über den Zugriff auf ein Element. Man kann sich
aus dem Fenster lehnen und sagen, dass der Zugriff über die Id der Königsweg
ist, wer er denn möglich ist.

 Die Methode getElementById liefert immer genau ein Element und kein Array.
Nur gibt es ein unkalkulierbares Risiko, das aus HTML bzw. dem Verhalten der
Browser selbst stammt. Eine Id sollte vom Prinzip her in einer Webseite eine
eindeutige Kennung darstellen. Aber wegen des Prinzips der Fehlertoleranz
werden Browser keinen Fehler melden, wenn es eine Id mehrfach in einer
Webseite gibt. Sie sind leider darauf angewiesen, selbst aufzupassen, dass es
in einer Webseite keine Id mehrfach gibt. Bei mehreren gleichen Ids ist das
Verhalten verschiedener Browser beim Zugriff über getElementById auf ein Ele-
ment nicht kalkulierbar.

Modifizieren wir unser Formularbeispiel nun so, dass wir Ids verwenden können (dom2.
html):


    <form name="formular1"><input name="user" id="userid"/> User<br/>
       <input type="password" name="passwort" id="pw" /> Passwort<br/>
       <input type="button" value="OK" id="ok" /></form>
    <script>
       [Link]("ok").onclick=function(){
232 11 Der DOM – das Mysterium der JavaScript-Welt

         alert([Link]("userid").value + "\n" +
           [Link]("pw").value);
       };
  </script></body></html>

Sie sehen, dass die Elemente in dem Webformular eine Id haben. Im Skript verwenden
wir dementsprechend nur einfach [Link] zum Zugriff auf die
Formularfelder.

 Sie können bei einem Element natürlich gleichzeitig ein name- und ein id-
Attribut vergeben, was wir im Beispiel ja auch machen. Das sind einfach ver-
schiedene Attribute. Nur gibt es bei verschiedenen Browsern recht seltsame
(unlogische) Verhaltensweisen beim Zugriff auf so ein Element über den
Namen bzw. die Id, wenn die Werte des name- und des id-Attributs gleich
sind. Am besten vermeiden Sie solche Gleichheit der Werte, was ja kein Prob-
lem ist und keinerlei Einschränkung darstellt. Im Beispiel sind deshalb bewusst
die Werte des name- und des id-Attributs verschieden gewählt.

[Link] Zugriff über den Elementnamen – getElementsByTagName


Wenn Sie auf ein Element einer Webseite über [Link] bzw.
im Fall der allgemeinen node-Methode [knoten].getElementsByTagName zugreifen,
geben Sie im String-Parameter einfach den Namen des gewünschten Tags an. Allerdings
müssen Sie noch einen Index angeben, da auch die Methode ein Array als Rückgabewert
liefert. Beispiel:

Beispiel

[Link](input)[10] ◄

Um auch dieses Zugriffsverfahren über den Elementnamen bzw. Tag-Namen in der Praxis
zu zeigen, modifizieren wir wieder unser Beispiel mit dem Webformular ([Link]):


    <form name="formular1"><input name="user" id="userid"/> User<br/>
       <input type="password" name="passwort" id="pw" /> Passwort<br/>
       <input type="button" value="OK" id="ok" /></form>
    <script>
       [Link]("input")[2].onclick=function(){
         alert([Link]("input")[0].value + "\n" +
           [Link]("input")[1].value);
       };
   </script></body></html>

Wie Sie sehen, erfolgt der Zugriff im Skript über getElementsByTagName und den
Namen für die Eingabeelemente in einem Formular – input. Das dritte Element dieses
11.4 Generelle Zugriffsmöglichkeiten auf DOM-Objekte 233

Typs steht für die Schaltfläche, das erste für den Benutzer und das zweite für das Pass-
wortfeld.

[Link] Zugriff über die Klasse


Im Rahmen von CSS setzen Sie häufig Klassen ein. Diese Klassen kann man – wie auch
Ids – multifunktional verwenden. Mittlerweile ist die Methode getElementsByClass-
Name im Rahmen von DOM5 bzw. HTML5 standardisiert worden, die ein Array mit
Elementen gleichen Klassennamens liefert. Sofern es sich um einen neuen Browser han-
delt, sollten Sie also auch so auf DOM-Elemente zugreifen können ([Link]), wobei
die multifunktionale Verwendung einer Id unter CSS und JavaScript oft sinnvoll, aber in
keiner Weise gekoppelt ist:


    <form><input name="user" class="userid"/> User<br/>
       <input type="password" class="passwort"/> Passwort<br/>
       <input type="button" value="OK" class="ok"/></form>
    <script>
       [Link]("ok")[0].onclick=function(){
         alert([Link]("userid")[0].value + "\n" +
           [Link]("passwort")[0].value);
       };
   </script></body></html> ◄

[Link] Der kurze Dienstweg – this


In einigen Situationen können Sie mit dem Schlüsselwort this auf ein Element zugreifen.
Über this sprechen Sie allgemein das gerade aktuelle Objekt an. Im direkten Zusammen-
hang mit dem DOM-Konzept können Sie den Zugriff auf ein DOM-Element verkürzen,
wenn Sie eine Methode direkt auf dem aktuellen Element auslösen. Wenn beispielsweise
aus einem Formularcontainer ein Eventhandler verwendet und in einer aufgerufenen
Funktion this übergeben wird, repräsentiert this das Formular. In anderen Konstella-
tionen (Aufruf einer Funktion über einen Eventhandler eines Formularelement-Tags)
können Sie mit this direkt auf Formularelemente zugreifen (das aktuelle Objekt ist ein
Formularelement). Dann repräsentiert this bereits das Formularelement selbst. Beispiel
(dom_5.html):


    <form><input/>User<br/></form>
    <script>
       [Link]("input")[0].onblur=function(){
         [Link]([Link]);
       };
   </script></body></html>
234 11 Der DOM – das Mysterium der JavaScript-Welt

Wenn der Anwender bei dem Webformular das Eingabefeld verlässt (etwa mit dem Tabu-
lator, steht dieser in der Event-Methode über this zur Verfügung. Damit ist [Link] der
Inhalt des Eingabefeldes.

11.5 Zugriff auf Inhalte von Elementen in der Webseite

Der Zugriff auf den Inhalt von Elementen in einer Webseite lässt sich im Wesentlichen in
drei Kategorien unterscheiden:

1. Zugriff auf Textinhalte


2. Zugriff auf Werte von Formularfeldern
3. Zugriff auf klassische HTML-Attribute über deren DOM-Repräsentation

11.5.1 Zugriff auf Textinhalte

Sie können bei jedem Element in der Webseite, das einen Text als Inhalt hat, diesen
per JavaScript ansprechen und austauschen, sofern Sie den zugehörigen Knoten selek-
tiert haben. Dazu gibt es innerHTML. Als Alternative steht innerText zur Verfügung.
Beide Eigenschaften gehörten schon lange zum Werkzeugkasten von JavaScript-
Programmierern, obwohl sie lange nicht zum offiziellen DOM-Sprachstandard gezählt
haben. Dennoch funktionierte der Zugriff über innerHTML in fast allen Browser-Gene-
rationen dieses Jahrtausends. Diese Eigenschaften vereinfachen die Zugriffswege gegen-
über der Verwendung der direkten node-Methoden und -Eigenschaften wie erheblich und
sind zudem zuverlässiger. Der Unterschied zwischen innerHTML und innerText ist recht
gering. Sowohl über innerHTML als auch über innerText haben Sie Zugang zum Text-
inhalt eines HTML-Elements. Wenn Sie beim dynamischen Ändern des gespeicherten
Inhalts bei innerHTML jedoch HTML-Tags notieren, werden diese bei der Aktualisie-
rung des Elementinhalts interpretiert. Das ist offiziell bei innerText nicht der Fall. In der
Praxis wird innerText fast nie verwendet. Die ebenfalls in einigen Browsern verfügbare
Eigenschaft outerText gibt Ihnen Zugang zum gleichen Wert wie innerText. Nur werden
beim Ändern umgebende HTML-Tags entfernt und durch Text ersetzt. Als weitere Alter-
nativen zum Inhalt eines Elements gibt es outerHTML. Mit outerHTML erhalten Sie Zu-
gang zum Inhalt eines HTML-Tags samt der umgebenden Tags mit allen Angaben.

 Die genannten Eigenschaften sollten nicht direkt beim Laden der HTML-Datei
zum Einsatz kommen, sondern erst nach dem vollständigen Laden der Seite.
Erst dann kann deren Verfügbarkeit gewährleistet werden.
11.5 Zugriff auf Inhalte von Elementen in der Webseite 235

11.5.2 Zugriff auf Formulare

Das Überprüfen und Manipulieren von Webformularen im Allgemeinen ist historisch


eines der ersten Anwendungsgebiete von JavaScript gewesen und auch heute noch eine
wichtige Aufgabe. Dies resultiert nicht zuletzt aus der grundsätzlichen Bedeutung von
Webformularen zur Interaktion mit dem Besucher einer Webseite. Gerade die Möglich-
keiten von modernem DHTML und Ajax machen Webformulare immer leistungsfähiger.
Aus JavaScript stehen zwar über das DOM-Objekt window einige wenige Standard-
methoden zur Entgegennahme von Benutzereingaben zur Verfügung, aber Formulare auf
Basis von HTML stellen die einzig vernünftige Möglichkeit im Web dar, um eine quali-
fizierte Interaktion mit dem Besucher einer Webseite zu erlauben. Als Technik zum Plau-
sibilisieren eines Webformulars bietet sich aufseiten eines Webclients idealerweise Java-
Script an. Aber JavaScript-Zugriff auf Formulare ist nicht nur auf das Überprüfen von
Formulareingaben beschränkt. Darüber hinaus zählen das dynamische Beeinflussen von
Formularinhalten und die Benutzerführung zu den ganz bedeutsamen Aufgaben. Gerade
die neuen Formularelemente in HTML5 und deren inkonsistente Behandlung in den ver-
schiedenen Browsern machen den Zugriff per JavaScript immer bedeutender. Und die-
sem gewichtigen Thema widmen wir uns nun.

[Link] Das Formularobjekt
Wie schon beschrieben, werden Formulare in einer Webseite im Rahmen des DOM-Kon-
zepts in einem Objektfeld mit Namen forms geführt, dessen Bestandteile Objekte vom
Typ form sind. Natürlich können Sie ein Formularobjekt über die üblichen DOM-Metho-
den und -Techniken wie getElementById oder getElementsByTagName etc. ansprechen.
Für das Objektfeld forms selbst ist im Grunde nur die Eigenschaft length mit der An-
zahl der Elemente des Formulars interessant. Allgemein stehen Ihnen jedoch eine ganze
Reihe von Member bei einem solchen einzelnen Formularobjekt vom Typ form zur Ver-
fügung. Die Eigenschaften eines einzelnen Formularobjekts ergeben sich nahezu zwin-
gend aufgrund der HTML-Struktur eines Webformulars bzw. der erlaubten Attribute
des <form>-Tags. Von daher sollten die Eigenschaften bereits bei elementaren HTML-
Kenntnissen weitgehend klar oder zumindest nachvollziehbar sein (Tab. 11.4).
Ein Formularobjekt besitzt nicht viele Methoden. Wesentliche Aufgabe von einem
Formular ist ja die Datenübermittlung und die zentrale Methode erledigt dies. Viel mehr
benötigen Sie nicht an Methoden (Tab. 11.5).
Mit Ausnahme des Containers einer Auswahlliste besitzt jede Objektrepräsentation
eines Formularelements die Eigenschaft value. Damit können Sie mit Ausnahme eines
Auswahllistencontainers den Wert eines beliebigen Formularelements abfragen oder set-
zen. Der Datentyp der Eigenschaft ist in jedem Fall ein String.
Eine der wichtigsten Eigenschaften eines Formularobjekts ist sicher dessen Objektfeld
elements. Darüber haben Sie Zugriff auf alle einzelnen Bestandteile innerhalb von Formu-
laren, also Eingabefelder, Auswahllisten, Kontrollkästchen etc. Einige der Eigenschaften
der enthaltenen Objekte vom Typ element sind bereits verfügbar, wenn der Browser
236 11 Der DOM – das Mysterium der JavaScript-Welt

Tab. 11.4  Die wichtigsten Eigenschaften eines Formularobjekts


Eigenschaft Beschreibung
action URL der Zieldatei für die Formulardaten (relativ oder absolut). Sie können den
Wert über JavaScript auslesen oder setzen.
elements Objektfeld, das alle Elemente eines spezifischen Webformulars als vollständige
Repräsentation der HTML-Implementierung in dem Formular enthält. Oder
anders ausgedrückt – darin befinden sich Objektrepräsentationen aller Formular-
elemente.
encoding Zugang zum Kodierungstyp des Formulars wie application/x-www-form-urlenco-
ded oder text/plain. Das entspricht dem Wert, der bei der HTML-Definition des
Formulars im Attribut enctype steht. Sie können den Wert über JavaScript aus-
lesen oder setzen.
method Art der Datenübertragung. Das ist in der Regel GET oder POST. Sie können den
Wert über JavaScript auslesen oder setzen.
name Der Name des Formulars. Sie können den Wert über JavaScript auslesen oder
setzen.
target Das Ziel für die Antwort vom Webserver. Dabei geben Sie den Namen eines Fens-
ters an, in dem die Ausgabe der Antwort erfolgen soll. Sie können den Wert über
JavaScript auslesen oder setzen.

Tab. 11.5  Die wichtigsten Methoden eines Formularobjekts


Methoden Beschreibung
reset Rücksetzen aller Eingaben in einem Formular. Das Zurücksetzen eines Formulars
bedeutet nicht zwangsläufig das Leeren des Formulars. Das Formular wird nur in
den Anfangszustand versetzt, in dem es nach den HTML-Definitionen sein muss.
Wenn Vorgabewerte in einem Feld vorhanden sind, werden diese wiederhergestellt.
submit Versenden der Daten im Formular

­ enigstens ein Formular mit wenigstens einem Element in der HTML-Datei vorfindet.
w
Andere Eigenschaften sind jedoch nur für bestimmte Formularelemente verfügbar (das
heißt, wenn ein Element des zugeordneten Typs überhaupt vorhanden ist) (Tab. 11.6).
Die Methoden von Objekten vom Typ element sind folgende (Tab. 11.7):
Mit dem Objektfeld options, das in der Objekthierarchie wiederum unter elements
als Eigenschaft angeordnet ist, haben Sie Zugriff auf Auswahllisten innerhalb eines For-
mulars oder genauer – auf die Einträge in einer Auswahlliste, denn der Container wird
über elements direkt bereitgestellt. Das Objektfeld selbst bestimmt über selectedIndex,
welche Auswahlmöglichkeit in einer Auswahlliste aktiviert ist. Wenn kein Eintrag der
Liste ausgewählt ist, dann ist in der Eigenschaft der Wert -1 gespeichert. Ein Objekt in
der Auswahlliste, das einen einzelnen Eintrag repräsentiert, hat folgende Eigenschaften
(Tab. 11.8):
11.5 Zugriff auf Inhalte von Elementen in der Webseite 237

Tab. 11.6  Eigenschaften bei einem Formularelement


Eigenschaft Beschreibung
checked Bei einem Radio- oder Checkbutton (Optionsfeld oder Kontrollkästchen) re-
präsentiert die Eigenschaft den Aktivierungszustand des Elements. Mögliche
Werte sind true beziehungsweise ein numerischer Wert ungleich 0 oder false
beziehungsweise 0.
defaultChecked Diese Eigenschaft steht zur Verfügung, wenn das Element ein Radio- oder
Checkbutton ist. Sie repräsentiert, ob ein Element per Voreinstellung aktiviert
ist oder nicht. Mögliche Werte sind true beziehungsweise ein numerischer
Wert ungleich 0 oder false beziehungsweise 0.
defaultValue Diese Eigenschaft steht zur Verfügung, wenn das Element ein Eingabefeld für
Text ist. Sie repräsentiert den voreingestellten Text, also einen Vorgabewert.
disabled Eine boolesche Information, ob das Formularelement aktivierbar (false) oder
nicht aktivierbar ist (true)
form In der Eigenschaft wird das zugehörige, umgebende Formular eines Formular-
elements als Objekt referenziert, sodass Sie auch aus dem Inneren heraus dar-
über alle Member des Formularobjekts selbst verwenden können. Man benutzt
die Eigenschaft hauptsächlich, wenn man über ein Element eines Formulars
Zugriff auf das Formular selbst haben möchte.
name Name des Formularelements, wie er in HTML bei der Definition des Elements
mit der Zusatzangabe name zugewiesen wurde.
readOnly Die boolesche Eigenschaft enthält die Information, ob ein Formularelement
nur auszulesen (true) oder auch im Wert zu ändern ist (false).
type Information über den Elementtyp eines Formularelements im Zusammenhang
mit allen Formularfeldern, bei denen das Attribut erlaubt ist.
value Wert in dem Formularelement für alle Elemente, die eine Wertrepräsentation
besitzen.

 Achten Sie auf die Unterschiede zwischen den Bezeichnern der Selektions-
eigenschaften von Radiobuttons und Checkboxen sowie Einträgen in Aus-
wahllisten. Bei Radiobuttons und Checkboxen wird in den Token die Zeichen-
kette checked und bei Einträgen in Auswahllisten selected verwendet. Obwohl
ein Aktivierungszustand streng formal nicht das Gleiche wie ein Selektions-
zustand ist, ist das dennoch eine tückische kleine Fehlerquelle, weil man das
leicht durcheinanderbringt.

[Link] Verschiede Beispiele mit Formularen


Schauen wir uns einige Beispiele an, wie Sie mit JavaScript auf Formulare zugreifen
können.

[Link].1 Vorbelegen von Eingabefeldern


Das nachfolgende Beispiel belegt die Werte von Textfeldern eines Formulars vor (Abb. 11.4),
wobei damit auch der lesende Zugriff abgedeckt ist. Das geht im Grunde auch mit HTML,
238 11 Der DOM – das Mysterium der JavaScript-Welt

Tab. 11.7  Methoden von Objekten vom Type element


Methoden Beschreibung
blur Der Fokus verlässt ein Element. Es wird damit deselektiert.
click Der Aufruf bewirkt das Gleiche, wie wenn der Anwender auf ein Element klickt.
focus Fokus auf diesem Element positionieren.
select Selektieren vom gesamten in einem Feld enthaltenen Text.

Tab. 11.8  Eigenschaften von Optionselementen


Eigenschaft Beschreibung
defaultSelected Defaulteinstellung in einer Auswahlliste, das heißt, ob sie per Voreinstellung
aktiviert ist oder nicht. Mögliche Werte sind true beziehungsweise ein numeri-
scher Wert ungleich 0 oder false beziehungsweise 0.
selected Angabe, ob eine Auswahlmöglichkeit in einer Auswahlliste aktiviert ist. Mög-
liche Werte sind true beziehungsweise ein numerischer Wert ungleich 0 oder
false beziehungsweise 0.
value Interner Wert des über den Index angegebenen Eintrags in einer Auswahlliste.
text Text einer Auswahlmöglichkeit in einer Auswahlliste.

aber mit JavaScript können Sie hierbei natürlich bei Bedarf Programmierlogik einfließen las-
sen. Beispiel (form_1.html):


<body><h1>Die Felder in dem Formular sind vorbelegt</h1>
  <form action="" method="get" name="a">
     <input name="a" size="40" /><br /><input name="b" size="40" /><br
/>
     <input name="c" size="40" /><br /><input name="d" size="40" /><br
/>

Abb. 11.4 Setzen und


Auslesen der Werte von
Formularelementen
11.5 Zugriff auf Inhalte von Elementen in der Webseite 239

     <input id="e" size="40" /><br /><input type="Submit" value="OK" />


  </form>
  <script>
     [Link] = function () {
     [Link][0].elements[0].value =
      new Date().toLocaleString();
     [Link][1].value = [Link](22);
     [Link] = [Link]();
     [Link][0].[Link] = "Chaos beherrscht die Ordnung";
     [Link]("e").value = Math.E;
   };
  </script></body></html>

Sie sehen hier ein ganz einfaches Formular. Die Callback-Funktion, die nach dem Fertig-
stellen des DOM-Baums aufgerufen wird, belegt die fünf einzeiligen Eingabefelder in
dem Formular über die Eigenschaft value mit Werten vor. Zur Demonstration wird der
Zugriff sowohl beim Formular selbst als auch den Formularfeldern unterschiedlich ge-
handhabt. Solch ein Mischen von Zugriffsformen ist definitiv praxisfern. In der Praxis
sollten Sie sich immer für eine Variante entscheiden und diese dann konsequent ver-
wenden, sofern es nicht zwingende Gründe für einen unterschiedlichen Zugriff gibt.
Beachten Sie in dem Beispiel, dass es in der HTML-Seite sowohl ein Formular mit
dem Namen a als auch ein Formularelement mit dem identischen Namen a gibt. Zwar
sollten die Namen in einer Webseite eindeutig sein, aber in diesem Fall befinden sich die
beiden Elemente der Webseite in getrennten Namensräumen (der Name des Eingabefelds
ist im Grunde a.a). Diese Wahl gleicher Namen sowohl für das Formular als auch eines
der darin enthaltenen Formularelemente wurde nur aus Demonstrationszwecken gezeigt
und sollte in der Praxis tunlichst unterbleiben. Denn insbesondere beim Zugriff über do-
[Link] ist die Sache nicht mehr unkritisch, da man leicht mit der
Indizierung durcheinanderkommen kann.

[Link].2 Zugriff auf Optionsfelder und Kontrollkästchen


Die eben gezeigte Technik beantwortet nun aber noch nicht die Frage, wie man auf den
Selektionszustand eines Optionsfelds oder eines Kontrollkästchens zugreifen kann?
Hierfür dient ja die Eigenschaft value nicht. Aber sowohl die Objektrepräsentation von
Optionsfeldern als auch Kontrollkästchen in einer Webseite verfügen über die boolesche
Eigenschaft checked. Diese korrespondiert mit dem entsprechenden HTML-Attribut und
ist aus JavaScript heraus sowohl zu lesen als auch zu schreiben. Hat diese Eigenschaft
den Wert true, ist das Formularelement selektiert. Hat sie aber den Wert false, ist das
Formularelement jedoch deselektiert. Beispiel (form_2.html):


<body><h1>Selektionszustand von Radiobutton und Checkboxen</h1>
   <form>Herr <input type="Radio" name="a" value="mann">
240 11 Der DOM – das Mysterium der JavaScript-Welt

     Frau <input type="Radio" name="a" value="frau"><br />


     <input type="checkbox" value="werbung" /> Newsletter</form>
  <h4>Selektionszustand</h4><div id="zustand"></div>
  <script>
     function check() {
       [Link]("zustand").innerHTML =
         "Selektionszustand von " + [Link] + ": " + [Link];
     }
     [Link] = function () {
       [Link][0].elements[0].defaultChecked = true;
       for (i = 0; i < [Link][0].length; i++) {
         [Link][0].elements[i].onclick = check;
       }
     };
  </script></body></html>

In diesem Beispiel finden Sie in dem Webformular zwei Optionsfelder und ein Kontroll-
kästchen per HTML definiert. In der Funktion check, die über den Eventhandler onclick
bei jedem Element des Webformulars aufgerufen wird, wird je nach Zustand des aus-
lösenden Formularelements eine unterschiedliche Meldung angezeigt. Zur Registrie-
rung verwenden wir eine Schleife innerhalb der Initialisierungsfunktion, in der zusätz-
lich beim Laden der Webseite der Defaultselektionszustand des ersten Radiobuttons auf
true gesetzt wird. Sie sehen hier, dass das Setzen einer Aktivierungs- bzw. Selektions-
eigenschaft aus JavaScript nur über den passenden booleschen Wert erfolgen kann. In der
Funktion check wird der Zustand des jeweils beim Klick aktuellen Elements ausgegeben.
Wir können hier mit this arbeiten, denn der Klick wurde ja bei einem spezifischen Radio-
button oder der Checkbox ausgelöst und this steht damit in der aufgerufenen Funktion
jeweils für das entsprechende Formularelement. Natürlich können Sie in der Funktion
auch alle alternativen Wege zum Zugriff auf Formularelemente verwenden, also etwa ein
Formularelement mit [Link][0].elements[0].checked aktivieren. Nur
dann müssen Sie den Index des Elements der aufgerufenen Funktion übergeben, wenn
Sie das Beispiel verwenden wollen.
Wenn Sie die Eigenschaft checked für ein Element einer Gruppe aus Optionsfeldern
auf true setzen, werden automatisch alle anderen Elemente der Gruppe deselektiert. Das
ist nichts weiter als das typische Verhalten von zusammengehörenden Optionsfeldern.

[Link].3 Zugriff auf eine Auswahlliste


Nun betrachten wir, wie Sie auf die Elemente einer Auswahlliste in einem Listenfeld zu-
greifen? Hier steht die Eigenschaft value zwar nicht beim umgebenden <select>-Con-
tainer zur Verfügung, aber bei den enthaltenen Elementen vom Typ <option>. Und dann
gibt es noch die Eigenschaft text, die wir uns auch genauer ansehen werden. Wenn Sie
ein Element einer Auswahlliste selektiert haben, erhalten Sie den Wert in dem <option>-
Tag über die Eigenschaft value. Da es sich bei jedem Eintrag in dem Objektfeld selbst
11.5 Zugriff auf Inhalte von Elementen in der Webseite 241

um ein Objekt handelt, stellt dieses auch weitere spezifische eigene Eigenschaften bereit,
die auch erst den konkreten Nutzen darstellen.
Darüber hinaus gibt es freilich einige sehr spezielle Feinheiten beim Umgang mit
Auswahllisten aus JavaScript heraus. Oder anders ausgedrückt – der Zugriff bei Aus-
wahllisten ist etwas tückisch. Denn wenn Sie bei einer Auswahlliste einen JavaScript-
Zugriff über Objektfelder wählen, referenzieren Sie mit dem elements-Objektfeld den
<select>-Container, jedoch noch nicht die einzelnen Einträge in den <option>-Contai-
nern! Andererseits wird die Selektion der Einträge in der Aufzählungsliste einige über-
raschende Probleme bereiten. Dementsprechend müssen Sie ziemlich aufpassen, wie Sie
die Ereignisse auslösen und was Ihnen wann und wie bereitgestellt wird.
Obwohl es nun verschiedene Ansätze gibt, wie man auf Klicks auf Einträge in Aus-
wahllisten reagieren kann (etwa mit onclick bei den Elementen eines <select>-Contai-
ners oder auch bei den einzelnen Einträgen in den <option>-Containern), sollten Sie in
der Regel den folgenden Weg gehen, wenn die Reaktion universell funktionieren soll.
Vergegenwärtigen wir uns nun erst einmal, was wir eigentlich wollen? Wir wollen
testen oder festlegen, welcher Eintrag in einer Auswahlliste selektiert ist. Die Objekt-
repräsentation einer Auswahlliste (also des Containers selbst) besitzt dazu die Eigen-
schaft selectedIndex. Damit können Sie ermitteln, welcher Eintrag in einer Auswahlliste
aktiviert ist, oder auch einen Eintrag in der Auswahlliste aus JavaScript heraus selektieren.
Allerdings werden Sie feststellen, dass Sie damit auch Probleme bekommen, wenn Sie
das auslösende Element ungeschickt verwerten wollen. Wenn Sie jedoch this beim Aus-
lösen eines Ereignisses aus einer Auswahlliste als Parameter übergeben, sind Sie auf der
sicheren Seite. Einen Eintrag als selektiert zu markieren, ist auch nicht kompliziert. Sie
müssen nur den passenden numerischen Index eines Eintrags verwenden. Dazu setzen Sie
die selectedIndex-Eigenschaft auf den numerischen Wert des gewünschten Eintrags und
damit ist das Element der Auswahlliste selektiert. Alternativ können Sie aber auch mit der
Eigenschaft selected eines einzelnen Eintrags in der Auswahlliste arbeiten, die Sie selbst-
verständlich ebenso abfragen wie zum Selektieren setzen können. So etwas für Selektion
eines Eintrags in der Auswahlliste über die Eigenschaft selected würde also funktionieren:

Beispiel

function welcherIndex(i) {
  [Link][i].selected=true;
} ◄

In diesem Fall setzt man die Eigenschaft selected einfach auf den Wert true beziehungs-
weise einen numerischen Wert ungleich 0 – dann ist das Element selektiert – oder den
Wert false beziehungsweise 0 – in diesem Fall ist das Element deselektiert. Wenn ein
Eintrag als selektiert gesetzt wird, deselektiert das alle anderen Einträge, sofern keine
Mehrfachselektion vorgesehen ist. Unsere zweite Überlegung betrifft den Event-
handler, den wir bei der Auswahlliste verwenden. Wir werden statt onclick auf onchange
242 11 Der DOM – das Mysterium der JavaScript-Welt

­umsteigen. Mit onchange werden wir ein Ereignis erst dann auslösen, wenn ein Eintrag
in der Auswahlliste selektiert (oder genauer geändert) wird. Betrachten Sie das nach-
folgende Beispiel (form_3.html):


<body><h1>Selektion bei Auswahllisten</h1>
   <form><select name="ticket" id="t">
     <option id="e">Erwachsener</option>
     <option id="k">Kind</option>
     <option id="f">Familie</option></select></form><hr />
   <div id="info"></div>
   <script>
     function init() {
      [Link][0].elements[0].defaultChecked = true;
      [Link]("t").onchange = function () {
         welcherIndex(this);
       }
     }
     function welcherIndex(i) {
       [Link]("info").innerHTML = "Ausgewählter Eintrag: "
         + [Link];
     }
     [Link] = init;
   </script></body></html>

Sie sehen eine einfache Auswahlliste (Abb. 11.5). In der Funktion init können Sie er-
kennen, dass der Eventhandler onchange beim Container der Auswahlliste verwendet
wird. Dieser Container hat die Id t. Sie sehen, dass wir das auslösende Objekt der Funk-
tion welcherIndex als Parameter übergeben und mit der Eigenschaft selectedIndex den
Index des ausgewählten Eintrags in der Auswahlliste bestimmen können.
Nun stellt sich noch die Frage, wie man an den Text kommt, der hinter den Einträgen
einer Auswahlliste liegt und dem Anwender angezeigt wird? In der Regel können Sie
einfach die Eigenschaft value des auslösenden Elements verwenden. Früher ging das
nicht immer und im Grunde ist der Zugriff über value auch nicht ganz logisch. Denn
es gibt ja noch die Eigenschaft text. Werfen wir also nochmals einen genauen Blick auf
die Eigenschaft value eines Eintrags in einer Auswahlliste. Damit greifen wir gezielt auf
den Wert eines Eintrags in einer Auswahlliste zu. Beachten Sie aber, dass der Wert eines
Eintrags in einer Auswahlliste nicht die Bedeutung hat, die er bei den meisten anderen
Formularelementen hat.
Der Wert repräsentiert im Allgemeinen nicht die sichtbare Beschriftung eines Eintrags
in einer Auswahlliste, sondern den Wert, der beim Versenden eines Formulars an den
Server übermittelt wird. Wenn value nicht angegeben wird (weder in HTML noch per
JavaScript), wird aber der Text aus dem <option>-Container an den Server übermittelt
11.5 Zugriff auf Inhalte von Elementen in der Webseite 243

Abb. 11.5 Der Zugriff auf


den ausgewählten Eintrag in
der Auswahlliste

und damit value indirekt durch den dort angezeigten Text belegt. Wenn jedoch die Eigen-
schaft value bei einem Eintrag in einer Auswahlliste angegeben ist, steht der angezeigte
Text nicht über value, sondern nur über die Eigenschaft text bereit.

[Link].4 Verschicken, Zurücksetzen und Plausibilisieren von Formulardaten


Betrachten wir das Verschicken und Zurücksetzen der gesamten Formulardaten. Wenn
Sie kein JavaScript anwenden, werden Formulardaten in der Regel mittels eines HTML-
SUBMIT-Buttons verschickt. In der Konstellation ist JavaScript hauptsächlich zum Ab-
bruch der Aktion interessant. Sie haben die Möglichkeit, das Versenden oder Zurück-
setzen eines Formulars zu unterbinden. Gegebenenfalls können Sie vorher auch eine
Bestätigung der Aktion einzuholen. Dabei können Sie natürlich auch Eingaben in
Formularfeldern vor dem Verschicken der Daten kontrollieren und bei Fehlern in der
Logik die Aktion abbrechen. Um das zu erreichen, muss die aufgerufene JavaScript-
Funktion bei einem HTML-Eventhandler mit vorangestelltem Schlüsselwort return no-
tiert werden und in der aufgerufenen Funktion selbst false als Rückgabewert zurück-
gegeben werden, wenn die Aktion abgebrochen werden soll. Wenn true (oder besser kein
explizites false) zurückgegeben wird, werden die Formulardaten verschickt. Beispiel:

Beispiel

<form name="f" action="[Link]" method="post" onsubmit="return


weg()" /> ◄

Aber hier ist man schon deshalb auf einem falschen Weg, weil man keine HTML-Event-
handler mehr verwenden sollte, sondern die gesamte Logik in JavaScript verlagert
wird. Jede Objektrepräsentation eines Webformulars besitzt eine zugeordnete Methode
submit. Deren Aufruf schickt ebenfalls die Werte in dem Formular ab. Die Alternative
ist reset. Wenn Sie die Daten nicht verschicken oder das Formular nicht zurücksetzen
wollen, rufen Sie einfach die Methoden nicht auf. Sie können damit das Abschicken der
Formulardaten dem Klick auf einem Hyperlink, einem einfachen Button oder jeder ande-
ren Situation zuordnen, die sich mit einem Eventhandler beschreiben lässt. Und n­ atürlich
244 11 Der DOM – das Mysterium der JavaScript-Welt

lässt sich damit leicht eine Logik implementieren, ob Daten verschickt werden oder
nicht.
Das gesamte Verfahren kann man hervorragend mit dem Plausibilisieren von Formu-
laren kombinieren. Die wichtigste Anwendung von JavaScript auf Formulare ist sicher
die Plausibilisierung von Benutzereingaben. Das Plausibilisieren eines Webformulars
bedeutet, die Schlüssigkeit der Anwendereingaben vor einem Versenden der Daten zu
kontrollieren und bei Widersprüchen zu den geforderten Vorgaben Gegenmaßnahmen zu
ergreifen. Die Sache ist jedoch alles andere als trivial und die wesentlichen Dinge be-
treffen nicht die Programmierung selbst. Plausibilisierung eines Webformulars ist nur als
globales Konzept schlüssig, das auch die Weiterverwendung der Daten nach dem Ver-
senden sowie diverse weitere Fakten beinhalten muss. Bei der Plausibilisierung eines
Webformulars müssen Sie sich zahlreicher Problemstellungen bewusstwerden. Obwohl
wir das Thema nur anreißen wollen, kann man einige Fragen recht pauschal zusammen-
fassen:

• Was muss plausibilisiert werden? Das bedeutet im Wesentlichen, welche Formular-


felder müssen kontrolliert werden?
• Welche Abhängigkeiten zwischen Benutzereingaben und Formularfeldern gibt es?
• Wie genau muss plausibilisiert werden? Das umfasst beispielsweise Fragen, ob es bei
einem Feld genügt, dass es gefüllt ist (ein sogenanntes Pflichtfeld), oder muss der In-
halt eine bestimmte Form haben (etwa nur numerisch sein)?
• Wann wird plausibilisiert? Kontrolliert man zum Beispiel die Benutzereingaben direkt
auf einem Eingabefeld oder erst beim Verschicken eines Formulars? Oder beim Ver-
lassen einer Webseite, wenn die Daten über mehrere Webseiten gesammelt werden,
bevor sie zum Server geschickt werden?
• Wo und wie wird plausibilisiert? Auf dem Server oder auf dem Client? Oder an bei-
den Stellen redundant? Meist ist eine redundante Überprüfung unabdingbar.

Sie sehen, dass man bei einem Plausibilisierungskonzept viel beachten muss. Hier eine
vernünftige Abwägung zwischen notwendigen Prüfungen, deren tatsächlicher Um-
setzung und realisierbarem Aufwand zu finden, ist alles andere als einfach. Verfolgen
wir kurz die Überlegung, wann ein Webangebot zu plausibilisieren ist. Bei klassischen
Webapplikationen hat es sich durchgesetzt, das Formular in der Regel erst beim Ab-
schicken der Formulardaten zum Server zu kontrollieren. Wenn wir allerdings den asyn-
chronen Datenaustausch mit Ajax einbeziehen, wird sich die Plausibilisierung nach und
nach etwas mehr zum Zeitpunkt des Verlassens eines Felds oder sogar einer einzelnen
Tastatureingabe verlagern. Ajax erlaubt es beispielsweise in Verbindung mit DHTML,
einem Anwender eine Rückmeldung zu geben, wenn beim Verlassen eines Eingabe-
felds ein offensichtlicher Fehler vorliegt, ohne ihn durch Mitteilungsfenster oder Neu-
laden der Seite in seinem Arbeitsfluss zu behindern. So wird Ajax nach und nach dafür
sorgen, dass sich Webapplikationen auch bei der Plausibilisierung mehr dem Verhalten
von Desktop-Applikationen annähern. Dennoch ist das Abschicken von Formulardaten
11.5 Zugriff auf Inhalte von Elementen in der Webseite 245

derzeit immer noch die zentrale Stelle, wo Webapplikationen zu plausibilisieren sind.


Wie Sie beim Auftreten einer Fehleingabe nun reagieren, bleibt Ihnen überlassen. In der
Regel werden Sie aber das Versenden der Formulardaten unterbinden, bis der Fehler be-
seitigt ist, und einem Anwender entsprechende Rückmeldungen geben.
Das Abbrechen des Verschickens der Formulardaten ist jedoch nur der erste Schritt.
Wie gehen Sie nun im Fehlerfall weiter vor? Das hängt massiv von der Art des Prob-
lems ab. Sie können bei einem Fehler eine Fehlermeldung anzeigen, im Fall von zu vie-
len Eingabezeichen die Anzahl der eingegebenen Zeichen auf die Maximalanzahl ab-
schneiden, das Eingabefeld vollständig leeren oder den Fokus wieder in das Eingabefeld
setzen und dem Anwender die Korrektur überlassen.
Die typischen Überprüfungen von Formularfeldern sind nun neben der Festlegung
von Pflichtfeldern oder der minimalen oder maximalen Anzahl der einzugebenden Zei-
chen meist Inhaltsüberprüfungen. Hier kommen dann entweder die verschiedenen
String-Methoden zum Suchen und Vergleichen von Inhalten zum Einsatz oder spezielle
Methoden aus dem Umfeld von regulären Ausdrücken, wobei reguläre Ausdrücke auch
schon das Zauberwort darstellen. Sie sind der Schlüssel zum Formulieren von komple-
xen Bedingungen an ein Eingabefeld wie eine vernünftige Struktur einer E-Mail oder ein
gewisses Zahlenformat. Ebenso können Sie damit natürlich auch die Eingabe bestimmter
Zeichen verhindern. Sie müssen Sie einfach in einem Feld suchen und beim Auffinden
ersetzen oder das Versenden der Formulardaten verhindern. Zu den Details sei auf unsere
Ausführung zu regulären Ausdrücken verwiesen. Hier sind nun ein paar praxisorientierte
Gegenmaßnahmen in einem vollständigen Beispiel, bei dem zwei Eingabefelder kont-
rolliert werden. Insbesondere wollen wir auch sämtliche Logik in JavaScript verlagern.
Wir arbeiten also mit den Methoden submit und reset sowie JavaScript-Eventhandler.
Zudem verwenden wir mit focus eine Methode eines Objekts aus dem Objektfeld ele-
ments ([Link]):

<body><h1>Geben Sie Ihre Zugangsdaten ein</h1>


   <form><div class="titel">Userid:</div>
     <input type="text" id="user" name="user" /><br />
     <div class="titel">Passwort:</div>
     <input type="password" id="pw" name="pw" /></form><hr />
   <button id="frage">Daten Wegschicken</button>
   <button id="zurueck">Zurücksetzen</button><div id="info"></div>
   <script>
     function maxAnzahl() {
       var f = [Link][0];
       if ([Link] < 8) { // Kontrolle Userid
         [Link]("info").innerHTML =
           "Die Userid hat zu wenig Zeichen";
         [Link] = "";
         [Link]();
         return false;
246 11 Der DOM – das Mysterium der JavaScript-Welt

       }
       if ([Link] < 10) { // Kontrolle Passwort
         [Link]("info").innerHTML =
           "Das Passwort besteht aus mindestens 10 Zeichen";
         [Link] = "";
         [Link]();
         return false;
       }
       [Link] = "[Link]";
       [Link]();
     }
     function zuruecksetzen() {
       if (confirm("Das Formular wird zurückgesetzt.\nSind Sie sicher?")) {
         [Link][0].reset();
         [Link]("info").innerHTML = "";
         return false;
        }
        return false;
     }
     function init() {
       [Link]("frage").onclick = maxAnzahl;
       [Link]("zurueck").onclick = zuruecksetzen;
     }
     [Link] = init;
   </script>
</body></html>

Das Webformular beinhaltet zwei einzeilige Eingabefelder (eines davon ist ein Passwort-
feld) und neben der Schaltfläche, die wir zum Verschicken der Daten verwenden wollen,
gibt es eine Schaltfläche zum Zurücksetzen des Formulars. Beide werden mit dem <but-
ton>-Tag umgesetzt (Abb. 11.6). Die Benutzereingaben werden so plausibilisiert, dass
eine Kontrolle der zwei Eingabefeldern beim Verlassen des Formulars erfolgt. Bei beiden
Schaltflächen finden Sie den onclick-Eventhandler, worüber die Funktionen maxAnzahl

Abb. 11.6 Das Webformular


zur Eingabe von Userid und
Passwort
11.5 Zugriff auf Inhalte von Elementen in der Webseite 247

und zuruecksetzen aufgerufen werden. Schauen wir uns die Funktion maxAnzahl im De-
tail an. In der lokalen Variablen f speichern wir eine Referenz auf das Formularobjekt
(dieses Mal über das Objektfeld, was aber vollkommen egal ist). In der Funktion wird
dann mit den zwei nacheinander notierten if-Abfragen und namentlicher Angabe des zu
testenden Eingabefelds ein Kontrollsystem aufgebaut.
Zuerst wird die Länge des ersten Eingabefelds kontrolliert (if([Link] < 8))).
Gibt der Anwender zu wenige Zeichen ein, erhält er eine Fehlermeldung, der Wert in dem
Formularfeld wird geleert, der Fokus wird auf das Feld gesetzt und mit return wird die
Funktion verlassen. Dabei wird der Wert false zurückgegeben. Die Prozedur wiederholt
sich für das zweite Eingabefeld analog. Sollte die Userid die erlaubte Anzahl der ­Zeichen
nicht unterschreiten, muss das Passwort die gleiche Hürde nehmen. Nur mit 10 Zeichen
als minimaler Länge. Beachten Sie, dass die beiden Tests nichts miteinander zu tun haben
(kein if-else). Die zweite Überprüfung findet nur statt, wenn die erste Überprüfung kein
Problem entdeckt hat (andernfalls wird die Funktion ja mit return verlassen und die
zweite if-Abfrage wird nicht erreicht). Das können Sie natürlich auch anders lösen und
etwa dem Anwender gleich alle Probleme zusammen anzeigen. Wenn die beiden Prüfun-
gen keine Probleme ermittelt haben, wird das Ziel für die Formulardaten gesetzt und die
Formulardaten werden abgeschickt. Beim Zurücksetzen des Formulars wird zuerst eine
Bestätigung des Anwenders eingeholt und dann entweder das Formular zurückgesetzt
oder nicht. In jedem Fall geben wir aber wieder false zurück, um das Verschicken der
Formulardaten zu verhindern (was ja in der Situation auf gar keinen Fall geschehen darf).

11.5.3 Zugriff auf klassische HTML-Attribute

Die per DOM verfügbaren Elemente einer Webseite verfügen in der Regel über die glei-
chen Eigenschaften, die als Attribute bei dem zugrunde liegenden HTML-Tag vorhanden
sind. Dazu kommen noch einige globale Eigenschaften wie style für den Zugriff auf
Style Sheets oder Eventhandler. Dementsprechend verfügt auch ein DOM-Objekt, das
ein Bild in der Webseite repräsentiert, über diese Eigenschaften. Indem Sie die Namen
der Eigenschaften angeben, können Sie lesend und meist auch schreibend darauf zu-
greifen. Beispiel ([Link]):


  <body><h1>Zugriff auf klassische HTML-Attribute über DOM</h1>
     <table><tr>
          <td><img src="images/[Link]" alt="Hardware" width="200" /></td>
          <td id="info" width="200"></td></tr></table>
     <button id="b1">Groß</button><button id="b2">Klein</button>
     <button id="b3">Neues Bild</button><button id="b4">Altes Bild</but-
ton>
     <script>
248 11 Der DOM – das Mysterium der JavaScript-Welt

       with (document) {
         getElementsByTagName("img")[0].onmouseover = function() {
           getElementById("info").innerHTML =
             getElementsByTagName("img")[0].alt;
         };
         getElementsByTagName("img")[0].onmouseout = function() {
           getElementById("info").innerHTML = "";
         };
         getElementsByTagName("img")[0].onclick = function() {
          getElementById("info").innerHTML =
             getElementsByTagName("img")[0].src;
         };
         getElementById("b1").onclick = function() {
           getElementsByTagName("img")[0].width = "400";
         };
         getElementById("b2").onclick = function() {
           getElementsByTagName("img")[0].width = "200";
         };
         getElementById("b3").onclick = function() {
           getElementsByTagName("img")[0].src = "images/[Link]";
           getElementsByTagName("img")[0].alt = "Kater im Korb";
         };
         getElementById("b4").onclick = function() {
           getElementsByTagName("img")[0].src = "images/[Link]";
           getElementsByTagName("img")[0].alt = "Hardware";
         };
       }
     </script></body></html>

Die Webseite in dem Beispiel enthält in einer Tabelle ein Bild und daneben eine leere
Zelle mit einer Id, in der bei gewissen Aktionen des Anwenders Informationen angezeigt
werden sollen (Abb. 11.7). Darunter befinden sich vier Schaltflächen. Bei einem Klick
sollen gewisse Aktionen ausgelöst werden.
Beachten Sie, dass wir in dem Skript explizit mit with arbeiten, denn wir haben hier
genau so einen Fall vorliegen, wo das sinnvoll ist. Wir müssten sonst ohne with bei jeder
Eigenschaft und Methode document voranstellen. In den folgenden Erklärungen notieren
wir aber document zum besseren Verdeutlichen. Beim Bildobjekt, das wir mit document.
getElementsByTagName("img")[0] ansprechen, werden die Eventhandler onmouseover,
onmouseout und onclick registriert. Damit wird beim Überstreichen des Bilds mit der
Maus und bei einem Klick im Bildbereich in der rechten Tabellenzelle eine Information
angezeigt und beim Verlassen des Bildbereichs mit dem Mauszeiger der Anzeigebereich
wieder gelöscht. In mehreren Zeilen sehen Sie, wie man innerHTML nutzt (mit docu-
[Link]("info").innerHTML) und wie auf den Wert des alt-Attributs des
Bilds zugegriffen wird ([Link]("img")[0].alt). Vollkommen
11.6 Das Objekt document 249

Abb. 11.7 Die Webseite nach


dem Laden

analog sehen Sie den Zugriff auf ein weiteres Attribut von img – die URL der Datei (do-
[Link]("img")[0].src).
Die anderen Zeilen zeigen nun noch weitere schreibende Zugriffe auf Attribute eines
HTML-Tags. Hier werden mit den ersten beiden Schaltflächen konkret die Breite des Bilds
über die Eigenschaft width und mit der dritten und vierten Schaltfläche die Werte von src
und alt verändert, was zu einem neuen Bild inklusive neuem Alternativtext führt (Abb. 11.8).
Die ausführenden Funktionen werden jeweils beim Klick auf eine der Schaltflächen
aufgerufen.

11.6 Das Objekt document

Über das document-Objekt, dessen Bedeutung eingangs schon hervorgehoben wurde,


haben Sie Zugriff auf die Objektrepräsentation der Webseite selbst. Eine Objektinstanz
von document wird als typisches DOM-Objekt automatisch erzeugt, wenn der WWW-
Browser eine HTML-Datei vorfindet. Sie können über dieses Objekt beispielsweise
HTML-formatierten Text in das angezeigte Dokument schreiben und auf alle enthaltenen
Tags der Webseite zugreifen.
Das Objekt document besitzt diverse Eigenschaften, die sowohl abzufragen als auch
teilweise zu ändern sind. Allerdings verlieren die Eigenschaften von document rapide
an Bedeutung, die sich auf optische HTML-Effekte beziehen (hier verwendet man heut-
zutage CSS). Andere Eigenschaften werden auch heute noch teils inkonsistent in ver-
schiedenen Browsern unterstützt, aber vor allen Dingen kaum noch benötigt. Gewisse
Bedeutung haben noch charset für die verwendete Zeichenkodierung eines Dokuments,
cookie für die Verwaltung der Cookies, lastModified mit dem Datum und der Uhrzeit der
letzten Änderung der Datei oder referrer mit der URL der Webseite, von dem aus die ak-
tuelle Webseite aufgerufen wurde.
250 11 Der DOM – das Mysterium der JavaScript-Welt

Abb. 11.8 Ein alternatives


Bild mit einer anderen Größe

Wenn die Bedeutung der Eigenschaften von document in modernen Webseiten ra-
pide abnimmt – wie kann das mit der oben im Kapitel geäußerten Aussage in Überein-
stimmung gebracht werden, dass document neben node den Kern aller modernen Web-
applikationen darstellt? Nun – es gibt ja noch die Methoden. Diese sind zum Teil eben-
falls von der Entwicklung im Web überholt worden (gerade die Methoden zur Gestaltung
der Webseite, aber auch der Ereignisbehandlung), zum Teil sind sie aber auch so elemen-
tar, dass ich das Objekt document so herausheben möchte. Die wichtigsten Methoden des
Objekts document dürften die Methoden zum Schreiben in die Seite und für den Zugriff
auf Bestandteile der Webseite sowie vor allen Dingen das Erstellen von Knoten sein. Das
Objekt document besitzt die folgenden immer noch interessanten Methoden (Tab. 11.9).

11.6.1 Ein Dokument mit Knoten dynamisch aufbauen

Mehrere Methoden von document sind dazu da, dass Sie damit Knoten erzeugen, um Web-
seiten dynamisch per Skript aufzubauen. Um diese Methoden überhaupt zu verstehen,
muss Ihnen aber zum einen das Knotenkonzept im DOM geläufig sein. Das haben wir
schon behandelt, aber das nachfolgende Beispiel vertieft die theoretischen Grundlagen
und wendet die Methoden createAttribute, createElement und createTextNode an. Beispiel
([Link]):
11.6 Das Objekt document 251

Tab. 11.9  Methoden von document


Methode Beschreibung
close Hiermit wird ein Dokument geschlossen. Man verwendet diese Me-
thode normalerweise in Verbindung mit [Link] und document.
write beziehungsweise [Link]. Der Aufruf der Methode er-
zwingt die sofortige Anzeige der bis dahin vorhandenen Daten. Ebenso
wird jede nachfolgende Schreiboperation den bestehenden Inhalt über-
schreiben, wenn vorher ein Dokument nicht wieder explizit geöffnet
wurde.
createAttribute Erzeugen eines neuen Attributs für ein HTML-Element. Die Methode
lässt sich auch auf XML-Elemente anwenden.
createElement Erzeugen eines neuen Elementknotens im DOM-Baum eines Doku-
ments.
createTextNode Mit der Methode können Sie im DOM-Baum eines Dokuments einen
Textknoten erzeugen.
getElementById Zugriff auf ein Element über die Id. Die Methode liefert als Rückgabe-
wert einen Knoten.
getElementsByName Zugriff auf ein Element über den Namen, wie er im Attribut name an-
gegeben wird. Die Methode liefert als Rückgabewert ein Array mit
Knoten.
getElementsByTagName Zugriff auf ein Element über den Namen eines Elements bzw. Tags.
Die Methode liefert als Rückgabewert ein Array mit Knoten.
getSelection Über diese Methode können Sie abfragen, welchen Text ein Anwender
im Dokument selektiert hat.
open Der Aufruf der Methode öffnet ein Dokument zum Schreiben. Man
verwendet diese Methode normalerweise in Verbindung mit document.
write beziehungsweise [Link]. Optional können Sie den
Dateityp als Parameter (als Mime-Typ) übergeben. Erlaubt sind die
Angaben "text/html", "text/plain", "image/gif", "image/jpeg", "image/x-
bitmap" oder diverse Plugin-Typen.
write und writeln Mit diesen Methoden können Sie in das Dokumentfenster schreiben.
Im Unterschied zu write wird bei writeln noch ein Zeilenumbruch-
zeichen am Ende eingefügt. Bitte beachten Sie, dass dies in einem
HTML-Dokument aber keinen Zeilenumbruch bewirkt, sofern Sie
nicht weitere HTML-Formatierungen wie <pre> verwenden. Die Me-
thoden werden so gut wie ausschließlich beim Laden einer Webseite
verwendet. Für den Zugriff nach dem Laden der Webseite gibt es die
oben beschriebenen Eigenschaften innerHTML, value etc.


  <body><h1>Zugriff auf DOM-Elemente</h1>
     <form><input type="button" value="OK" name="ok" /></form><hr />
     <script>
       [Link]("ok")[0].onclick = function() {
252 11 Der DOM – das Mysterium der JavaScript-Welt

         var hintergrundfarbe = [Link]("bgcolor");


         [Link] = "blue";
         var vordergrundfarbe = [Link]("text");
         [Link] = "white";
         [Link]("body")[0].
           setAttributeNode(hintergrundfarbe);
         [Link]("body")[0].
           setAttributeNode(vordergrundfarbe);
         var u1 = [Link]("h1");
         var text = [Link](
           "Erstellen von Knoten mit Methoden von document");
         [Link](text);
         [Link]("body")[0].appendChild(u1);
       };
     </script></body></html>

In dem Beispiel sehen Sie eine Webseite, die bis auf das Grundgerüst und ein Formular
mit einer Schaltfläche keinerlei statische HTML-Strukturen aufweist (Abb. 11.9).
Wenn Anwender jedoch die Schaltfläche anklicken, werden im Körper der Webseite
eine Überschrift der Ordnung 1 mit einem spezifischen Textinhalt auftauchen. Ebenso
ist die Hintergrund- und Vordergrundfarbe der Webseite spezifiziert, obwohl im <body>-
Tag kein Attribut dafür angegeben wurde und auch keine CSS-Regeln verwendet werden
(Abb. 11.10).
In Entwicklertools eines Browsers können Sie schön sehen, wie die tatsächlich vom
Browser verarbeitete Webseite aussieht. Dynamisch wurden neben der Überschrift auch
die Attribute des <body>-Tags hinzugefügt. Wenn man mehrfach die Schaltfläche an-
klickt, wird immer eine weitere Überschrift an den vorhandenen Inhalt a­ngehängt.

Abb. 11.9 Die Webseite nach dem Laden


11.6 Das Objekt document 253

Abb. 11.10 Die Entwicklertools eines Browsers zeigen die dynamisch hinzugefügten Strukturen

Schauen wir uns an, wie das gemacht wurde. Zuerst wird mit [Link]-
te("bgcolor") ein Attributknoten (ein node-Objekt) erstellt, der in der Variable hinter-
grundfarbe gespeichert wird. Der Attributknoten steht damit für die Angabe einer
Hintergrundfarbe bei einer Webseite über das Attribut bgcolor des <body>-Tags. Der
Attributknoten repräsentiert bis jetzt noch keinen expliziten Wert, aber in der folgen-
den Zeile wird der Wert dieses Attributknotens mit der Eigenschaft nodeValue gesetzt
([Link] = "blue";). Anschließend erzeugen wir so einen weiteren
Attributknoten für die Vordergrundfarbe mittels des Attributs text und setzen auch dafür
einen Farbwert. Nun sind bis dahin zwar die Attributknoten erzeugt, aber noch nicht der
Webseite hinzugefügt worden! Das muss mit einer expliziten Anweisung erfolgen – der
node-Methode setAttributeNode. Die beiden Attribute werden dem <body>-Tag und
damit der Webseite hinzugefügt. Im nächsten Schritt erzeugen wir mit [Link]-
teElement einen Elementknoten und speichern das resultierende node-Objekt in der Va-
riablen u1 (var u1 = [Link]("h1");).
Der Folgeschritt erzeugt mit [Link] einen Textknoten und spei-
chert auch das resultierende node-Objekt in einer Variablen – text. Jetzt haben wir aber
auch die Situation, dass weder der Elementknoten noch der Textknoten der Webseite
hinzugefügt wurden, noch besteht irgendeine Verbindung zwischen dem Elementknoten
und dem Textknoten. Widmen wir uns dem zweiten Problem zuerst. Offensichtlich ent-
hält der Textknoten den gewünschten Inhalt des Elementknotens. Wie kommt der Inhalt
nun da hinein? Diese Verbindung schaffen wir, indem wir den Textknoten als Kind an
den Elementknoten anhängen. Dazu verwenden wir die node-Methode appendChild (u1.
appendChild(text);). Zum Abschluss müssen wir den nun gefüllten Elementknoten der
254 11 Der DOM – das Mysterium der JavaScript-Welt

Webseite hinzufügen bzw. in den Knotenbaum einfügen. Das machen wir wieder mit
der Methode appendChild über [Link]("body")[0].append-
Child(u1);. Damit ist die dynamisch generierte Seite fertig. Das dynamische Generieren
von Webseiten funktioniert in allen modernen Browsern. Nur ältere Browser machen hier
Ärger. Aber es sollte aufgefallen sein, dass das Verfahren recht aufwendig ist. Mit in-
nerHTML können Sie die Schritte erheblich vereinfachen.

11.7 Das node-Objekt

Kommen wir nun zur Klärung der Hintergründe, die eben schon beim dynamischen Er-
zeugen einer Webseite relevant waren, wobei diese noch in einem größeren Umfeld ge-
sehen werden sollten. Wenn Sie im Browser die Webseite als ein strukturiertes Dokument
betrachten, steht Ihnen mit dem node-Objekt ein Objekt zur Verfügung, um über dessen
Member auf den Elementen im Baum zu navigieren und sogar Teile einer Webseite zu
ergänzen, wie wir es gerade gesehen haben. Um so etwas aber machen zu können, be-
nötigen Sie einen Knoten, der bereits angesprochen und explizit verwendet wurde.

11.7.1 Knotenarten

Um effektiv mit einem node-Objekt und auch den Zugriffstechniken auf DOM-Elemente
arbeiten zu können, muss man sich Gedanken um die verschiedenen Arten von Knoten
machen, die in einem Dokument vorkommen können. Denn diese werden im DOM-Kon-
zept hinsichtlich der verfügbaren Eigenschaften deutlich unterschieden. Wir wissen bereits,
dass es etwa Dokumentknoten gibt, die die gesamte Baumstruktur darstellen, Dokument-
fragmentknoten für nur einen Teil der Baumstruktur, Elementknoten als Gegenstück von
einem HTML-Element, Textknoten mit dem Textinhalt eines Elements oder Attributs und
nicht zuletzt Attributknoten, die einem Attribut eines HTML-Elements entsprechen. Uns
sind ja im letzten Beispiel diese unterschiedlichen Knotenarten explizit begegnet.
Der Umgang mit diesem Knotenkonzept ist nun leider etwas sperrig, denn Textknoten
und Attributknoten sind definitiv nicht ganz konsistent in die Konzeption integriert. Für
viele Anwender erscheint es zum Beispiel etwas seltsam, dass man den Textinhalt eines
Elements nicht direkt über eine Eigenschaft von einem Elementknoten erhält, sondern
nur über einen als Kindelement untergeordneten Textknoten drankommt. Und Attribut-
knoten sind insofern eigenartig, da sie nicht direkt als Knoten in der Baumstruktur vor-
kommen, die vor allem durch Elementknoten gebildet wird. Attributknoten sind dem-
entsprechend keine Kinder von Elementknoten, sondern werden als Eigenschaften von
ihnen im DOM-Konzept geführt. Diese Inkonsistenz gefällt vielen Anwendern des DOM
zwar nicht, aber letztendlich wollen und müssen wir mit node-Objekten arbeiten und
wenn man sich diese beiden Besonderheiten merkt, ist das auch gar nicht so schwer.
11.7 Das node-Objekt 255

[Link] Knotentypen
Knoten werden im DOM-Konzept über Nummern gekennzeichnet. Hier sehen Sie die
verfügbaren Knotentypen (Tab. 11.10):

 In den Entwicklertools moderner Browser können Sie im Register DOM diese


Nummern für die Knotentypen auch sehen, wenn Sie ein Element in einer
Webseite analysieren.

In der Praxis verwendet man in JavaScript beim Zugriff auf eine Webseite meist nur die
Typen 1 bis 3 und vielleicht noch 8 und 9. Die anderen Knotentypen kommen teilweise
auch nur im Rahmen von XML vor (etwa eine PI).

11.7.2 Eigenschaften eines node-Objekts

Ein Objekt vom Typ node stellt nun die folgenden Eigenschaften bereit, die vielfach an
den Verwandtschaftsbeziehungen zwischen Knoten orientiert sind (Tab. 11.11):

 Die Attribute eines Objekts vom Typ node eröffnen Ihnen viele Möglichkeiten
und sind im Grunde einfach. Aber der Umgang damit ist nicht trivial. Es be-
darf ein abstraktes Vorstellungsvermögen bezüglich des Baums einer Web-
seite. Entwicklertools von Browsern sind hier eine wirklich große Hilfe, um zu-
mindest dieser abstrakten Vorstellung des Baums eine optische Unterstützung
zu geben.

11.7.3 Methoden eines node-Objekts

Kommen wir nun zu den Methoden eines Objekts vom Type node (Tab. 11.12).
Betrachten wir ein Beispiel, um rekursiv alle Knoten im Körper eine Webseite zu
durchlaufen ([Link]).

Tab. 11.10  Knotentypen im DOM


Nummer Knotentyp Nummer Knotentyp
1 Elementknoten 7 Process Instruction (PI)
2 Attributknoten 8 Kommentar
3 Textknoten 9 Dokument
4 CDATA-Bereich 10 Dokumenttyp
5 Entity-Referenz 11 Dokumentfragment
6 Entity 12 Notation
256 11 Der DOM – das Mysterium der JavaScript-Welt

Tab. 11.11  Eigenschaften eines Objekts vom Typ node


Eigenschaft Beschreibung
attributes Repräsentation eines Arrays mit den verfügbaren Attributen eines Elements.
Natürlich stehen damit alle Member zur Verfügung, die auf jedes JavaScript-
Array angewendet werden können, insbesondere die Eigenschaft length für
die Anzahl der Elemente. Wichtiger ist jedoch, dass jedes Element in dem
Array einen Attributknoten darstellt und man damit dessen Eigenschaften an-
sprechen kann.
childNodes Repräsentation eines Arrays mit den verfügbaren Kindknoten eines Elements
und allen Standardeigenschaften und -methoden. Wichtiger ist wieder die Tat-
sache, dass jedes Element in dem Array einen Knoten im Baum darstellt und
man damit dessen Eigenschaften ansprechen kann. Achtung – die tatsächlich
in dem Array verfügbare Anzahl der Kindknoten kann browserabhängig sein!
data Inhalt eines Knotens in Form von Zeichendaten, sofern es sich um einen Text-
knoten handelt.
firstChild Der erste Kindknoten eines vorangestellten Knotens
lastChild Der letzte Kindknoten eines vorangestellten Knotens
nextSibling Der nächste Knoten eines vorangestellten Knotens auf derselben Ebene des
Baums
nodeName Der Name des vorangestellten Knotens
nodeType Der Knotentyp in numerischer Form (Tab. 11.10). Die Zahlenwerte sind vom
W3C standardisiert worden und zum Teil explizit nur in einem XML-Doku-
ment verfügbar. HTML-Dokumente stellen also eine Teilmenge der standardi-
sierten Knotentypen bereit.
nodeValue Wert oder Inhalt eines vorangestellten Knotens. Bei Textknoten ist dies der
Text, bei Attributknoten der zugewiesene Attributwert. Bei Elementknoten
steht in der Eigenschaft der Wert null.
parentNode Der Elternknoten vom aktuell selektierten Knoten aus gesehen.
previousSibling Der vorherige Knoten auf derselben Ebene.


<body><h1>Rekursive Ausgabe der Knoten im Body</h1>
   <img src="images/[Link]" alt="Bild 1" />
   <img src="images/[Link]" alt="Bild 2" />
   <div class="fl"><img src="images/[Link]" alt="Bild 3" />
     <img src="images/[Link]" alt="Bild 4" />
     <div class="fl"><img src="images/[Link]" alt="Bild 5" />
       <img src="images/[Link]" alt="Bild 6" /></div></div>
   <div id="anzeige"></div> <!-- Der Ausgabebereich -->
   <script>
     function liste(knten) {
       var ausgabe;
       ausgabe = '<li>';
11.7 Das node-Objekt 257

Tab. 11.12  Methoden eines Objekts vom Typ node


Methode Beschreibung
appendChild Mit dieser Methode hängen Sie einen vorhandenen Knoten als letztes Kindele-
ment des vorangestellten Knotens an.
appendData Einem Textknoten oder Wert eines Attributknotens am Ende neue Zeichen-
daten hinzufügen. Die bereits vorhandenen Daten bleiben erhalten.
cloneNode Erstellung einer Kopie eines Knotens. Der Rückgabewert ist eine identische
Kopie des Elementknotens des vorangestellten Knotens. Wenn als Parameter
false angegeben wird, wird nur das Element ohne den Inhalt geklont.
deleteData Löschen von Zeichendaten eines Textknotens oder des Werts eines Attribut-
knotens
getAttribute Rückgabe des Werts eines Attributknotens
getAttribute- Rückgabe des Attributknotens selbst
Node
getElementsBy- Zugriff auf Kindelemente eines Knotens über den Elementnamen. Die Me-
TagName thode liefert ein Array mit den Knoten.
hasChildNodes Prüfen auf die Existenz von Kindknoten. Wenn ein Knoten Kindknoten hat,
liefert sie den booleschen Wert true, andernfalls false.
insertBefore Einfügen eines Kindknotens im aktuellen Knoten vor einem anderen Kindkno-
ten. Die Methode erwartet zwei Parameter. Zuerst geben Sie den neu einzu-
fügenden Kindknoten und dann den Knoten an, vor dem der neue Kindknoten
eingefügt werden soll. Achtung: Obwohl der Name der Methode es nicht deut-
lich macht – wenn Sie einen Knoten nach einem bestimmten Kindknoten ein-
fügen wollen, können Sie dazu ebenfalls diese Methode verwenden. Sie geben
dazu als zweiten Parameter den Kindknoten an, der auf den Knoten folgt, hin-
ter dem der neue Knoten eingefügt werden soll.
insertData Mit dieser Methode fügen Sie Zeichendaten in einem Textknoten ein. Dazu
geben Sie als ersten Parameter die Zeichenposition an.
removeAttribute Löschen eines benannten Attributs des aktuellen Knotens
removeAttribu- Löschen des genannten Attributknotens
teNode
removeChild Löschen eines benannten Kindknotens
replaceChild Ersetzen eines Kindknotens durch einen anderen Knoten. Der neue Kindkno-
ten wird als erster Parameter und der zu ersetzende Knoten als zweiter Para-
meter angegeben.
replaceData Ersetzen der Zeichendaten eines Textknotens durch einen anderen Text. Als
Parameter geben Sie die Startposition in der Zeichenkette an, ab der ersetzt
werden soll. Der zweite Parameter gibt die Anzahl der zu ersetzenden Zeichen
und der erste Parameter eine Zeichenkette an, mit der die Zeichenkette im
Textknoten ersetzt werden soll.
(Fortsetzung)
258 11 Der DOM – das Mysterium der JavaScript-Welt

Tab. 11.12 (Fortsetzung)


Methode Beschreibung
setAttribute Über diese Methode können Sie in einem Element einen Attributwert neu zu-
weisen. Ist das Attribut bereits vorhanden, wird der alte Wert durch den neuen
Wert ersetzt. Andernfalls wird das Attribut erzeugt und mit dem Wert belegt.
Als erster Parameter wird der Name des zu erzeugenden oder zu ersetzenden
Attributs übergeben, als zweiter Parameter der gewünschte Wert.
setAttribute- Über diese Methode können Sie in einem Element einen neuen Attributknoten
Node einfügen. Ist der Attributknoten bereits vorhanden, wird der alte Knoten durch
den neuen Knoten ersetzt. Andernfalls wird er neu angelegt.

       switch ([Link]) {
         case 1:
           ausgabe += [Link] + '-Element<br />';
           if ([Link]()) {
             ausgabe += '<span class="mark">' + [Link] +
              ' Knoten</span>';
           }
           break;
         case 3:
           var kntenwert = [Link](
            /</g, '&lt;').replace(/\n/g, '\\n');
           ausgabe += 'Textknoten: ' + kntenwert + '<br />';
           break;
         case 8:
           var kntenwert = [Link](
            /</g, '&lt;').replace(/\n/g, '\\n');
           ausgabe += 'Kommentarknoten: ' + kntenwert + '<br />';
           break;
         default:
           ausgabe += 'Knotentyp ' + [Link] + ', Wert: ' +
             [Link] + '<br />';
       }
       if ([Link]()) {
         ausgabe += '\n<ol>\n';
         for (var i = 0; i < [Link]; i++) {
           kntenneu = [Link][i];
           ausgabe += liste(kntenneu); // Rekursion
         }
         ausgabe += '</ol>\n';
       }
       ausgabe += '</li>\n';
       return ausgabe;
11.7 Das node-Objekt 259

     }
     [Link] = function () {
       var obj = [Link]("body")[0];
       [Link]('anzeige').innerHTML = liste(obj);
     }
   </script></body ></html >

Sie sehen in der Webseite einige verschachtelte Strukturen mit verschiedenen Knoten-
typen (Abb. 11.11). Es gibt Elementknoten, Textknoten (inklusive Zeilenumbrüche) und
Kommentarknoten. Die Elemente werden mit Stylesheets formatiert, was für die Anzahl
der Knoten keine wirkliche Rolle spielt. Dennoch wird sich die Formatierung auswirken,
denn mit der Klasse fl werden Div-Elemente als display:inline-block formatiert und die
Zeilenumbrüche optisch weggenommen. Die Zeilenumbrüche sind jedoch im DOM
immer noch explizit vorhanden und werden bei der Ausgabe der Knoten auch mitgezählt
und angegeben. Die Funktion liste ist rekursiv aufgebaut und liefert einen Rückgabe-
wert mit einer Auflistung der Knoten in dem als Parameter übergebenen DOM-Objekt.
Wir übergeben das Body-Element in dem Beispiel. In der Funktion werden die einzel-
nen Knoten in eine Liste geschrieben. Aufgrund des Knotentyps ([Link]) kön-
nen wir die Art des Knotens unterscheiden. Bei Textknoten wissen wir ja, dass der Inhalt
das erste Kindelement ist bzw. über nodeValue zur Verfügung steht. Das gilt auch für

Abb. 11.11 Ausgabe von Knotentypen


260 11 Der DOM – das Mysterium der JavaScript-Welt

Kommentarknoten. Bei Elementknoten ist die Anzahl der Kindknoten interessant und die
erhalten wir mit childNodes und dessen Eigenschaft length ([Link]).
Nun ist es von entscheidender Bedeutung, ob ein Knoten einen Kindknoten besitzt und
dieser wieder Elementknoten mit Kindelementen, denn dann müssen wir rekursiv in die
Tiefe gehen. Das machen wir in der Schleife, in der wir das Knoten-Array durchlaufen
und jeden aktuellen Knoten der Funktion liste übergeben. Das sorgt für eine rekursive
Abarbeitung des Baums.

11.8 Fenstertechniken

Bis vor wenigen Jahren war das Objekt window, das die oberste Ebene der sichtbaren
Bestandteile eines Browsers (globales Objekt) repräsentiert, eines der wichtigsten Ob-
jekte bei Webanwendungen. Die direkte Arbeit mit den Member von window hat jedoch
in der modernen Webprogrammierung stark abgenommen. DHTML und Ajax haben
viele Techniken überflüssig gemacht, die man lange Zeit mit Member von window um-
gesetzt hat, etwa das Öffnen eines neuen Fensters. Die Möglichkeit, neue Fenster, die
oft als Popups konstruiert wurden, mit dem window-Objekt zu erstellen und zu öffnen,
wurde – gerade auf unseriösen Seiten und von Werbetreibenden – auch massiv miss-
braucht und das hat dazu geführt, dass viele Anwender mit Popup-Blockern arbeiten.
Aber auch Standardeigenschaften vieler Browser unterbinden das Öffnen oder Verändern
von Fenstern mit JavaScript. Dennoch zählt das Objekt immer noch zu den Kernbestand-
teilen des DOM und ist dessen globales Objekt.
Ein Unterobjekt von window mit analogen Member ist frame. Frames und die Fra-
metechnik zur optischen Aufteilung des Browserfensters sind schon seit etwa dem Jahr
2000 veraltet. So hat auch das Objekt heute kaum noch eine Bedeutung. Allerdings muss
man sogenannte IFrames (Inline-Frames) gesondert betrachten. Denn für diese gibt es
auch – und gerade – in der modernen RIA-Programmierung wichtige Anwendungen und
auch die werden als frame-Objekt repräsentiert.
Des Weiteren existieren noch ein paar weitere Unterobjekte von window, die ver-
schiedene Fenstertechniken gestatten. Dazu zählen die Weiterleitung zu anderen Adres-
sen, der Zugriff auf die Historie und Statuszeile des Browsers und der Zugriff auf den
Bildschirm des Besuchers. Da jedes Browserfenster automatisch einem window-Objekt
entspricht, haben Sie in jedem JavaScript auf jeden Fall diese Member für dieses Ob-
jekt zur Verfügung. Schauen wir uns zunächst die Member von einem window-Objekt in
einem Überblick an.

11.8.1 Member des window-Objekts

Die Eigenschaften von window reichen von Statusinformationen, ob ein Fenster mit
einer Referenz wieder geschlossen ist (closed) über den Standardzustand (defaultStatus),
11.8 Fenstertechniken 261

innere und äußere Größenangaben (innerHeight, innerWidth, outerHeight, outerWidth),


Positionsangaben (pageXOffset, pageYOffset), Konfigurationen des Fensters (location-
bar, menubar, personalbar, scrollbars, statusbar, toolbar) zu Referenzen auf konkrete
Fensterinstanzen (self, top). Sie haben aber in moderner Webprogrammierung massiv an
Bedeutung verloren. Auch die Methoden von window sind nur noch begrenzt von Be-
deutung. Nachfolgend finden Sie eine Auswahl von Methoden des window-Objekts, die
man kennen sollte. Komplett veraltete oder proprietäre Methoden wurden weggelassen
(Tab. 11.13).

Einige der Methoden sind äußerst wichtig (setTimeout), andere werden noch gelegentlich ein-
gesetzt (alert, prompt, confirm, open), aber viele Methoden haben wie die Eigenschaften von
window massiv an Bedeutung verloren. Single Page-Applikationen mit DHTML und Ajax machen
diese Techniken seit Jahren obsolet.

11.8.2 Anwendungen mit Fenstern

Wir schauen uns ein paar Member eines Fensters an, die in der Praxis Bedeutung haben.

[Link] Zugriff auf die Historie des Browsers


Ein Browser speichert beim Surfen zahlreiche Informationen über die Aktionen eines
Anwenders, solange der Anwender dies nicht durch Gegenmaßnahmen unterbindet, so
auch die URLs der besuchten Webseiten – Historie oder auch Verlauf genannt. Je nach
Einstellung des Browsers wird eine Liste mit den URLs verwaltet. Über das history-Ob-
jekt – ein direktes Unterobjekt von window – hat man aus JavaScript heraus Zugriff auf
die zuvor geladenen Seiten im Browser. Maßgeblich ist dabei die Liste, wie sie in der
History-Liste des Browsers gespeichert ist. Eine Instanz des Objekts history steht auto-
matisch bereit und sämtliche Member lassen sich wie üblich über die Punktnotation
verwenden. Das Objekt history besitzt die Eigenschaft length, worüber die Anzahl der
Einträge in der aktuellen Liste verfügbar ist. Rein von der Theorie her stellt das history-
Objekt noch die Eigenschaften current (aktuelle Seite in der Historie), next (nächste) und
previous (vorherige) bereit, aber diese werden nicht immer unterstützt. Zudem ist meist
nur ein Zurück in der Historie eine wirklich praxisrelevante Anwendung. Diese gehört
aber – trotz der extrem einfachen Anwendung – zu den nützlichsten und am häufigsten
angewendeten JavaScripts überhaupt. Das Objekt history stellt die Methode back bereit.
Darüber laden Sie die zuletzt besuchte Webseite, wie sie im Browser dokumentiert ist.

Beispiel

<a href="javascript:[Link]()">Zurück</a>

Alternativ rufen Sie mit einem Eventhandler die Methode direkt in JavaScript mit
­[Link]() auf. So kurz und knapp das Beispiel ist, so genial ist dessen Anwendungs-
262 11 Der DOM – das Mysterium der JavaScript-Welt

Tab. 11.13  Methoden von window


Methode Beschreibung
alert Öffnen eines Anzeigefensters mit der in den Klammern spezifizierten Nachricht
(einem String)
back Entspricht weitgehend einem Klick auf die ZURÜCK-Schaltfläche im Browser.
blur Der Aufruf der Methode entzieht einem Fenster den Fokus.
clearInterval Der Aufruf der Methode bricht ein sich permanent wiederholendes Intervall mit
Aktivitäten ab, das mit der Methode setInterval begonnen wurde. Als Parameter
geben Sie die Variable an, in der der Rückgabewert von setInterval (eine Refe-
renz) gespeichert wurde.
clearTimeout Diese Methode macht den Aufruf vonsetTimeout wieder rückgängig, sofern sie
eine Variable mit einer Referenz haben. Diese muss clearTimeout als Parameter
übergeben werden.
close Die Methode schließt ein Fenster. Sie können sowohl von einem anderen Fens-
ter aus ein fremdes Fenster schließen als auch das gerade angezeigte Fenster
selbst. Dies funktioniert aber nur innerhalb vorgegebener Randbedingungen.
Sie müssen eine Variable (sprich einen Namen) zur Verfügung haben, die eine
Objektreferenz des zu schließenden Fensters beinhaltet. Insbesondere ist dies
für das Hauptfenster des Browsers ein Problem. Die Namen self oder window
stehen aber immer für das gerade aktive Fenster. Damit haben Sie schon eine
Lösung, wie Sie den Browser selbst schließen können. Jedes aktive Fenster
(auch das Hauptfenster) lässt sich über die Anweisungen [Link] oder self.
close schließen, wenn sie innerhalb des Fensters ausgelöst wird. Beachten Sie,
dass insbesondere das Schließen des aktuellen Fensters von den Sicherheitsein-
stellungen des Browsers verhindert oder nur nach Nachfrage gestattet werden
kann.
confirm Ein Dialogfenster zum Bestätigen oder Abbrechen einer Aktion. Es gibt zwei
Schaltflächen (OK und ABBRUCH), welche die Rückgabewerte true und false
liefern. Diese können Sie dann auswerten. Die auszugebende Meldung ist ein
String.
focus Der Aufruf der Methode legt den Fokus auf ein Fenster.
forward Entspricht weitgehend einem Klick auf die VORWÄRTS-Schaltfläche im Brow-
ser.
home Laden der Startseite des Browsers, sofern diese das unterstützt.
moveBy Verschieben eines Fensters um die angegebenen Pixel in horizontale oder verti-
kale Richtung.
moveTo Verschieben eines Fensters auf die angegebene X/Y-Position.
open Öffnen eines neuen Fensters oder Tabs des Browsers. Grundsätzlich kann die
Methode ganz ohne Parameter angegeben werden. Dann öffnet sich ein neues
leeres Fenster oder Tab. Wenn Sie als ersten Parameter einen URL angeben,
dann wird diese Datei in das neue Fenster geladen. Als zweiten Parameter kön-
nen Sie noch einen Fensternamen und als dritten Parameter Optionen angeben.
print Der Aufruf der Methode druckt den Inhalt einer Seite aus (analog dem Menü-
befehl zum Drucken).
(Fortsetzung)
11.8 Fenstertechniken 263

Tab. 11.13 (Fortsetzung)


Methode Beschreibung
prompt Dialogfenster mit einer OK- und einer ABBRECHEN-Schaltfläche zu einer Ein-
gabe in einem Feld und Rückgabe des Werts an die aufrufende Funktion, wenn
der Anwender die OK-Schaltfläche betätigt. Klickt er ABBRECHEN an, erhält
man den Wert null. Als ersten Parameter geben Sie das Label des Eingabefelds
und optional als zweiten Parameter einen Vorbelegungswert an.
resizeBy Verändern der Größe eines Fensters um die angegebenen Pixel in horizontale
oder vertikale Richtung.
resizeTo Setzen der Größe eines Fensters auf die angegebenen Pixel in der angegebenen
Breite und Höhe.
scrollBy Über die Methode können Sie den Inhalt eines Fensters um die angegebenen
Pixel in horizontale oder vertikale Richtung scrollen.
scrollTo Über die Methode können Sie den Inhalt eines Fensters auf die angegebene X/Y-
Position scrollen.
setInterval Mit dieser Methode können Sie eine Anweisung in einem festgelegten Intervall
immer wieder ausführen. Der Vorgang lässt sich mit der Methode clearInterval
beenden, wenn Sie den Rückgabewert der Methode in einer Variablen speichern,
die der clearInterval-Methode dann übergeben wird. Als Parameter geben Sie die
zu wiederholende Anweisung und einen Wert für die jeweilige Pause zwischen
den Aufrufen in Millisekunden an.
setTimeout Diese Methode ruft die angegebene Anweisung (1. Parameter – eine Funktions-
referenz) nach der angegebenen Anzahl von Millisekunden auf (2. Parameter).
Der erste Parameter kann auch ein Funktionsaufruf (also mit Klammern) sein,
aber dieser muss dann als String notiert werden. Man sollte auf diese Möglich-
keit verzichten und grundsätzlich nur mit Funktionsreferenzen arbeiten.
Der Rückgabewert der Methode kann einer Variablen zugewiesen werden, die
von clearTimeout zum Widerrufen einer verzögerten Anweisung genutzt werden
kann. Diese Methode ist elementar für die Programmierung von selbst auf-
rufenden Funktionen, wie sie oft bei Animationen eingesetzt werden. Allerdings
muss man beachten, dass mehrere nacheinander notierte setTimeout-Aufrufe alle
ab dem gleichen (!) Zeitpunkt die Verzögerung starten. Mit anderen Worten –
wenn zwei oder mehrere Schritte in einem Skript nacheinander – mit definierter
Verzögerung – ablaufen sollen, muss in den verwendeten setTimeout-Methoden
jeweils eine unterschiedliche Zeitspanne stehen. Bei mehreren verzögerten
Schritten verwendet man aber meist einen rekursiven Aufruf.
stop Der Aufruf der Methode bricht den Ladevorgang einer Seite ab. Dies entspricht
weitgehend einem Klick des Anwenders auf die STOP-Schaltfläche im Browser.

möglichkeit. Wenn ein Besucher auf eine Seite verzweigt und er möchte wieder zu der
zuletzt besuchten Seite zurück, wird ihn ein Hyperlink oder eine Schaltfläche mit der
Methode an Hand der Historie dorthin führen. Die Vorteile gegenüber einem statisch ko-
dierten Hyperlink sind groß – Sie müssen nicht wissen, von wo der Besucher kam. Die
Syntax ist ohne Änderungen (höchstens vielleicht der Text) universell für alle Situatio-
264 11 Der DOM – das Mysterium der JavaScript-Welt

nen einsetzbar, in denen der Anwender wieder zu einer gerade besuchten Seite zurück
möchte. Das kann man in vielen Navigationsfällen hervorragend gebrauchen.
Das history-Objekt stellt auch das Gegenstück zu der Methode back zur Verfügung –
die Methode forward. Die Methode macht einen Rückschritt in der Historie rückgängig.
Sie geht also in der Historie vorwärts, wenn das möglich ist. Ebenso können Sie die
Methode go verwenden. Mit dieser laden Sie eine beliebige Seite der History-Liste. Sie
übergeben der Methode als Parameter die Seitenzahl. Bezugspunkt ist dabei die aktuelle
Seite. Sie können die Anzahl mit Vorzeichen verwenden und damit also sowohl zurück-
gehen als auch vorwärts, wenn Sie vorher zurückgegangen sind.

[Link] Öffnen und Schließen eines Fensters oder Tabs


Grundlegend ist das Öffnen und Schließen eines Fensters oder Tabs mit JavaScript,
wobei Sie natürlich das erste Hauptfenster eines Browsers damit nicht öffnen werden.
Aber Sie können neue Instanzen des Objekts window erzeugen. Dazu verwenden Sie
eine der Methoden des Objekts window – open. Dabei wird das neue Fenster oder der
Tab als Client des aufrufenden Fensters direkt geöffnet. Es gilt folgende Syntaxfür das
Schema zum Öffnen eines neuen Fensters mit JavaScript und open:

Beispiel

[Fenstervariable] = [Link]("[URL]", "[Fenstername]", "[Optionen]") ◄

Alle Parameter sind optional, wie oben in der Tabelle schon beschrieben. Wenn sie aber
verwendet werden, beschreibt die URL eine beliebige Datei, die in dem Fenster oder Tab
angezeigt werden soll. Es kann eine HTML- oder Grafikdatei sein, aber auch jede andere
Ressource, welche der Browser darstellen kann oder die beim Anwender gespeichert
werden soll. Dabei gelten die üblichen Regeln für Verweise. Die Adresse muss in An-
führungszeichen stehen. Ein neues leeres Fenster wird erzeugt, wenn Sie anstelle einer
URL-Adresse einfach "" angeben oder eben den Parameter weglassen.
Der Fenstername ist ein frei wählbarer Name. Bei der Namensvergabe gelten die glei-
chen Regeln wie bei Variablennamen. Sie können den Namen eines Fensters im weiteren
Ablauf des Skripts bei verschiedenen Aktionen und auch über die Eigenschaft name ver-
wenden.
Die Angabe von Optionen ist optional. Entfallen sie, wird beim Öffnen eines Fens-
ters dieses in der Regel in der gleichen Größe wie das aufrufende Fenster oder in einer
scheinbar zufälligen Größe dargestellt werden und die Position (wenn kein Vollbild vor-
liegt) mehr oder weniger zufällig relativ zur Position des Elternfensters gesetzt. Oder
aber die Folgefenster von Browsern, die mehrere Unterfenster (Tabs) verwalten können,
werden einfach übereinander in Form von überlagernden oder parallelen Registern an-
geordnet. Dieses Verhalten lässt sich jedoch individuell anpassen, wozu einige Member
des window-Objekts genutzt werden können. Wenn Sie allerdings Optionen angeben,
muss der gesamte Ausdruck in Anführungszeichen stehen oder aus Strings bestehen, die
11.8 Fenstertechniken 265

mit dem Plus-Operator zu einem String zusammengefügt werden. Die Optionen bilden
also einen einzigen Parameter. Angaben zu einzelnen Optionen werden durch Kommata
getrennt. Beispiel für das Öffnen eines Fensters mit Optionen zur Angabe der Höhe und
Breite eines Fensters:

Beispiel

var f = [Link]("[Link]","meinFenster", "width=200,


height=200");

Die möglichen Optionen von open sollen an dieser Stelle nicht genauer ausgeführt
werden, da einerseits selbsterklärend und andererseits mittlerweile in der Praxis obso-
let. Sinnvoll (wenngleich nicht zwingend) ist es, bei der Erstellung einer Fensterobjekt-
instanz eine Referenz auf dieses Objekt in einer Variablen zu speichern. Sie können dann
an späterer Stelle noch darauf zugreifen (zum Beispiel zum Schließen des Fensters über
die Methode close) oder bei der nachträglichen Veränderung der Größe oder Position
eines Fensters.

[Link] Automatische Weiterleitung von Besuchern


Eine interessante Anwendung der window-Methode open oder alternativ des Unter-
objekts location und dessen Eigenschaft href ist die automatische Weiterleitung von
Besuchern beim Laden einer Webseite. Aus dieser Idee resultieren einige nützliche An-
wendungen:

• Sie können von verschiedenen Domains oder Einstiegsseiten in ein Webprojekt Be-
sucher auf eine zentrale Website führen. Dazu müssen Sie bloß eine entsprechende
[Link]- oder [Link]-Anweisung beim Laden einer Webseite ausführen
(Landing page).
• Einem Projekt können Sie auf der Einstiegsseite einen Test voranstellen, ob bei einem
Browser JavaScript aktiviert ist. Dabei führen Sie mit JavaScript eine Weiterleitung zu
einem anderen URL aus. Funktioniert der, muss auch JavaScript aktiviert sein. Und
zusätzlich gibt es wieder eine solche Weiterleitung für den Fall von deaktiviertem Ja-
vaScript, die den Besucher automatisch zu einem Projekt führt, das ohne JavaScript
auskommt. Wie wir schon gesehen haben, funktioniert eine solche Weiterleitung ohne
JavaScript mit folgendem Meta-Tag (<meta http-equiv="refresh" content="[Zei-
t];URL=[Ziel]">).
• In einer Browserweiche überprüfen Sie den Browser eines Anwenders und leiten dann
zu passenden Seiten weiter. Ebenso könnten Sie die Bildschirmauflösung abfragen.

[Link] Zugriff auf die Adresszeile des Browsers


Sie können aus JavaScript heraus über das location-Objekt auf die Adresszeile des Brow-
sers zugreifen. Bei location handelt es sich um ein direktes Unterobjekt von window.
266 11 Der DOM – das Mysterium der JavaScript-Welt

Es repräsentiert die komplette URL der gerade in einem Browser angezeigten Webseite.
Jede der Eigenschaften des location-Objekts steht für einen anderen Bestandteil des
URL. Wenn man die allgemeine Struktur einer URL zugrunde legt, sieht sie so aus:

Beispiel

protocol://user:passwort@host:port/pathname#hash?search

Jeder der einzelnen Bestandteile der URL – wir vernachlässigen die Zugangsdaten –
steht als Bezeichner einer Eigenschaft des location-Objekts zur Verfügung und kann di-
rekt verwendet werden (Tab. 11.14).
Schauen wir uns einige vollständige Beispiele an, die mit location sinnvolle Aktionen
ausführen.

[Link].1 Ausgabe der Bestandteile


Das erste Beispiel soll bloß die Bestandteile von location ausgeben ([Link]):


  <body><h3>Zugriff auf die Informationen in location</h3>
     <button>Zerlegen von location</button></body>
   <script>
     function locationZerlegen() {
       alert("protocol: " + [Link] + "\nhost: " +
         [Link] + "\nport: " + [Link] + "\npathname: " +
         [Link] + "\nhash: " + [Link] + "\nsearch: " +
         [Link] + "\nhref: " + [Link] + "\nhostname: " +
         [Link]);
     }
     [Link]("button")[0].onclick = locationZerle-
gen;
   </script></html>

Tab. 11.14  Eigenschaften des location-Objekts


Eigenschaft Beschreibung
hash Anker in der Webseite inklusive des Trennzeichens # (nur bei HTTP-URLs)
host Host- und Domainname oder die IP-Adresse
hostname Repräsentation des kontaktierten Host samt Port
href Die vollständige Repräsentation einer URL
pathname Pfad inklusive Datei
port Port
protocol Das einleitende Protokoll inklusive des ersten Doppelpunkts
search Eine Suchabfrage inklusive dem Trennzeichen ? (nur bei HTTP-URLs). Der
Suchstring besteht aus Variablen und Wertepaaren, jedes Paar durch & getrennt.
11.8 Fenstertechniken 267

Mit der Schaltfläche in der Webseite wird die Funktion locationZerlegen aufgerufen.
Darin werden die besprochenen Eigenschaften von location ausgegeben. Beachten Sie,
dass nicht jeder Browser alle Eigenschaften mit Werten belegt und die Art des Aufrufs
von Bedeutung ist (etwa über file oder http). Die search-Eigenschaft können Sie belegen,
wenn Sie etwa an die URL der Datei einfach ?user=meier oder etwas der Art anhängen.

[Link].2 Werte von einer Seite an eine andere Webseite weitergeben


Sobald Sie in einem JavaScript einen Variablenwert dynamisch entgegennehmen (etwa
über eine Benutzereingabe) oder berechnen, ist dieser verloren, wenn Sie ohne spezielle
Vorkehrungen eine neue Seite in den Browser laden.

Dieses Problem lösen sogenannte Single page- Webapplikationen, die (wie der Name schon sagt)
nur aus einer Webseite bestehen und dynamisch alle neuen Daten laden. Die Seite und das Java-
Script bleiben also permanent erhalten.

Wenn Sie den Wert einer Variablen in mehreren Webseiten benötigen, können Sie diesen
auf verschiedene Weise beim Wechsel einer Seite weitergeben:

• Sie können mit einem Cookie arbeiten. So gehen viele Websites vor, aber Cookies
sind oft deaktiviert.
• Sie können mit den neuen Möglichkeiten von HTML5 (Local Data Storage und Ses-
sion Data Storage oder clientseitige Datenbanken) arbeiten.
• Sie weisen den Wert einer Variablen einem Formularfeld (am besten versteckt) zu
und schicken ihn zum Server. Wenn Sie sowieso Daten in einem Formular entgegen-
nehmen, ist das besonders naheliegend. Oder Sie schicken direkt die Variablenwerte
zum Server. Der Server gibt den Wert dann zurück in die neue Seite. Die Lösung er-
fordert aber, dass Sie eben auch auf Serverseite programmieren können.
• Sie hängen den Wert der Variablen samt einem Bezeichner an den URL der neu auf-
gerufenen Seite an. Damit können Sie diese Werte auch in Lesezeichen speichern.

Mit der vierten Variante sind wir genau beim Thema, das wir jetzt angehen. Uns interes-
siert dazu die Eigenschaft search, denn darin steht normalerweise der Inhalt von Such-
abfragen inklusive dem vorangestellten Trennzeichen?. Der Suchstring besteht aus Va-
riablen und Wertepaaren, jedes Paar durch & getrennt. Angenommen, Sie wollen nun die
Variable antwort mit dem Wert 42 an die Datei [Link] übergeben, müssen Sie nur
einen Link (hier relativ) so zusammensetzen:
Listing 1.32 Das Anhängen einer Variablen an den URL

Beispiel

[Link]?antwort=42
268 11 Der DOM – das Mysterium der JavaScript-Welt

Dieses Zusammensetzen des URL können Sie natürlich von Hand vornehmen. Dabei
müssen Sie jedoch beachten, dass Sie Sonderzeichen maskieren, denn diese sollten nicht
direkt in einem Pseudo-URL verwendet werden. Dazu gibt es die Standardfunktionen
escape, encodeURI oder am besten encodeURIComponent.
Aber wenn Sie mit einem kleinen Trick arbeiten, brauchen Sie sich die Arbeit des
Maskierens und des manuellen Zusammensetzens des Pseudo-URL gar nicht zu machen.
Verwenden Sie einfach versteckte Formularfelder, die mit den zu übergebenden Wer-
ten gefüllt werden, und rufen Sie eine Folgeseite mit der submit-Methode und der Ver-
sandmethode get sowie der Angabe der Zieldatei im action-Parameter auf. Dann wird
der Browser automatisch die übergebenen Daten maskieren. Betrachten wir eine Seite, in
der Variablen mit gewissen Werten versehen werden, die dann an eine neue Seite weiter-
gereicht werden sollen ([Link]):


    <script>
       var a = [Link]();
       var b = "ß%$§&";
       function neueSeite() {
         [Link] = a;
         [Link] = b;
         [Link]();
       }
     </script></head>
   <body><h3>Daten an eine neue Seite weitergeben</h3>
     <form name="f1" action="[Link]">
       <input type="hidden" name="a"><input type="hidden" name="b"></form>
     <a href="javascript:neueSeite()">Daten an eine neue Seite</a>
   </body></html>

In dem Skript werden zwei Variablen mit Werten belegt. Die Variable b bekommt be-
wusst Sonderzeichen zugewiesen, um den Effekt der Maskierung zu zeigen. In dem
Formular finden Sie ausschließlich zwei versteckte Felder. Der Anwender bekommt
also von dem gesamten Formular nichts mit und sieht nur einen gewöhnlichen Hyper-
link. Beachten Sie, dass die Namen der Formularfelder wie die Bezeichner der Variab-
len gewählt wurden. Damit übergeben wir bereits mit den Namen der Formularfelder
die Variablenbezeichner. Das ist nicht zwingend, aber sinnvoll. Im Kopf des Formulars
sehen Sie den Namen der Datei, an die die Daten beim Verschicken des Formulars ge-
sendet werden (action="[Link]"). Außerhalb von dem Formular sehen eine In-
line-Referenz zum Aufruf der Funktion neueSeite. In dieser Funktion werden den ver-
steckten Feldern die Werte der Variablen zugewiesen. Mit [Link]();
werden die Daten des Formulars dann abgeschickt und damit die über action angegebene
Seite in den Browser geladen. Schauen wir uns diese Seite an, in der die Daten wieder
ausgelesen werden können ([Link]):
11.8 Fenstertechniken 269


       <script>
         function werte() {
         retur = new Array();
           [Link]("Das steht in der search-Eigenschaft: " +
         [Link] + "<br />");
         splt1 = [Link](1);
         [Link]("Beseitigen des ?: " + splt1 + "<br />");
         splt2 = [Link]("&");
         for ( i = 0; i < [Link]; i++) {
           [Link]("Das steht in splt2[" + i + "]: " +
             splt2[i] + "<br />");
           splt3 = splt2[i].split("=");
           [Link]("Das steht in splt3[0]: " + splt3[0] + "<br />");
           [Link]("Das steht in splt3[1]: " + splt3[1] + "<br />");
           [Link]("Das ergibt decodeURIComponent(splt3[1]): "
             + decodeURIComponent(splt3[1]) + "<br />");
           retur[2 * i] = splt3[0];
           retur[2 * i + 1] = decodeURIComponent(splt3[1]);
         }
         [Link](
           "<hr><h4>Resultate für die einzelnen Wertepaare</h4>");
         return retur;
       }
     </script></head>
   <body><h3>Daten von einer Seite übernehmen</h3>
     <h4>Zerlegung Schritt für Schritt</h4>
     <script>
       wertepaar = werte();
       for ( i = 0; i < [Link]; i++) {
         if (i % 2 == 0) [Link]("Variablenname: ");
         else [Link](" - Wert: ");
         [Link](wertepaar[i]);
         if (i % 2 != 0) [Link]("<br />");
       }
     </script></body></html>

Die zentrale Funktionalität, um die Werte der Variablen wieder aus der URL auszulesen,
steckt in der Funktion werte. Die Ausgaben in der Funktion werden für die praktische
Anwendung nicht von Interesse sein. Sie sollen nur die schrittweise Extrahierung ver-
deutlichen. Im Grunde ist nur der Rückgabewert der Funktion interessant. Dazu wird die
Variable retur als Array eingeführt (retur = new Array();). Darin wird der Rückgabe-
wert der Funktion gespeichert. Zunächst zeigen wir an, was beim Aufruf der Seite in lo-
[Link] steht. Danach wird das führende Fragezeichen beseitigt und das Ergebnis
in der Variablen splt1 zwischengespeichert (splt1 =[Link](1);). Die
270 11 Der DOM – das Mysterium der JavaScript-Welt

folgende Zeile zeigt das Resultat. Das verbleibende Resultat wird am kaufmännischen
Und in ein Array mit Namen splt2 aufgespalten (splt2 = [Link]("&");). In der folgen-
den Schleife werden die Datenfeldelemente von splt2 ausgegeben und dann bei jedem
Durchlauf noch einmal splt (am Gleichheitszeichen – splt3 = splt2[i].split("=");). Die
verbleibenden Datenfeldelemente enthalten immer abwechselnd den Variablennamen
und dann den zugehörigen Wert.
In dem Beispiel wenden wir auch die Funktion decodeURIComponent an, um den
Wert einer Variablen wieder zu dekodieren. Die wichtigsten Stellen sind retur[2*i] =
splt3[0]; und retur[2*i + 1] = decodeURIComponent(splt3[1]);, womit das Array auf-
gebaut wird, das nach Abarbeiten der Schleife mit return retur; zurückgegeben wird. In
dem zweiten Skript-Container wird die Funktion werte aufgerufen und der Rückgabe-
wert der Variablen wertepaar zugewiesen (wertepaar = werte();). Damit ist wertpaar
ein Array, das danach mit einer Schleife durchlaufen wird (for(i = 0; i < wertepaar.
length; i++){). Darin werden zwar nur der jeweilige Variablenname und der zugehörige
Wert ausgegeben, aber Sie erkennen, wie Sie an die Variablenbezeichner und die jeweili-
gen Werte durch Aufruf der Funktion werte kommen.

 Wenn Sie in die URL einen JSON-String schreiben, können Sie sich die Sache
vereinfachen.

 Wenn der Server das Fragezeichen nicht als Trennung des Query ansieht,
funktioniert das Beispiel nicht. Dann findet der Server die aufgerufene Datei
nicht, denn der Query wird noch als Teil des Dateinamens gesehen. Das ist bei
einigen Serverkonfigurationen, die aber in der Praxis eher selten vorkommen.

[Link].3 Eine Webseite regelmäßig neu laden


Das location-Objekt verfügt über eine Methode mit Namen reload. Mit deren Aufruf er-
zwingen Sie ein Neuladen des aktuell im Fenster angezeigten Dokuments. Nun ist der
praktische Nutzwert ein bisschen beschränkt. Warum sollte der Anwender nicht den AK-
TUALISIERUNG-Button des Browsers verwenden? Zudem macht Ajax das Neuladen
oft überflüssig. Eine zumindest früher nützliche Anwendung der Methode in Verbindung
mit setTimeout ist, eine Webseite in festen Intervallen automatisch zu aktualisieren, ob-
wohl der Anwender im Browser die Seite nicht manuell neu geladen hat. Das würde so
aussehen:

Beispiel

setTimeout([Link], 2000); ◄
11.8 Fenstertechniken 271

11.8.3 Frames, IFrames und das Objektfeld frames

Frames stehen für die Aufteilung des Anzeigebereichs eines Browsers in einzelne Seg-
mente, die unabhängig mit Inhalt gefüllt werden können. Frames zählen jedoch schon
seit zig Jahren zu den veralteten Webtechniken. Aber das bedeutet nicht, dass es keine
Webseiten mit Frames mehr gibt und sie nicht in vereinzelten Situationen auch noch
in neuen Webpräsenzen sinnvoll sein können. Insbesondere sogenannte Inline-Frames
oder kurz IFrames erfreuen sich großer Beliebtheit und dies, obwohl man sie kaum
noch sieht! Das scheint natürlich ein Widerspruch zu sein. Aber das scheint nur so!
Denn die Inline-Frames werden nicht mehr zur Anzeige von Inhalten, sondern zur un-
sichtbaren Zwischenspeicherung von Inhalten verwendet. Viele Frameworks und APIs
von Google arbeiten mit solchen (unsichtbaren) Inline-Frames, was Sie mit einer Ana-
lyse des Quellcodes und der Suche nach dem Token frames schnell sehen können. Aber
auch viele der anderen modernen JavaScript-Frameworks nutzen diese Möglichkeit, um
Daten zwischenzuspeichern. Es scheint derzeit definitiv die beste Möglichkeit zu sein,
um Daten in gewissen Situationen im Client temporär vorzuhalten. Selbstverständlich
können Sie mit JavaScript auf Frames zugreifen und diese beeinflussen. Dazu steht Ihnen
das Objekt frame – ein Unterobjekt von window – zur Verfügung. Darüber haben Sie
vollen Zugriff auf Frames (unterteilte Fensterstrukturen), also auf ein Frameset und seine
Frame-Fenster, die in einer HTML-Datei definiert sind. Ein frame-Objekt wird auto-
matisch erzeugt, wenn der Browser ein Frameset oder ein IFrame in einer HTML-Datei
vorfindet. Das frame-Objekt steht dann in der Datei zur Verfügung, in der das Frameset
oder IFrame definiert wird, sowie in allen Dateien, die in einem Frame-Fenster des Fra-
mesets angezeigt werden. Der Zugriff auf frame-Objekte erfolgt über die üblichen Wege
wie Objektfelder, eine Id, einen Namen oder den Tag-Namen. In dem Objektfeld frames
werden die einzelnen frame-Objekte abgelegt. Jedes Frame in einer Frameset-Webseite
wird also in einem Array-Element gespeichert. Da das Objektfeld frames window unter-
geordnet ist, kann ein Frame samt seiner Member über die Punktnotation angesprochen
werden:

Beispiel

[Fensterreferenz].[frame].[Eigenschaft/Methode()] ◄

Frame-Objekte werden in JavaScript nur als (besondere) window-Objekte gesehen und


besitzen dementsprechend alle Methoden und Eigenschaften eines solchen. Aus dem
Kontext heraus gibt es natürlich noch die Eigenschaft length für das gesamte Frameset.
Schauen wir uns einige praktische Anwendungen an, in denen mit JavaScript auf Frames
zugegriffen wird. Dabei werden die Anwendungen mit „echten“ Frames und der paralle-
len Darstellung von mehreren Webseiten unter einer gemeinsamen Frame-Struktur nicht
mehr gezeigt. Das ist wirklich out. Wir wollen uns auf die Datenspeicherung in IFrames
konzentrieren.
272 11 Der DOM – das Mysterium der JavaScript-Welt

[Link] Daten aus IFrames laden – ein unsichtbarer Datenspeicher


Wie erwähnt ist die aktuell wichtigste und interessanteste Anwendung von Frames der
„Missbrauch“ als unsichtbarer Datenspeicher bzw. Cache. Trotz der neuen Speicherungs-
möglichkeiten in HTML5 ist diese Art des Datenspeichers eine flexibele und mächtige
Technik. Und Sie können hier sowohl rein lokal als auch mit Serverkontakt arbeiten. In
einem Frame bzw. in der Praxis fast immer in einem IFrame werden Daten geladen, die
erst einmal nicht direkt in der Webseite angezeigt werden sollen. Sie werden in gewisser
Weise dort geparkt, bis man sie ganz oder teilweise aus dem Datenspeicher holt und in
der Webseite anzeigt oder verwendet. Und dieser Zugriff auf ein IFrame funktioniert
natürlich mit JavaScript. Welche Art von Daten Sie in einem IFrame so bereitstellen, ist
nicht beschränkt. Das können Multimediadateien wie Musik, Videos oder Bilder sein,
wenn diese als Ressourcen in eine Webseite eingebunden sind (diese werden quasi pro-
phylaktisch geladen, um bei einem Zugriff schneller verfügbar zu sein), aber auch reine
Texte bis hin zu strukturierten JSON- oder XML-Daten. Gerade das Vorhalten von gro-
ßen Binärdateien auf diese Weise ist sehr sinnvoll. Dem Besucher wird schnell ein voll-
ständiger Startzustand einer Webseite angezeigt und während er sich dort orientiert, wer-
den für ihn unbemerkt weiter Daten geladen. Beispiel ([Link]):


     <link rel="stylesheet" type="text/css" href="lib/css/[Link]" />
     <script src="lib/js/[Link]"></script></head>
  <body><h1>Unsichtbare IFrames als Datenspeicher</h1>
     <iframe src="frame1_1.html"></iframe>
     <iframe src="frame1_2.html"></iframe>
     <button>Bild 1</button><button>Bild 2</button>
     <button>Bild 3</button><button>Bild 4</button><button>Bild 5</button>
     <button>Text 1</button><button>Text 2</button><hr/>
     <div id="td1"><img id="b1" /></div> <div id="td2"></div>
  </body></html>

In dem Beispiel arbeiten wir mit zwei IFrames, in denen zwei HTML-Dateien geladen
werden. Diese IFrames werden mit CSS unsichtbar gemacht (Abb. 11.12), was in der
Datei [Link] so umgesetzt wird:


iframe { display:none; }

In den Entwicklertools eines Browsers kann man aber deutlich erkennen, dass die IFra-
mes im DOM vorhanden und welche Dateien darin geladen sind (Abb. 11.12). Dieses
Mal arbeiten wir mit einer externen JavaScript-Datei [Link] und dort erfolgt der Zu-
griff auf den Inhalt von IFrames:
11.8 Fenstertechniken 273

function bild(i) {
   [Link]("b1").src =
     [Link][0].[Link][i].src;
}
function text(i) {
   [Link]("td2").innerHTML = [Link][1].document.
     getElementsByTagName("h1")[i].innerHTML;
}
function init() {
   for (let i=0; i < [Link]("button").length;
i++) {
     [Link]("button")[i].onclick = function () {
       [Link](i);
       if (i < 5) bild(i);
       else text(i - 5);
     }
   }
}
[Link] = init;

Für die sieben Schaltflächen in der Webseite werden onclick-Eventhandler registriert. Bei
den ersten fünf wird die Funktion bild mit einem spezifischen numerischen Parameter
aufgerufen. Die letzten beiden Schaltflächen rufen die Funktion text ebenfalls mit einem
spezifischen Parameter auf.

Sie erkennen, dass sich Codestrukturen zum Registrieren eines Eventhandlers mehrfach wieder-
holen und sowohl die Bezeichner mit der Nummerierung als auch die Indizes der Arrays auf-
einander abgestimmt sind. Da drängt sich eine Schleife auf, wie es hier gemacht wird. Doch Vor-
sicht, wenn Sie hier mit Schleifen arbeiten wollen. Das sind Funktionsreferenzen und dement-
sprechend bekommen Sie Probleme, wenn Sie innerhalb der anonymen Funktionen nicht sauber
auf die Zählvariable zugreifen. Die referenzierten Funktionen werden vom Interpreter zum Zeit-
punkt der Referenzierung nicht (!) verarbeitet und dementsprechend steht der Zähler der Schleife
dort bei einer unsauberen Programmierung nicht zur Verfügung. Wenn Sie bei der Deklaration der
Schleifenvariablen var statt let verwenden, werden Sie dieses Problem bekommen. Dann wird der
Interpreter erst beim tatsächlichen Aufruf der Funktion den Funktionscode verarbeiten und alle (!)
Funktionsaufrufe von bild und text werden mit dem Wert 7 ausgeführt, was natürlich fehlerhaft ist.

In der Funktion bild sehen Sie, dass der Eigenschaft src des Bildobjekts mit der Id b1 ein
Wert zugewiesen wird. Das Bildobjekt wird von dem DOM über das <img>-Tag in der
HTML-Datei bereitgestellt. Der zugewiesene Wert kommt aber aus dem ersten IFrame.
Mit [Link][0] sprechen wir ihn an und erhalten damit ein DOM-Fensterobjekt.
Und dann stellt document wie gehabt die Objektrepräsentation der Webseite darin dar.
Schauen wir uns den Aufbau der in dem IFrame geladenen Datei frame1_1.html an, denn
dieser Aufbau ist massiv für die Art unseres Zugriffs von Bedeutung:
274 11 Der DOM – das Mysterium der JavaScript-Welt

Abb. 11.12 Die IFrames sind unsichtbar, aber in den Entwicklertools als geladene Struktur zu
sehen


  <body><img src="images/[Link]" /><img src="images/[Link]" />
     <img src="images/[Link]" /><img src="images/[Link]" />
     <img src="images/[Link]" /></body></html>
11.8 Fenstertechniken 275

Sie erkennen, dass darin fünf Grafiken geladen werden. Und deshalb kommen wir
über das Objektfeld images in der Funktion bild an die einzelnen Grafiken in der Web-
seite, die im IFrame geladen ist, und können diese dann in der eigentlichen Webseite an-
zeigen (Abb. 11.13).
Die Funktion text hat einen analogen Aufbau. Nur wird hier der Textinhalt einer
Überschrift der Ordnung h1 aus einer Datei im zweiten IFrame ausgelesen und in eine
Tabellenzelle geschrieben. Der Aufbau der Datei, die im zweiten IFrame geladen wird,
sieht so aus (frame2_2.html):

Abb. 11.13 Ein Bild wurde aus dem IFrame-Datenspeicher in die Webseite geholt
276 11 Der DOM – das Mysterium der JavaScript-Welt


  <body><h1>[Link]</h1><h1>[Link]</h1></body></html>

Die Wahl der Überschriften vom Typ h1 zur Strukturierung des Inhalts ist willkürlich.
Sie können beliebige Strukturen hier nehmen. Etwa auch XML. Sie sehen ja, dass in
dem Beispiel bewusst der Inhalt einer Überschrift einer Tabellenzelle zugewiesen wird.
Sie müssen bloß eindeutig die gewünschte Textinformation in der IFrame-Datei adressie-
ren können.

Diejenigen Leser, die sich bereits mit Ajax auskennen, werden hier gewisse Ähnlichkeiten zu
den Möglichkeiten sehen, die Ihnen Ajax bietet. Zumindest dann, wenn man über die IFrames
im Hintergrund Daten vom Server holt. In der Tat hat man vor Ajax sehr oft IFrames (und auch
Frames) eingesetzt, wenn man eine Webseite nicht vollständig austauschen wollte. Aber eben auch
heute wird diese Technik noch eingesetzt, wenngleich meist in etwas abgewandelter Form (Vor-
halten von binären Ressourcen wie Bildern oder allgemein Multimediadateien, um darüber bei Be-
darf eine schnellere Anzeige zu gewährleisten), aber manchmal auch wirklich als explizite Alter-
native zu Ajax.

[Link] Daten in einem IFrame speichern


Sehen wir uns nun an, wie Sie aus JavaScript in einem IFrame Daten speichern können,
zum Beispiel ein Protokoll von Aktionen, die ein Anwender in einer Webseite durch-
geführt hat. Wir wollen der Einfachheit halber nur die Klicks auf drei Schaltflächen be-
obachten, aber das Verfahren können Sie universell verwenden ([Link]). Wir ver-
wenden hier auch wieder ein internes JavaScript.


<body><h1>IFrames als Datenspeicher für Benutzeraktionen</h1>
   <iframe></iframe><hr /><button>1</button>
   <button>2</button><button>3</button><div id="t1"></div></body>
<script>
   function text(i) {
     var text;
     switch (i) {
       case 0: text = [Link](); break;
       case 1: text = new Date().toLocaleString(); break;
       case 2: text = new Date().getSeconds();
     }
     [Link]("t1").innerHTML = text;
     var u6 = [Link]("h6");
     var textknten = [Link](text);
     [Link](textknten);
     [Link][0].document.
       getElementsByTagName("body")[0].appendChild(u6);
   }
11.9 Browserauswertung und das Objekt navigator 277

   for (let i=0; i < [Link]("button").length;


i++) {
     [Link]("button")[i].onclick = function () {
       text(i);
     }
   }
</script></html>

Sie sehen wieder einen versteckten IFrame, dessen Inhalt man in den Entwicklertools
des Browsers verfolgen kann (Abb. 11.14). Bei einem Klick auf eine der Schaltflächen
in der Webseite wird die Funktion text aufgerufen. Dort wird aufgrund des übergebenen
Parameters mit switch-case entschieden, welcher Text in der Webseite angezeigt wird.
Aber das ist der für uns hier nicht wichtige Part. Die entscheidende Stelle ist window.
frames[0].[Link]("body")[0]. appendChild(u6);. Wir hän-
gen hier einen Textknoten an den Körper der Datei, die in dem IFrame geladen ist. Als
Inhalt des Textknotens wählen wir hier den angezeigten Text in der Webseite, der in
eine h6-Struktur eingefügt wurde. Aber Sie können natürlich beliebige Informationen
so speichern. Entscheidend ist, dass die Datei im IFrame als Protokolldatei verwendet
wird. Natürlich können Sie hier auch leicht JSON- oder XML-Daten verwenden. Die ge-
speicherten Daten können Sie wieder analog des vorherigen Beispiels aus einer Webseite
heraus auslesen, wenn das sinnvoll ist. Und natürlich können Sie die Daten auch zum
Server schicken, wobei da die lokale Zwischenspeicherung in einem IFrame eigentlich
der falsche Weg ist.

11.9 Browserauswertung und das Objekt navigator

In der Praxis ist die genaue Kenntnis über den Browser und das Betriebssystem des Be-
suchers in vielen Fällen von Bedeutung. Beim Datenaustausch zwischen Webserver und
Browser werden mit dem HTTP-Protokoll viele Hintergrundinformationen ausgetauscht
und unter anderem eben auch zahlreiche Details über den Browser und die Plattform des
Besuchers. Diese Informationen können dann auf dem Webserver verwertet werden. Aber
bereits aufseiten des Clients kann man ebenfalls mit diesen Informationen arbeiten. Denn
über das DOM-Objekt navigator können Sie aus JavaScript heraus auf zahlreiche Informa-
tionen über das Clientprogramm und die Plattform eines Besuchers zugreifen. Wohlgemerkt
sind das jedoch nur lesende Zugriffe, im Gegenteil zu den meisten Eigenschaften von
Fensterobjekten und dessen Bestandteilen, die auch per JavaScript gesetzt werden können.

 Beachten Sie, dass das Objekt navigator im DOM nicht window untergeordnet ist.
Die Informationen zum Browser und zur Plattform bilden einen eigenständigen
DOM-Zweig.
278 11 Der DOM – das Mysterium der JavaScript-Welt

Abb. 11.14 Alle Klicks wurden im IFrame protokolliert

11.9.1 Auflistung aller Eigenschaften von navigator

Mit einem einfachen Skript können wir uns alle Member von navigator ausgeben lassen.
Dazu verwenden wir einfach eine for…in-Schleife und die Tatsache, dass alle Objekte in
JavaScript als Arrays bzw. Hash-Listen verfügbar sind. Das gilt damit natürlich auch für
sämtliche DOM-Objekte und diese Möglichkeit wollen wir jetzt nutzen ([Link]).


  <body><h1>Das navigator-Objekt</h1>
     <script>
       for (let i in navigator) {
         [Link](i + ": " + navigator[i] + "<br />");
       };
     </script>
   </body></html>
11.9 Browserauswertung und das Objekt navigator 279

Abb. 11.15 Über navigator kommen Sie an alle relevanten Daten zu einem Browsers

Sie sehen in der Webseite nach dem Laden des Beispiels alle verfügbaren Member
eines Browsers (Abb. 11.15), wobei Sie bei manchen Eigenschaften wieder selbst Ob-
jekte vorliegen haben (etwa geolocation), die Sie aber ebenfalls auf diese Weise durch-
laufen können (gegebenenfalls rekursiv). Beachten Sie jedoch, dass sich diese Angaben
in verschiedenen Browsern erheblich unterscheiden. Das Objekt navigator stellt in den
meisten Browsern nur die Methode javaEnabled bereit, mit der Sie testen können, ob
280 11 Der DOM – das Mysterium der JavaScript-Welt

Tab. 11.15  Eigenschaften von navigator


Eigenschaft Beschreibung
appCodeName Der Codename eines Browsers
appName
appVersion Die Version und die Betriebssystemplattform des Browsers
cookieEnabled Die Information, ob der Nutzer das Setzen von Cookies erlaubt
language Die Sprache des Browsers in international üblichen Abkürzungen (beispiels-
weise „de“ für deutsch). Der konkrete Wert ist browserabhängig.
mimeTypes Ein Array, welche Dateitypen der Browser des Anwenders kennt und ob ein
Plugin zum Anzeigen oder Abspielen eines Dateityps installiert ist
platform Das Betriebssystem des Anwenders
plugins Ein Array mit allen Plugins, die ein Browser installiert hat
userAgent Detaillierte Daten des Browsers in einer browserabhängigen Form. Die Daten
werden genauso im http-Protokoll an den aufgerufenen Server übermittelt.

der Browser eines Anwenders Java-fähig ist (true) oder nicht (false) und die mittlerweile
kaum noch interessant ist. Eventuell weiter verfügbare Methoden sind entweder depre-
cated oder browserabhängig. Wichtiger als die Methoden sind die Eigenschaften von na-
vigator. Hier gibt es auch verschiedene browserabhängige Eigenschaften, aber die nach-
folgend notierten sind fast immer verfügbar (Tab. 11.15):
Ausnahmebehandlung
12

Mit Eventhandling sind wir ja schon an verschiedenen Stellen in Berührung ge-


kommen. Exceptionhandling oder auf deutsch Ausnahmebehandlung ist Event-
handling ähnlich, nur radikaler. Darum kümmern wir uns hier. Es geht wieder um
grundsätzliche Reaktion auf bestimmte Situationen im Rahmen eines JavaScripts.
Aber dieses Mal kann das Skript nicht ohne gewisse Maßnahmen folgerichtig
weiterlaufen und wird ohne diese Maßnahmen beendet.

12.1 Was ist Ausnahmebehandlung?

Obwohl man mit Ausnahmebehandlung oft auf das Auftreten von Fehlersituationen (oder
genauer – nichtplanbaren Situationen) in einem Skript reagieren möchte, ist die grund-
sätzliche Idee des Konzepts nicht darauf beschränkt und wie gesagt eng verwandt mit
der Reaktion auf Ereignisse. Auch beim Auftreten einer Ausnahme wird eine ganz be-
stimmte Reaktion durchgeführt werden (müssen). Nur ist das Ignorieren einer Ausnahme
im Gegensatz zum Vernachlässigen eines Ereignisses nicht ohne Konsequenzen möglich.
Auch werden Ausnahmen viel, viel seltener auftreten als Ereignisse, was ob der zwin-
genden Reaktion auch unabdingbar ist.

12.1.1 Eine Ausnahme

Besprechen wir, was eine Ausnahme im Verständnis von JavaScript darstellt. Die prin-
zipielle Idee baut massiv auf der OOP auf. Auch Ausnahmen sind Mitteilungsobjekte.
Wir haben das Konzept von Ereignisobjekten und deren Verwertung schon gesehen. Was

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 281
Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
282 12 Ausnahmebehandlung

passiert da? Wenn während der Ausführung eines Programms oder Skripts eine „be-
merkenswerte“ Situation eintritt, wird vom ausführenden System automatisch ein Ereig-
nisobjekt (Mitteilungsobjekt) erzeugt. Solch ein Mitteilungsobjekt enthält alle relevanten
Informationen, was für eine Situation gerade aufgetreten ist, wo und warum. Neben den
klassischen Ereignissen wie das Abschicken von Formulardaten, die grundsätzliche Be-
wegung der Maus oder das Drücken einer Taste auf der Tastatur könnten „bemerkens-
werte“ Situationen desgleichen die fehlerhafte Erzeugung eines Objekts, ein mathema-
tischer Fehler bei einer Berechnung oder etwas Ähnliches sein. Für das Entstehen von
solchen Mitteilungsobjekten ist es nun erst einmal egal, um was für einen genauen Typ
es sich handelt. Im Laufzeitsystem von JavaScript wird die Erzeugung von solchen Ob-
jekten vollkommen automatisch geschehen. Dieser Automatismus kann weder vom Pro-
grammierer beim Erstellen des Programms beziehungsweise Skripts noch Anwender
bei der Ausführung beeinflusst bzw. verhindert werden. Es handelt sich wie gesagt um
essenzielle Kernelemente einer bestimmten Programm- bzw. Laufzeitumgebung (gleich-
wertig solcher Verfahren wie der automatischen Typumwandlung und Ähnliches). Aus-
nahmen beziehungsweise Exceptions bezeichnen also Situationen zur Laufzeit eines
Skripts oder Programms, die eine unmittelbare Reaktion und damit die Generierung
von passenden Mitteilungsobjekten erzwingen. Solche Ausnahmen können entweder
durch den JavaScript-Interpreter automatisch ausgeworfen werden, wenn es in einer be-
stimmten Situation notwendig ist, oder aber von dem Programmierer selbst, wenn es er-
forderlich erscheint. Das Resultat ist aber immer gleich – eine Ausnahme tritt auf und
ein entsprechendes Mitteilungsobjekt wird in das Laufzeitsystem hineingefeuert bzw. ge-
worfen.

Der Begriff „Feuern“ ist bei Ausnahmen ungewöhnlich, soll aber die Ähnlichkeit zu einem Ereig-
nisobjekt andeuten. Ein Ereignisobjekt wird abgefeuert (fire an event). Bei einer Ausnahme spricht
man im Allgemeinen vom „Werfen“ (wegen dem Schlüsselwort throw und dem „Auffangen“ einer
Ausnahme in einem catch-Block).

Wesentlicher Unterschied zwischen der Reaktion auf ein Ereignisobjekt und ein Aus-
nahmeobjekt ist, dass die Kenntnisnahme sowie die Reaktion auf ein Ereignis (etwa
Klick mit der Maus in der Webseite) weitgehend „freiwillig“ ist, während die Kenntnis-
nahme und Reaktion auf eine Ausnahme vom Konzept generell erzwungen wird oder es
hat Konsequenzen (das Skript wird beendet).

 Um es noch einmal ausdrücklich zu betonen – die automatische Erzeugung eines


Mitteilungsobjekts ist nicht gleichbedeutend damit, dass in eine bestimmte Re-
aktion darauf erfolgt oder erfolgen muss. An dieser Stelle greift explizit der Pro-
grammierer ein, der eine ganz bestimmte Reaktion für das Auftreten eines jeweili-
gen Mitteilungsobjekts programmiert. Auf ein Eventobjekt muss man nicht reagie-
ren. Wird jedoch auf eine ausgeworfene Ausnahme nicht reagiert, wird das Skript
abgebrochen!
12.1 Was ist Ausnahmebehandlung? 283

12.1.2 Wozu ein Ausnahmekonzept?

Wozu braucht man in JavaScript (sowie in nahezu allen modernen Programmiertechno-


logien) überhaupt ein solches Ausnahmekonzept? Nicht jede Situation im Ablauf eines
Programms beziehungsweise Skripts ist vollkommen planbar. Denken Sie an freie Ein-
gaben in einem Webformular oder den Versuch, auf ein bestimmtes Objekt zuzugreifen
oder es zu erzeugen. Um Fehler in solchen Fällen zu vermeiden, steht Ihnen zwar die
Möglichkeit einer sorgfältig durchdachten und umgesetzten Programmierung mit dem
vorherigen Abfangen potenzieller Fehlersituationen zur Verfügung. Aber unter Um-
ständen kalkulieren Sie nicht alle potenziellen Fehlersituationen ein. Nichtplanbare Situ-
ationen können auch aufgrund anderer Effekte eintreten und dann steht unter Umständen
gar kein plausibles Konzept für den weiteren Programm- bzw. Skriptablauf bereit. Be-
trachten Sie das nachfolgende Skript, das aus Gründen der Vereinfachung hartkodiert
eine Fehlersituation produziert und bewusst keinen strikten Modus verwendet, um dieses
fehlerhafte Verhalten überhaupt zu gewährleisten ([Link]):

[Link](eval("5 * 5"));
[Link](eval("a * b"));
[Link]("Weitere Ausgaben");

Im Skript werden mit der JavaScript-Toplevelfunktion eval Ausdrücke numerisch aus-


gewertet. Die Funktion bekommt als Parameter einen String übergeben und betrachtet
die übergebene Zeichenkette als Zahlen mit zugehörigen Operatoren und berechnet das
Ergebnis. Bei der ersten Evaluierung kommt ein vernünftiges Ergebnis heraus und das
wird im Anzeigebereich des Browsers ausgegeben. In der zweiten Evaluierung hingegen
wird eval Probleme bekommen, denn a * b kann nicht sinnvoll numerisch ausgewertet
werden. Diese Variablen sind vorher im Skript nicht eingeführt worden und daher nicht
mit numerischen Werten versorgt. Das Skript bricht ab (ReferenceError), die zweite Eva-
luierung wird nicht ausgeführt und nachfolgende Zeile gar nicht mehr erreicht.
Solche Probleme in einem Skript müssen aber nicht zwangsläufig zum Abbruch eines
Skripts führen. In vielen Fällen machen geeignete vorgegebene Gegenmaßnahmen viel
Sinn – mithilfe des Ausnahmekonzepts zur Reaktion auf kritische Situationen. Machen
wir uns die Lage noch einmal deutlich – eine Ausnahme können Sie sich allgemein als
eine Störung des normalen Skript- bzw. Programmablaufs auf Grund einer besonderen
Situation vorstellen. Diese Störung macht zwingend eine isolierte und unverzügliche Be-
handlung notwendig, bevor der normale Skript- bzw. Programmablauf fortgesetzt wer-
den darf. Der normale Ablauf wird also so lange unterbrochen, bis diese Ausnahme be-
handelt wurde. Wird die Ausnahme nicht behandelt, wird das Programm oder Skript wie
gesagt beendet. Genau so etwas sehen wir in unserem ersten Exempel.
Wenn eine Ausnahme erzeugt wird, wird von dem Laufzeitsystem ein Mitteilungs-
objekt generiert, welches genaue Informationen über die aktuelle Störung enthält.
284 12 Ausnahmebehandlung

­ ufgrund dieser Information kann man gezielte Gegenmaßnahmen ergreifen oder aber
A
auch einfach nur auf die pure Existenz des Mitteilungsobjekts eine Standardgegenmaß-
nahme ergreifen. Eine Ausnahme ist aber noch mehr als ein reines Mitteilungsobjekt
mit spezifischen Informationen. Das Auftreten eines Ausnahmeobjekts unterbricht wie
gesagt unmittelbar einen normalen Skriptablauf und veranlasst den ausführenden Inter-
preter, nach einer geeigneten Behandlungsstruktur zu suchen. Dabei entsteht im Skript
ein Laufzeitfehler, der sich relativ standardisiert beschreiben lässt. Die ECMAScript Edi-
tion stellt ein Fehlerobjekt Error bereit und Instanzen von Error-Objekten werden als
Ausnahmen ausgeworfen, wenn ein bekannter Laufzeitfehler auftritt.

 In der ECMAScript Language Specification Edition gibt es neben Error selbst


weitere Ausnahmetypen wie ReferenceError, EvalError, TypeError, SyntaxError,
URIError oder RangeError, die wir in dem Buch an anderer Stelle schon be-
sprochen haben, deren explizite Behandlung jedoch in der Praxis kaum eine
Rolle spielt, weil man die Typen bei der Reaktion kaum unterscheidet. In den
meisten Fällen sind Sie also bei der Behandlung von Standardausnahmen auf
der besseren Seite, wenn Sie nur Error verwenden, sofern Sie überhaupt den
Typ beachten wollen.

12.1.3 Das Auffangen einer Ausnahme

Wenn eine Ausnahme aufgetreten ist, müssen Sie diese wie gesagt zwingend behandeln
bzw. auffangen, sonst wird das Skript abgebrochen. Um nun eine ausgeworfene Aus-
nahme behandeln zu können, umgibt man den Aufruf einer kritischen Funktion, Methode
oder Anweisungsstruktur, die möglicherweise eine Ausnahme auswirft, mit einem um-
gebenden try-catch-Block. Dies sieht schematisch so aus:

Beispiel

try { [kritische Anweisung(en)]}


catch ([Ausdruck]) { [Maßnahmen für den Ausnahmefall]} ◄

• Alle potenziell kritischen Anweisungen stehen bei dieser Konstruktion in dem try-
Zweig. Sie sollten da immer alle Anweisungen notieren, die nur zusammen einen Vor-
gang beschreiben, also nicht nur die kritische Anweisung selbst. In den try-Block ge-
hört ein kompletter Vorgang, der entweder vollständig abgearbeitet werden muss oder
bei dem man eine komplette Gegenreaktion im Fall einer Ausnahme implementieren
muss.
12.1 Was ist Ausnahmebehandlung? 285

• Der optionale Ausdruck in den Klammern von dem catch-Zweig ist ein Bezeichner
für das konkrete Ausnahmeobjekt – eine Variable innerhalb des catch-Blocks.
• In dem catch-Zweig selbst stehen einfach innerhalb geschweifter Klammern die An-
weisungen, die beim Auftreten der Ausnahme durchgeführt werden sollen.

Wenn also eine der Anweisungen innerhalb des try-Blocks eine Ausnahme erzeugt, wird
diese durch die passenden catch-Anweisungen aufgefangen und entsprechend behandelt,
sofern es dafür die passende Behandlung gibt. Werden hingegen alle Anweisungen in
dem try-Block ohne Probleme abgearbeitet, wird der catch-Block übersprungen. Das
Auftreten einer Ausnahme innerhalb eines try-catch-Konstrukts führt nun auch nicht
mehr dazu, dass das Skript beendet wird. Es läuft mit den Anweisungen hinter dem try-
catch-Konstrukt weiter, nachdem die Anweisungen im catch-Teil abgearbeitet wurden.
Das Ausnahmeobjekt ist damit auch vom Laufzeitsystem beseitigt. Man sagt, die auf-
getretene Ausnahme wurde konsumiert.

Der Begriff try sagt übrigens bereits sehr treffend, was in diesem Block passiert. Es wird „versucht“,
den Code innerhalb des try-Blocks auszuführen. Wenn ein Problem auftauchen sollte (sprich, es
wird eine Ausnahme ausgeworfen), wird diese sofort entsprechend im passenden catch-Block auf-
gefangen und alle nachfolgenden Schritte in diesem try-Block werden nicht mehr durchgeführt.

Wenn Sie in JavaScript einen try-Block notieren, muss zwingend ein catch-Block fol-
gen. Fehlt dieser, ist die Reaktion der Interpretersysteme jedoch uneinheitlich. Einige
Systeme zeigen eine Fehlermeldung beim Fehlen des catch-Blocks an, andere gar nichts
mehr an. Der try-Block wird einfach ignoriert. Das ist für den Besucher einer Webseite
besser, denn Besucher sind nicht an Fehlermeldungen interessiert, aber schlecht für Pro-
grammierer, denn man erkennt das Problem möglicherweise nicht.

 Beachten Sie, dass JavaScript lose typisiert ist und nur vier Grunddatentypen
kennt. Eine Unterscheidung des Typs des Ausnahmeobjekts über einen spezi-
fischen Referenztyp, wie man es etwa in Java oder C# macht, ist nicht möglich.
Deshalb kann man auch nicht mehrere catch-Blöcke alternativ einem try-Block zu-
ordnen, wie es etwa in Java oder C# üblicherweise gemacht wird, um damit eine
unterschiedliche Reaktion je nach Typ der Ausnahme zu implementieren. Statt-
dessen notieren Sie einfach getrennte try-catch-Strukturen für jeden einzelnen Fall.
Oder Sie verschachteln try-catch-Strukturen, indem Sie in einem catch-Block wie-
der try-catch-Blöcke notieren.

Was Sie nun im catch-Teil konkret tun, ist nicht weiter festgelegt. Sie können jede Ihnen
sinnvoll erscheinende Maßnahme ergreifen, die das Skript folgerichtig weiterlaufen lässt.
Oft ist das eine Fehlermeldung mit anschließendem Rücksprung zu der Situation, die mit
der ausgelösten Ausnahme sinnvoll umgehen lässt, etwa im Fall einer Benutzereingabe
286 12 Ausnahmebehandlung

ein Fokussieren des kritischen Eingabefelds. Oder aber Sie können einen alternativen
Weg zur Durchführung einer Aktion probieren, wenn der erste Weg gescheitert ist. In
letzterem Fall werden Sie oft auch try-catch-Strukturen verschachteln.

 Wenn Sie das Ausnahmebehandlungskonzept in JavaScript einsetzen wollen,


sollten kritische Aktionen also immer innerhalb eines try-Blocks durchgeführt
werden. Aber obwohl in vielen Fällen Ausnahmebehandlung elegant ist, ist
eine konservative Programmierung (etwa mit dem vorangestellten Testen von
potenziell kritischen Situationen, wenn Sie diese erkennen können) in der Pra-
xis bedeutend besser – wenn das möglich ist. Das Erzeugen einer Ausnahme
ist immer ein ziemlich „ruppiger“ Vorgang im smarten Ablauf des Skripts, der
wirklich nur in „Ausnahmefällen“ eintreten sollte. Auch bezüglich der Per-
formance sind das Auftreten und Behandeln einer Ausnahme von Nachteil.

Das nachfolgende Beispiel zeigt den Fall, in dem eine Ausnahme auftritt (hier eine ein-
fache Situation, wo in jedem Fall eine Ausnahme auftritt – das ist in der Praxis natür-
lich nicht sinnvoll) und allgemein abgefangen wird. Es wird wie im Beispiel zuvor
einfach versucht, die Variablen a und b zu multiplizieren, die vorher nicht eingeführt
wurden. Im catch-Teil wird das dabei erzeugte Ausnahmeobjekt aufgefangen. Die
Maßnahmen im catch-Block bestehen nur daraus, die im Ausnahmeobjekt enthaltenen
Standardmeldungen nacheinander anzuzeigen. Die nach dem try-catch-Block stehen-
den Anweisungen werden trotz der aufgetretenen Ausnahme ausgeführt. Das bedeutet,
das Skript wird nicht abgebrochen, sondern mit unkritischen Anweisungen fortgesetzt.
Betrachten wir die Abwandlung unseres letzten Beispiels und fangen wir hier die Aus-
nahme ab ([Link]):

try {
   [Link](eval("5 * 5"));
   [Link](eval("a * b"));
   [Link]("Im try-Block nach Ausnahme")
} catch (e) {
   [Link]("Information in dem Ausnahmeobjekt: ", [Link]());
}
[Link]("Nach dem Auftreten der Ausnahme.");

Die beiden eval-Anweisungen sowie die reine Textausgabe sind nun von einem try-
Block umgeben. Wieder wird die erste Ausgabe problemlos erzeugt. Die folgende An-
weisung löst dann wie im vorherigen Beispiel die Ausnahme aus. Die Anweisung wird
nicht durchgeführt und der try-Block wird unmittelbar verlassen. Die nachfolgende Aus-
gabe wird nicht erreicht. Der Programmfluss verzweigt unmittelbar in den catch-Block.
Dort wird der Inhalt des generierten Ausnahmeobjekts angezeigt. Wenn der catch-Block
abgearbeitet wurde, läuft das Skript mit der nächsten Anweisung hinter dem try-catch-
Konstrukt weiter. Das ist die Ausgabe:
12.2 Selbstdefinierte Ausnahmen erzeugen und verwenden 287

25
Information in dem Ausnahmeobjekt: ReferenceError: a is not defined
Nach dem Auftreten der Ausnahme

12.1.4 Der finally-Block

Optional können Sie auch einen finally-Block bei der Ausnahmebehandlung notieren
([Link]):

try {
      [Link](eval("5 * 5"));
      [Link](eval("a * b"));
      [Link]("Im try-Block nach Ausnahme")
    } catch (e) {
      [Link]("Information in dem Ausnahmeobjekt: ", [Link]());
    }
    finally{
      [Link]("Kommt immer");
    }
    [Link]("Nach dem Auftreten der Ausnahme.");

Dieser wird immer ausgeführt. Unabhängig davon, ob eine Ausnahme aufgetreten ist
oder nicht. Vor allen Dingen wird dieser auch ausgeführt, wenn man mit einer Sprung-
anweisung innerhalb der try- oder catch-Blocks arbeitet.

12.2 Selbstdefinierte Ausnahmen erzeugen und verwenden

Man kann auch in JavaScript selbstdefinierte Ausnahmen erzeugen und diese dann ähn-
lich wie die Standardausnahmen von JavaScript verwenden. Damit erweitern Sie die
Flexibilität der Anwendung von Ausnahmen.

12.2.1 Erstellen einer selbstdefinierten Ausnahme

Die Klasse Error kann sowohl als Funktion über Error() als auch – wie jede Funktion
in JavaScript – als Konstruktor über new Error() verwendet werden. Damit erzeugt man
dann ein neues Error-Objekt. Interessant ist dabei, dass man mit Error([Meldung]) eine
selbstdefinierte Fehlermeldung für das Objekt aufbauen kann, die sich mit der Eigen-
schaft message auswerten lässt. Daraus resultiert der hauptsächliche Nutzen von selbst-
definierten Ausnahmen. Beispiel zum Erzeugen einer selbstdefinierten Ausnahme mit
Meldung:
288 12 Ausnahmebehandlung

Beispiel

a = new Error("Nein! Doch! Ohh!"); ◄

Nun braucht man in JavaScript durch die wenigen Datentypen und die lose Typisierung
sogar nicht einmal explizit ein Ausnahmeobjekt zu erstellen. Man kann jeden (!) Daten-
typ in JavaScript als Ausnahme werfen. Es kommt nur auf die Anwendung an – eben das
Werfen eines Datentyps.

12.2.2 Auswerfen von Ausnahmen mit throw

Eine selbstdefinierte Ausnahme muss zu einer spezifizierten Situation ausgeworfen wer-


den. Das machen Sie mit dem Schlüsselwort throw. Das ist eine Sprunganweisung mit
Rückgabewert – eben das Ausnahmeobjekt. Verwandt ist die Situation mit dem Aufruf
einer return-Ausweisung. Die Syntax sieht so aus:

Beispiel

throw [Ausdruck]; ◄

12.2.3 Ein Beispiel zum manuellen Werfen von Ausnahmen

Nachfolgend sehen Sie ein einfaches Skript, in dem wir Callbacks registrieren, die mit
throw verschiedene Datentypen als Ausnahmen werfen sollen ([Link]). Deren Meldungen
werden dann im Ausgabebereich angezeigt:

"use strict";
try {
       throw new Error("Nein! ");
} catch(e) {
  catchMeldung(e);
}
try {
      throw "Doch!";
} catch(e) {
   catchMeldung(e);
}
try {
   throw new Date();
} catch(e) {
   catchMeldung(e);
}
12.2 Selbstdefinierte Ausnahmen erzeugen und verwenden 289

try {
     throw 42;
} catch(e) {
   catchMeldung(e);
}
function catchMeldung(e) {
    [Link]("Typ: " + (typeof e) + ", Meldung: " + e);
}

In den Callbacks werfen wir direkt Ausnahmen unterschiedlicher Datentypen und fan-
gen sie wieder auf, was in der Praxis so natürlich nicht sinnvoll ist und immer mit einer
gewissen Logik gekoppelt sein sollte. Aber dieses Beispiel macht die Anwendungs-
möglichkeiten deutlich.
Asynchrone Programmierung
13

In diesem Kapitel kümmern wir uns hauptsächlich um sogenanntes asynchrones


JavaScript. Dieser Begriff bezieht sich auf die Ausführung von JavaScript-Code,
bei der bestimmte Operationen oder Funktionen nicht blockierend sind und parallel
ablaufen. Das bedeutet, dass der Code nicht auf das Ergebnis einer Operation war-
ten muss, bevor er mit der Ausführung des nächsten Teils des Codes fortfährt.

13.1 Was versteht man unter asynchronem JavaScript?

 Ein Thread ist vereinfacht in der Informatik der kleinste ausführbare Programm-
bereich innerhalb eines Prozesses. Prozesse sind Instanzen von Programmen, die im
Betriebssystem ausgeführt werden. Innerhalb eines Prozesses können mehrere Threads
gleichzeitig existieren und arbeiten. Threads teilen sich den Speicher und andere Res-
sourcen eines Prozesses, was ihre Kommunikation und Zusammenarbeit erleichtert. Ein
Thread kann unabhängig oder in Zusammenarbeit mit anderen Threads arbeiten, um eine
Aufgabe zu erledigen. Threads können parallel oder nacheinander ausgeführt werden, je
nachdem, wie das Betriebssystem die Ressourcen verwaltet und verteilt. Threads werden
oft für Multitasking und Multithreading verwendet, um effizient Aufgaben zu erledigen,
die gleichzeitig oder nebenläufig ausgeführt werden können. Sie sind in vielen modernen
Betriebssystemen und Programmiersprachen integraler Bestandteil der Entwicklung von
Anwendungen und Systemen.

Traditionell war JavaScript eine Sprache, die auf einer einzigen Thread-Ausführung ba-
siert. Das bedeutet, dass sie synchron ist. Wenn eine Operation ausgeführt wird, hält die
Ausführung des restlichen Codes an, bis diese Operation abgeschlossen ist. Dies kann

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 291
Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
292 13 Asynchrone Programmierung

zu Problemen führen, insbesondere wenn es um Operationen geht, die eine lange Zeit
zum Abschluss benötigen, wie beispielsweise das Laden von Daten von einem Server.
Asynchrones JavaScript wurde eingeführt, um dieses Problem zu lösen. Es ermöglicht,
dass bestimmte Operationen im Hintergrund ausgeführt werden, während der restliche
Code weiterhin ausgeführt wird. Dies wird oft durch die Verwendung von Callback-
Funktionen, Promises oder asynchronen Funktionen erreicht. Populär wurde diese Art
der Programmierung vor allen Dingen mit Ajax (Asynchronous JavaScript and XML)1,
was wir ausführlich behandeln und mit dem wir auch nach ein paar Grundlagen begin-
nen wollen. Asynchrones JavaScript ist allgemein besonders wichtig in Umgebungen
wie Webbrowsern, wo Operationen wie das Laden von Bildern, das Abrufen von Daten
von einem Server oder das Verarbeiten von Benutzereingaben oft Zeit benötigen und die
Benutzeroberfläche nicht blockieren sollten. Durch die Verwendung von asynchronem
JavaScript können solche Operationen im Hintergrund ausgeführt werden, während die
Benutzeroberfläche weiterhin reagiert und andere Operationen ausführt. Nachfolgend
schauen wir uns ein einfaches Beispiel an, das als gute Simulation für asynchrones Java-
Script dienen kann und das Konzept einer Callback-Funktion nutzt ([Link]):

[Link]("Start");
setTimeout(function() {
  [Link]("Timeout abgelaufen");
}, 2000); // Warte 2 Sekunden, bevor die Callback-Funktion ausgeführt
wird
[Link]("Ende");

In diesem Beispiel wird Start zuerst ausgegeben, dann Ende, und schließlich, nachdem
2 s vergangen sind, wird Timeout abgelaufen ausgegeben. Während die Ausführung des
Callbackes mit setTimeout eine Verzögerung von 2 s hat, blockiert dies nicht die Aus-
führung des restlichen Codes. Das ist zwar nur eine Simulation, aber das Kernkonzept ist
damit bereits demonstriert.

Beachten Sie, dass setTimeout sowohl als Methode vom DOM-Objekt window als auch als Build-
in-Funktion von [Link] zur Verfügung steht. Das Beispiel funktioniert also sowohl im Browser als
auch unter [Link].

13.2 Die Beziehung zwischen Webserver und Browser

Asynchrone Vorgänge entstehen im Web alleine schon deswegen, weil Skripte im Brow-
ser und Skripte bzw. Anwendungen in einem Webserver nicht synchron sein können.
Sie „wissen“ nichts voneinander und zudem sind die Programme meist auch räumlich

1 Früher oft vollständig großgeschrieben als AJAX.


13.2 Die Beziehung zwischen Webserver und Browser 293

getrennt, was eine Verbindung über ein Netzwerk erzwingt. Im Internet im Allgemeinen
und dem WWW im Speziellen werden Daten und Ressourcen von einem Webserver zur
Verfügung gestellt und jeweils gezielt von seinen Klienten über ein solches Netzwerk an-
gefordert. Es handelt sich hier im WWW also immer um ein Client–Server-System. Der
Webserver (Server kommt aus dem lateinischen von Servus und das bedeutet Sklave oder
Diener) stellt in dieser Beziehung den „Diener“ dar und der Anwender mit seinem Brow-
ser seinen „Herrn“. Dieser „Diener“ hat die Wünsche seines „Herrn“ zu befriedigen. Der
Webserver ist also ein (dummer) Diener, der – sofern keine Zugangsbeschränkungen
oder Probleme es verhindern – einfach nur auf eine Anforderung eine gewisse Leistung
(oder genauer – Ressource) zur Verfügung stellt, nicht mehr und nicht weniger. Erst
wenn ein Applikation-Server im weiteren Sinn ins Spiel kommt, kann man von einer ge-
wissen eigenständigen „Intelligenz“ sprechen.

13.2.1 Der grundsätzliche Ablauf einer Kommunikation – HTTP und


HTTPS

Bei der Client–Server-Beziehung im WWW wird eine Kommunikation grundsätzlich


vom Klienten eingeleitet. Sie geben in der Adresszeile des Browsers einen URL an, Sie
klicken auf einen Hyperlink oder ein Lesezeichen, Sie schicken Formulardaten zu einem
Server etc. Und dann schickt Ihnen der Webserver Daten und Ressourcen als Antwort.

[Link] HTTP(s) und TCP/IP


Basis dieser immer vom Client ausgehenden Kommunikation ist das Protokoll HTTP
(Hypertext Transfer Protocol) bzw. dessen neuere, verschlüsselte Variante HTTPS
(Hypertext Transfer Protocol Secure), das als Dienstprotokoll auf dem Transport-
protokoll TCP/IP (Transmission Control Protokoll/Internet Protokoll) aufsitzt. Dabei
beschreibt TCP/IP ein Übertragungssystem zwischen Client und Server, bei dem die
auszutauschenden Daten zwischen den beiden kommunizierenden Systemen in kleine
Datenpakete zerlegt werden. Die gesamte Internetkommunikation basiert auf der Daten-
übertragung in Form einer solchen Paketvermittlung, die mit TCP/IP realisiert wird,
wobei hier TCP in unserem Sprachgebrauch als Familie zu sehen sein soll und auch
Protokolle wie UDP einschließt. Bei einer Paketvermittlung und der Zerlegung aller zu
übertragenden Daten in kleinere Datenpakete werden diese Pakete als abgeschlossene
und vollständige Transporteinheiten behandelt, die unabhängig voneinander vom Sender
zum Empfänger gelangen. Es werden einfach Datenpakete hin und her verschickt, die
nur aufgrund von Adressangaben (IP-Nummern oder damit gekoppelter Zuordnungen
wie Namenssysteme) zugeordnet werden. Dabei wird während der Kommunikation
über TCP/IP explizit keine Verbindung im eigentlichen Sinn zwischen dem Server und
dem Client aufgebaut oder gehalten. Bei einer Paketvermittlung unterscheidet man den-
noch allgemein zwischen einer verbindungsorientierten und einer nichtverbindungs-
orientierten (oder auch zustandslosen) Form der darauf aufsitzenden Dienstprotokolle.
294 13 Asynchrone Programmierung

Diese Spezifizierung der Kommunikationsform wird also nicht auf der Transportebene,
sondern oberhalb davon jeweils mit spezifischen Protokollen realisiert, die auf TCP/IP
aufsetzen. Solche Protokolle sind etwa ssh zur Fernsteuerung von entfernten Rechnern,
ftp (File Transfer Protocol) zur Dateiübertragung oder eben auch HTTP. Diese auf dem
eigentlichen Transportprotokoll aufsetzenden Protokolle sind besagte Dienstprotokolle.
Bei einer verbindungsorientierten Datenkommunikation wird oberhalb der Daten-
pakete in der Ebene der Dienstprotokolle zwischen Sender und Empfänger eine tempo-
räre virtuelle/logische Verbindung aufgebaut. Das bedeutet, obwohl rein physikalisch
keine dauerhafte Verbindung zwischen den Kommunikationspartnern vorhanden ist, wird
über ein Dienstprotokoll ein Mechanismus bereitgestellt, um eine dauerhafte Verbindung
zu gewährleisten. Das ist zum Beispiel bei einer Echtzeitkommunikation wie VoIP (Voice
over IP – Internettelefonie) oder beim Fernsteuern von einem Remoterechner der Fall.
Bei der nichtverbindungsorientierten Form der Datenübertragung verschickt ein Sen-
der hingegen einfach Datenpakete mit allen notwendigen Informationen zum Empfänger
und kümmert sich nicht um eine virtuelle Verbindung. Jede Anfrage durch einen Client an
einen Server erzeugt dort genau eine Sendung einer Antwort (die durchaus in viele Einzel-
pakete zerlegt sein kann, die erst beim Empfänger wieder zusammengefügt werden). Da-
nach „vergessen“ beide Kommunikationspartner die Verbindung. Das zur Kommunikation
von einem Webserver und einem Webclient (in der Regel ein Webbrowser) entwickelte
HTTP ist nun genau so ein zustandsloses Protokoll. Es erlaubt einen einfachen Datenaus-
tausch zwischen den beteiligten Parteien, bei dem der Client Daten vom Server anfordert
und als Antwort vom Server eine Webseite gesendet bekommt. Aber es wird keine Ver-
bindung zwischen Webserver und Webbrowser aufrecht gehalten. Das erschwert auch die
Verfolgung zusammengehörenden Kommunikationsschritten zwischen einem Client und
dem Webserver und die ressourcenschonende Nachforderung von Daten. Hier ist ein Di-
lemma angesiedelt, das Ajax mittlerweile gelöst hat, während bereits davor sogenannte
Sessions zusammengehörende Schritte protokolliert haben – etwa mit Cookies.

[Link] Die HTTP(s)-Details und die Header


HTTP wird in den offiziellen RFCs von TCP der Standard-Port 80 zugeordnet. Zusätz-
liche Informationen für die Kommunikation (Übertragungsart, Browsertyp, Servertyp,
Sprache etc.) können in Form von Klartext im HTTP-Header untergebracht werden.
Gegebenenfalls können von einer Applikation auch eigene Header-Felder ergänzt wer-
den. Ein HTTP-Header, der wie gesagt aus lesbarem Klartext besteht, wird immer von
einer Leerzeile begrenzt. Die HTTP-Header (HTTP-Request-Header für die Anfrage und
HTTP-Response-Header für die Antwort) werden dabei als Environment-Variablen (Um-
gebungsvariablen) übertragen.

[Link].1 Der HTTP-Request
Bei einer typischen HTTP-Anfrage (HTTP-Request) durch einen Client besteht das ge-
samte Datenpaket, das an den Webserver gesendet wird, ausschließlich aus dem HTTP-
Header. Schauen wir uns so einen typischen HTTP-Request exemplarisch an:
13.2 Die Beziehung zwischen Webserver und Browser 295

[Link]
GET / http/1.1
Host: [Link]
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; de; rv:[Link])
Gecko/20.091.221 Firefox/3.5.7 (.NET CLR 3.5.30729)
Accept: text/html,application/xhtml + xml,application/
xml;q = 0.9,*/*;q = 0.8
Accept-Language: de-de,de;q = 0.8,en-us;q = 0.5,en;q = 0.3
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859–1,utf-8;q = 0.7,*;q = 0.7
Keep-Alive: 300
Connection: keep-alive

 Mit einem Sniffer wie Wireshark oder dem Netzwerkmodul von modernen
Browsern können Sie die verschickten Daten vom Browser sowie die empfan-
genen Daten vom Server analysieren.

Deutlich sind als zweite Information im Header die Methode der Datenanforderung
(hier GET) und die Protokollversion (hier http 1.1) zu erkennen. Auf die Startzeile des
Requests folgen eine Reihe von Feldern in mehr oder weniger beliebiger Reihenfolge,
die die Anfrage genauer beschreiben. Jedes Header-Feld besteht aus einem Namen und
einem Wert. Das Wertepaar wird durch einen Doppelpunkt voneinander getrennt. Die
Werte gleichnamiger Header-Felder können in einem Header zusammengefasst werden,
indem sie mit einem Komma getrennt werden. Sie können auch eigene Felder definieren
können – der Webserver muss nur diese auch verstehen.

 Eine Liste der wichtigsten standardisierten HTTP-Header-Felder finden Sie


hier: [Link]

Bei HTTPS gibt es weitere Felder, die hier nicht verfolgt werden. Grundsätzlich be-
schränken wir uns in der Folge auf HTTP.

[Link] Die Methoden der Datenübertragung per HTTP


Sowohl beim <form>-Tag eines Webformulars und dem dort vorkommenden method-
Parameter als auch direkt unter Ajax legen Sie für die Datenübertragung per HTTP die
genaue Art und Weise fest, wie die eingegebenen Daten zum Server gelangen und dort
zu behandeln sind. Dabei wird gewöhnlicherweise GET oder POST verwendet. Diese
Angaben spezifizieren unterschiedliche Möglichkeiten, mit HTTP Daten vom Client zum
Server zu schicken. HTTP ist wie gesagt ein zustandsloses und transaktionsorientiertes
Protokoll. Das bedeutet, zu jeder Anfrage eines Clients (Request) wird vom Empfänger
(dem Webserver) genau eine Antwort (englisch: Response) generiert. Ein solcher HTTP-
Request wird zum Beispiel von einem Webbrowser generiert, wenn Anwender eine URL
in die Adresszeile des Browsers eingegeben oder auf einen Hyperlink in der Webseite
296 13 Asynchrone Programmierung

geklickt haben. Ist die Anfrage erfolgreich, dann enthält die Antwort des Servers die
angefragte Ressource zur Darstellung. Bei einer klassischen Webseite ohne Anwender-
interaktion ist der Frage-Antwort-Zyklus damit beendet. Ein solcher Zyklus läuft aber
ebenso ab, wenn Benutzereingaben in einem HTML-Formular durch den Client ab-
geschickt werden oder auch wenn per Ajax Daten vom Server angefordert werden. Das
soll im Fall von Ajax möglicherweise unbemerkt vom Anwender erfolgen. Bevor die
Daten vom Client an den Webserver gesendet werden, werden sie zunächst vom Brow-
ser zu einer einzigen Zeichenkette verpackt. Die Art und Weise der weiteren Behandlung
kann durch die Angabe von GET oder POST gesteuert werden. Bei der GET-Methode
werden die Daten vor dem Versenden zusammen an das Ziel der Datenübertragung (die
URL) angehängt, wobei die Daten in der Regel durch ein ? von der ursprünglichen URL
abgetrennt werden. Diese entstehende Pseudo-URL sieht ein Anwender nach dem Ab-
schicken eines Formulars im Adressfeld des Browsers. So eine Pseudo-URL können Sie
auch manuell im Adressfeld des Browsers zur Ergänzung einer normalen Adresse eines
Servers eingeben oder in einem Hyperlink aufrufen.
Wenn Sie beim Versenden von Daten jedoch die Methode POST verwenden, werden
die zu verschickenden Daten in eine Umgebungsvariable gepackt, die im HTTP-Rumpf
versteckt ist. Das empfangende Programm beziehungsweise Skript auf dem Server wer-
tet so oder so die übergebenen Daten aus (sofern es entsprechend programmiert ist),
wobei einige Arbeit zu leisten ist. Wir werden das genauer sehen, wenn wie einen voll-
ständigen Webserver selbst programmieren (Abschn. [Link]).

[Link].1 Die URL-Kodierung
Ein Pseudo-URL mit der Übergabe von Werten an ein Skript beziehungsweise Pro-
gramm enthält die Zeichen der Benutzereingabe nicht unverändert. Bei der Generierung
des Pseudo-URL wird in der Regel der Prozess der URL-Kodierung durchlaufen, was
beim klassischen Versenden von Formulardaten automatisch erfolgt. Diese URL-Ko-
dierung erfolgt hauptsächlich, damit bei der Übertragung von Sonderzeichen über das
Internet/Intranet keine Probleme entstehen. Dabei werden alle Leerzeichen in dem
String, der aus den Benutzereingaben zusammengesetzt wird, durch Pluszeichen ersetzt.
Zusätzlich werden sämtliche reservierten Zeichen, die ein Benutzer beispielsweise im
Formular eingegeben hat (etwa das Gleichheitszeichen oder das kaufmännische Und),
in hexadezimale Äquivalente konvertiert. Ein so konvertiertes Zeichen wird jeweils
mit dem Prozentzeichen eingeleitet. Danach folgt der Hexadezimalcode. In dem String
des Pseudo-URL können also weder das Gleichheitszeichen, das Prozentzeichen, das
Fragezeichen, das Pluszeichen oder das kaufmännische Und (Ampersand) durch Be-
nutzereingaben vorkommen. Auch zahlreiche andere Zeichen werden verschlüsselt.
Wenn die Daten an ein auswertendes Skript beziehungsweise Programm auf dem Ser-
ver übermittelt werden, kommen alle Daten als ein Satz von Name-Wert-Paaren an. Der
Name repräsentiert eine Variable, die beispielsweise über ein entsprechendes Tag in der
HTML-Seite festgelegt wurde. Die Werte sind das, was die Variable für einen Wert an
den Server übermitteln soll. Das kann z. B. eine Anwendereingabe sein. Dieser Satz von
13.2 Die Beziehung zwischen Webserver und Browser 297

Name-Wert-Paaren wird in einem Teilstring des URL übermittelt, den ein Webskript
wieder auflösen muss. Ein solcher String (Teilstring mit zwei Wertepaaren) ist ungefähr
so aufgebaut:

Beispiel

farbe = rot&zahl = 123&text = felix ◄

Der String mit den Wertepaaren muss vom auswertenden Skript beziehungsweise Pro-
gramm beim kaufmännischen UND (&) sowie dem Gleichheitszeichen (=) in einzelne
Stücke zerlegt werden. Die entstandenen Teilstücke müssen dann noch dekodiert werden.

[Link] Einen Webserver mit [Link] erstellen


Wir schauen uns den Vorgang einmal konkret an und programmieren selbst einen Web-
server, was die theoretischen Ausführungen praktisch erfahrbar macht. Das ist mit Node.
js und passenden Modulen als Erweiterung gut machbar ([Link]). Der Server ist
nicht praxisfest, aber schon recht umfangreich von der Funktionalität ausgearbeitet.

"use strict";
const http = require('http');
const fs = require('fs');
const url = require('url');
const path = require('path');
let queryAntwort = "";
const server = [Link]((req, res) => {
  const parsedUrl = [Link]([Link], true);
  let filePath = '.' + [Link];
  if (filePath === './') {
    filePath = './[Link]'; // Standardseite
  }
  const extname = [Link](filePath);
  let contentType = 'text/html';
  switch (extname) {
    case '.txt': contentType = 'text/plain'; break;
    case '.html': contentType = 'text/html'; break;
    case '.css': contentType = 'text/css'; break;
    case '.js': contentType = 'text/javascript'; break;
    case '.json': contentType = 'text/javascript'; break;
    case '.xml': contentType = 'text/xml'; break;
    default: contentType = 'text/plain';
  }
  [Link](filePath, (err, content) => {
    if (err) {
      if ([Link] === 'ENOENT') { // Datei nicht gefunden
298 13 Asynchrone Programmierung

         [Link](404);
         [Link]('Datei nicht gefunden');
      } else {
        [Link](500); // Anderer Fehler
        [Link]("Interner Serverfehler: "+ [Link]);
      }
    } else { // Datei gefunden, sende Inhalt oder Reaktion auf Query
      [Link](queryAntwort); // Query
      [Link](200, { 'Content-Type': contentType });
      // Wenn kein Query vorhanden
      if([Link] < 5)
        [Link](content, 'utf-8'); // Sende Datei
      else // Sende Query-Daten
        [Link](queryAntwort, 'utf-8');
    }
  });
});
[Link]('error', (err) => {
  [Link]('Serverfehler:', err);
});
// Daten über GET oder POST entgegennehmen
[Link]('request', (req, res) => {
  if ([Link] === 'GET') {
    [Link]('GET-Anfrage empfangen');
    const queryData = [Link]([Link], true).query;
    [Link]('Daten:', queryData);
    queryAntwort = [Link](queryData);
  } else if ([Link] === 'POST') {
    [Link]('POST-Anfrage empfangen');
    let body = '';
    [Link]('data', (chunk) => {
    body += [Link]();
    });
    [Link]('end', () => {
    [Link]('Daten:', body);
    queryAntwort = body;
    // Hier auf die POST-Daten reagieren und entsprechend queryAntworten
// [Link](200, { 'Content-Type': 'application/json' });
// [Link]([Link]({ message: 'POST-Erfolg' }));
    });
  }
});
// Server starten
const PORT = [Link] || 3210;
13.2 Die Beziehung zwischen Webserver und Browser 299

[Link](PORT, () => {
  [Link]("Server läuft auf Port " + PORT);
});

Auch wenn der Quellcode nicht ganz trivial ist, sind die Techniken bekannt und decken
das ab, was wir gerade zu Datenübertragung ausgeführt haben. In der Konsole gibt der
Server auch wesentlichen Kommunikationsschritte aus.
Zuerst werden die benötigten [Link]-Module importiert:

• http: Zur Erstellung des HTTP-Servers und zur Verarbeitung von HTTP-Anfragen.
• fs: Zur Arbeit mit dem Dateisystem, um statische Dateien zu lesen.
• url: Zur Analyse von URL-Objekten für die Anfrageverarbeitung.
• path: Zur Bearbeitung von Dateipfaden.

Die Variable queryAntwort wird verwendet, um bei einer Datenübermittelung an den


Server gezielt reagieren zu können. Statt dann einfach die angeforderte Datei zu schi-
cken, kann man eine individuelle Logik implementieren. In dem Beispiel wird einfach
der Query zurückgegeben, was aber in der Praxis dann natürlich ausgearbeitet wird.
Dann wird der konkrete HTTP-Server erstellt. Dazu stellt das http-Modul die Methode
createServer bereit, der Parameter für den Request (Anfrage durch Client) und Response
(Antwort an Client) an eine Callback-Funktion übergeben werden können. Für die Um-
setzung der Server-Logik wird die Lambda-Notation verwendet.
Die konkrete Anfrageverarbeitung analysiert die URL der Anfrage, um den Pfad zur
angeforderten Ressource zu erhalten. Wenn die URL nur das Wurzelverzeichnis darstellt,
wird eine Standardseite ([Link]) vorausgesetzt, was bei vielen Webservern üblich
ist. Das Modul url stellt die parse-Methode bereit und der Request-Parameter die Eigen-
schaft url. Um flexibel auf die Art der angefragten Datei reagieren zu können, wird der
Dateityp bestimmt. Dazu wird aus dem Modul path die Methode extname verwendet und
dann der MIME-Type über contentType festgelegt.
Der nächste Schritt kümmert sich um das Lesen der Datei und das Senden an den
Client. Das Module fs stellt dazu die Methode readFile zur Verfügung, die mit einer
Fehlerbehandlung einigermaßen praxisfähig aufgearbeitet ist. Es wird entweder eine
Meldung gesendet, dass eine Datei nicht gefunden wird, eine Defaultseite gesendet, die
angeforderte Datei geschickt oder aber im Fall einer Datenübermittelung per GET oder
POST der Query als Antwort geschickt. Die Entscheidung, ob eine Übergabe von Daten
erfolgt ist, wird primitiv über die Länge der Variablen queryAntwort getroffen. Diese
muss eine gewisse Länge haben, wenn Daten übermittelt wurden.
Zusätzlich gibt es noch eine Implementierung, um Serverfehler zu behandeln. Mittels
on-Eventhandling wird auf das error-Event reagiert und beim Auftreten eines Fehlers
mit Lambda-Notation das Fehlerobjekt zur Behandlung übergeben. Wie analog in jQuery
gibt es im http-Modul eine Methode on, um Event-Handler für verschiedene Ereignisse
300 13 Asynchrone Programmierung

Abb. 13.1 Der [Link]-Server läuft und nimmt Anfragen von einem Browser entgegen, die pro-
tokolliert werden

zu registrieren, die während des HTTP-Serverbetriebs auftreten können, wie z. B. "re-


quest" für eingehende Anfragen oder "error" für Fehlerereignisse.
Damit Daten entgegengenommen werden können, wird mit on beim request-Event ein
Callback reagiert, der sowohl mit GET als auch POST umgehen kann. Beim Request-
Parameter gibt es die Eigenschaft method. Wenn die Methode GET ist, extrahiert man
die übergebenen Werte aus dem Query mittels der parse-Methode des url-Moduls. Man
erhält die Daten in JSON-Form. Der Wert wird dann stringifiziert der Variablen query-
Antwort zugewiesen. Bei POST ist die Extraktion etwas aufwendiger, denn die Daten
werden – wie oben beschrieben – im Header der Anfrage übermittelt und müssen dort
gefunden und extrahiert werden. Die Daten bei POST werden auch als JSON verstanden
und entsprechend behandelt und der Variablen queryAntwort zugewiesen. Das Starten
des Servers erfolgt mit der listen-Methode, wobei vorher der Port auf 3210 eingestellt
wurde (Abb. 13.1).
Wenn wir konkrete Ajax-Beispiele besprechen, werden wir auf diesen Server zurück-
greifen und dann noch das Verhalten etwas genauer sehen.

13.3 Ajax & Web 2.0

Kommen wir nun zu Ajax und dem Web 2.0, also die Grundlage dessen, was in RIAs
gipfelt – dynamische Applikationen, die sehr interaktiv und asynchron mit dem Webserver
kommunizieren und Inhalte der Webseite immer wieder durch nachgeladene Informationen
13.3 Ajax & Web 2.0 301

austauschen und dies meist im Hintergrund. Das soll bedeuten, dass dieser Datenaus-
tausch zwischen Client und Server im Gegensatz zu einer klassischen Webapplikation
oft gar nicht bemerkt wird, denn die Webseite, die zum Zeitpunkt der Veränderung im
Browser angezeigt wird, wird nicht mehr komplett ausgetauscht. Stattdessen werden nur
die Teile einer Webseite ausgetauscht, die zu aktualisieren sind. Und ob die neuen Daten
schon vorher im Client vorhanden sind und nicht angezeigt werden (beispielsweise über
das Vorausladen von Daten und Vorhalten in JavaScript) oder erst bei Bedarf gezielt vom
Webserver nachgeladen werden, spielt keine Rolle. Für die Applikation nicht und für
Anwender schon gar nicht. Den programmiertechnischen Kern dieser „reichhaltigen“
Möglichkeiten rein im Browser (als aufseiten des Clients) bildet JavaScript respektive
Ajax.

13.3.1 Das Datenformat

Bei der Datenanforderung mit Ajax, aber auch dem konventionellen Datenaustausch zwi-
schen Webserver und Browser, ist es von Bedeutung, welches Datenformat beim Aus-
tausch verwendet wird. Dabei wird die Anfrage inklusive aller HTTP-Header immer aus
reinem Klartext bestehen. Die Antwort des Servers besteht zwar auch aus Klartext (die
HTTP-Header-Informationen), aber es gibt auch die eigentliche Nutzlast. Dies sind Daten
und Ressourcen, die der Client eigentlich vom Server möchte. Metainformationen in Form
von HTTP-Headern sind normalerweise im Client uninteressant, solange kein Fehler bei
der Transaktion aufgetreten ist. Die Nutzlast kann nun in verschiedenen Datenformaten
vorliegen. Grundsätzlich können Sie also beliebige Ressourcen von einem Webserver an-
fordern, natürlich als Referenzen im Rahmen einer Webseite, aber auch direkt (also ohne
Webseite). Sofern ein anfordernder Browser mit einem spezifischen Datenformat direkt
umgehen kann, wird er die entsprechende Ressource direkt oder mit einem installierten
Plugin bzw. Add-on verarbeiten. Dies betrifft auch binäre Ressourcen wie beispielsweise
Bilder, Videos oder andere Multimediadateien. Kann der Browser mit dem Datenformat
einer gesendeten Ressource nicht unmittelbar umgehen, bietet er in der Regel über einen
Dialog die Speicherung auf dem lokalen System oder das Öffnen mit einem externen Pro-
gramm an. Im Rahmen der Webseite werden binäre Ressourcen wie gesagt natürlich als
Referenzen über HTML verankert. In Hinsicht auf die Datennachforderung mit Ajax oder
allgemein die Verarbeitung mit JavaScript sind binäre Datenformate allerdings nicht im
Detail von Bedeutung, denn hier werden Daten nur als Klartext verwendet.

[Link] Klartextdatenformate
Im WWW setzt man zum Austausch von interpretierbaren Informationen fast ausschließ-
lich auf das Klartextformat, denn dieses ist plattformneutral und wird auf nahezu jedem
System verstanden und identisch interpretiert, sofern man die Zeichensätze richtig
festgelegt. Wie konkret der Klartext auf dem Server entsteht, ist aus Sicht des Clients
302 13 Asynchrone Programmierung

vollkommen uninteressant. Er kann von einem serverseitigen Skript oder Programm


generiert werden oder bereits einfach als Textdatei auf dem Server vorhanden sein. Die-
ser Klartext kann sowohl vollkommen unstrukturiert sein als auch intern verschiedene
Strukturen aufweisen. Bei strukturierten Klartexten wird vom Webserver in der Regel
ein bestimmter MIME-Typ im HTTP-Header angegeben, aber es ist und bleibt Klar-
text. Auch bei strukturiertem Text ist es vollkommen gleichgültig, ob dieser Klartext
auf dem Server generiert wird oder bereits als fertige Datei vorliegt. In der Praxis haben
sich im WWW fünf Varianten an Klartextformaten durchgesetzt, wobei einige davon ins-
besondere erst im Zusammenhang mit Ajax von Bedeutung sind:

• Eine vollständige Webseite bestehend aus HTML oder XHTML


• Klartext, der mit HTML-Tags durchzogen ist, also HTML-Fragmente
• Reiner Klartext ganz ohne Struktur
• XML
• JSON

[Link].1 Klartext ohne Logik


Im Fall von Ajax-Datenanforderungen werden oft HTML-Fragmente vom Webserver
angefordert und diese in der Regel im Client einfach per DTHML in der Webseite ein-
gebaut. Hierbei werden einfach unter Verzicht auf eine vollständige Webseitenstruktur
Texte mit HTML-Tags gemischt. Meistens erfolgt der Einbau dieser Texte dann ohne
eine weitere Verarbeitung der HTML-Fragmente im Client durch JavaScript oder eine
andere clientseitige Programmiertechnik. Die Antwort des Webservers wird also vor-
wiegend unverändert angezeigt und muss vorher auf dem Server bereits in die endgültige
Form gebracht werden, die im Client dann verwendet werden soll. Aber wie gesagt – im
Fall von Ajax kann der Browser die HTML-Tags interpretieren, wenn die Daten nach-
geladen und in die Webseite eingefügt wurden, aber auch dann, wenn die Tags einfach
mit DHTML aus einem lokal bereits vorhanden Cache (etwa eine JavaScript-Variable
oder einem versteckten IFrame) in die Webseite geschoben werden.
Der Browser geht aber auch mit reinem Klartext vollkommen analog um. Wenn Sie
reine Textdateien ohne relevante Strukturen vom Webserver anfordern, werden diese
in der Regel vom Webserver mit dem MIME-Typ text/plain versehen. So gekenn-
zeichneter Klartext kann von jedem Browser direkt dargestellt werden. Er wird aber
nicht interpretiert! Aber wenn Textdateien mit Ajax nachgefordert werden, befindet sich
der Browser durch die Anzeige einer richtigen Webseite bereits in einem Verarbeitungs-
modus, bei dem er auch Daten mit dem MIME-Typ text/plain so in die Webseite ein-
bauen kann und wird, dass eventuell enthaltene HTML-Befehle interpretiert werden.
Auch lassen sich so nachgeladene JavaScript- oder CSS-Anweisungen sinngemäß ver-
wenden, wobei hier teilweise vor allen Dingen beim Nachladen von externen Dateien
mit inkonsistenten Browserverhaltensweisen gerechnet werden muss und man das in der
Praxis selten so macht.
13.3 Ajax & Web 2.0 303

[Link].2 Klartext mit Struktur und beliebiger Logik


Klartext ohne irgendwelche relevante Struktur wird also im Browser überhaupt nicht
interpretiert oder wenn HTML-Strukturen bzw. ein entsprechender MIME-Typ vorliegen
und erkannt werden, erfolgt die Standardverarbeitung für HTML, sowohl mit als auch
ohne Ajax. Aber Sie werden viel anspruchsvollere Applikationen erstellen können, wenn
Sie die Antwort des Servers um ein strukturiertes Format erweitern, das im Client ge-
zielt verwertet werden kann. Die Strukturierung der übertragenen Information lässt
Ihnen weit mehr Möglichkeiten, um Geschäftsprozesse zu verteilen. Als Strukturierungs-
format kommen JSON oder XML infrage. Schickt der Webserver also XML oder JSON,
kann man im Client auf der Antwort operieren und damit Geschäftslogik zum Client ver-
lagern. Allerdings ist das hauptsächlich bei Ajax eine sehr interessante Möglichkeit. Das
direkte Laden dieser Datenstrukturen ist selten sinnvoll.

 Die Verlagerung von Logik in einer Webapplikation in den Client ist teilweise kri-
tisch. Sie sollten gründlich darüber nachdenken, ob das für Ihre Applikation wirk-
lich der richtige Weg ist.

13.3.2 Was ist Ajax und was bezeichnet Web 2.0?

Der Marketingbegriff Web 2.0 kann nicht klar technisch definiert werden. Er beschreibt
mehr eine Art des Umgangs mit dem WWW. Statt passiv zu konsumieren, fasst man unter
den zum Web 2.0 gefassten Diensten die Teile des Internets zusammen, bei denen Konsu-
menten gleichzeitig auch Produzenten von Inhalten sind. Teilnehmer können im Web 2.0
also aktiv werden und das ohne die Erstellung einer Webseite im klassischen Sinn. Man
redet deshalb beim Web 2.0 auch vom „Mit-Mach-Web“. So eine Konstellation trifft man
in Wikis, in Blogs, in File-Sharing-Portalen, sozialen Netzwerken und ähnlichen Diensten.

[Link] Ajax als technische Basis des Web 2.0


Zentraler Aspekt vieler Angebote, die im engeren Sinn zum Web 2.0 gezählt werden,
ist jedoch, dass in vielen Fällen als technische Basis Ajax fungiert. Wie durch die aus-
geschriebene Form Asynchronous JavaScript and XML deutlich wird, handelt es sich bei
Ajax also im Kern um eine Erweiterung von JavaScript. Mit Ajax ist es möglich, auch im
Internet interaktive Webseiten zu erstellen, die im günstigsten Fall ein Antwortverhalten
zeigen, wie es die Anwender von ihren Desktop-Applikationen gewohnt sind. Teile der
Webseite können durch neue Informationen ausgetauscht und ergänzt werden, ohne das
träge Antwortverhalten bisheriger Webapplikationen und die Unterbrechung der An-
wenderaktivität in Kauf nehmen zu müssen.
Ajax beschreibt im Grunde nicht viel mehr als die Möglichkeit, mit JavaScript
über ein 2005 neu verbreitetes Kommunikationsobjekt neue Daten für eine Webseite
nachzufordern und diese darin einzubauen, ohne die Webseite komplett neu laden zu
304 13 Asynchrone Programmierung

müssen. Vereinfacht gesagt werden Daten bei der Verwendung von Ajax also erst dann
angefordert, wenn sie benötigt werden, dann für JavaScript verfügbar gemacht und an-
schließend mit DHTML in die bereits in den Browser geladene Webseite eingebaut. Wenn
Sie Ajax einsetzen, wollen Sie in jedem Fall einen Teil einer bereits geladenen Webseite
austauschen oder dort Daten ergänzen, ohne die gesamte Webseite wieder neu zu laden.

13.3.3 Der grundsätzliche Ablauf

Der grundsätzliche Ablauf einer Ajax-Anfrage folgt dabei in der Regel immer dem glei-
chen Schema:

1. Zuerst wird ein Objekt (XMLHttpRequest oder kurz XHR) erzeugt, über das die Kom-
munikation am Browser vorbei mit dem Server erfolgen wird, in der Regel asynchron
zur eigentlichen bewussten Kommunikation des Anwenders mit dem Server. Dieses
Kommunikationsobjekt ist eine Erweiterung des Objektmodells von JavaScript.
2. Ein Callback wird bei diesem Kommunikationsobjekt über einen Eventhandler als
Funktionsreferenz registriert. Diese Funktion wird dann bei jeder Zustandsänderung
der Transaktion aufgerufen. Die angegebene Funktion wird nach der Registrierung für
jede Statusänderung des Kommunikationsobjekts aufgerufen. Eine Eigenschaft des
Kommunikationsobjekts gibt dabei Aufschluss über den aktuellen Status der Trans-
aktion beim Aufruf dieses Callbacks. So lassen sich einzelne Phasen der Datenüber-
tragung unterscheiden.
3. Die Verbindung wird geöffnet. Das ist aber noch nicht die konkrete Anfrage. Deshalb
ist es auch unerheblich, ob Schritt 2 und 3 vertauscht werden.
4. Die Anfrage wird abgeschickt und die Ajax-Applikation wartet auf die Antwort vom
Webserver.
5. Die Antwort des Webservers wird im Browser verwertet. Um eine vollständige Ant-
wort zu erkennen, kann die Statusänderung des Kommunikationsobjekts explizit ge-
nutzt werden.

Bei Ajax steht damit das Kommunikationsobjekt XMLHttpRequest im Mittelpunkt. Alle


modernen Browser bieten mittlerweile Unterstützung dieser asynchronen Kommunika-
tion zwischen Client und Webserver. Diese XMLHttpRequest-Objekte sind damit un-
mittelbar an dem internen Aufbau von HTTP orientiert.

13.3.4 Ein XMLHttpRequest-Objekt erzeugen

Um mit einem XHR-Objekt arbeiten zu können, muss dieses erzeugt werden. Mittler-
weile geht das in JavaScript einheitlich über den Einsatz einer passenden Konstruktor-
methode.
13.3 Ajax & Web 2.0 305

Beispiel

resObjekt = new XMLHttpRequest(); ◄

Ein Objekt vom Typ XMLHttpRequest stellt dann alle relevanten Methoden und Eigen-
schaften zur asynchronen Kommunikation per Ajax bereit. Dabei können auf Basis der
üblichen Übertragungsmethoden wie GET und POST Daten verschickt bzw. Informatio-
nen angefordert und Antworten empfangen sowie der Request-Header gesetzt und aus-
gewertet werden.

Beachten Sie, dass mit Ajax-Anfragen aus Sicherheitsgründen in der Regel nur Daten von der glei-
chen Domain angefordert werden dürfen, von der die anfordernde Webseite stammt. Das ist ein
oft zu findendes Sandkastenprinzip (Sandbox), das wir beim Thema Sicherheit ebenfalls berühren
(Same Origin Policy). Andernfalls würde man einen sogenannten Cross-Domain-Zugriff durch-
führen und dieser würde Missbrauch und Manipulation Tür und Tor öffnen. Allerdings ist die Ein-
schränkung erheblich und es gibt einige Workarounds, diese Beschränkungen aufzuheben, ohne
die Sicherheit zu gefährden.

In den ersten Jahren von Ajax haben verschiedene Browserinkompatibilitäten er-


zwungen, dass man das XHR-Objekt auf verschiedene Arten erzeugt, damit das Ver-
fahren in allen relevanten Browsern funktioniert. Das ist mittlerweile nicht mehr not-
wendig. Dennoch ist es sinnvoll, die Erzeugung von einem XHR-Objekt mit Ausnahme-
behandlung abzusichern. Das kann man so machen ([Link]):

Beispiel

function erzXHRObjekt(){
var resObjekt = null;
try {
resObjekt = new XMLHttpRequest();
} catch(e){
alert("Erzeugung XHR-Objekt nicht möglich");
}
return resObjekt;
}◄

13.3.5 Die Anforderung von Daten

Wenn wir ein XHR-Objekt erzeugen, können wir darüber Daten asynchron nachladen.
Zudem können Sie auf alle Header-Felder von der Anfrage an den Server und der Ant-
wort durch den Server zugreifen. Ein XHR-Objekt stellt Ihnen alle relevanten Methoden
und Eigenschaften bereit. Wie Sie diese Daten in der Webseite präsentieren, ist dann rei-
nes DHTML.
306 13 Asynchrone Programmierung

[Link] Die Methoden eines XHR-Objekts


Nachfolgend finden Sie die wichtigsten Methoden eines XHR-Objekts, die Sie für den
Ajax-Austausch entweder zwingend benötigen oder die teilweise optionalen Features
bereitstellen (Tab. 13.1):

Tab. 13.1  Die Methoden eines XHR-Objekts


Methode Beschreibung
abort Stopp der aktuellen Serveranfrage. Genau genommen wird damit nur das
Kommunikationsobjekt angewiesen, die Serverantwort zu ignorieren.
getAllResponse Über den Aufruf der Methode erhalten Sie als Rückgabe die vom Server
Headers gesendeten Header-Felder als String.
getResponseHeader Die Methode gibt das als Parameter benannte Header-Feld als String zurück.
open Die Methode initialisiert das Kommunikationsobjekt mit den Ver-
bindungsdaten, die für einen Kontakt zum Webserver notwendig sind.
Dabei werden mindestens zwei Parameter angegeben.
• Für den ersten Parameter kann GET, POST, PUT oder HEAD verwendet
werden, wobei in der Praxis GET oder POST dominieren.
• Eine URL ist als zweiter Parameter der relative oder absolute Pfad zum
Serverskript oder der angeforderten Datei, wobei gegebenenfalls ein
Query-String angefügt und ein Pseudo-URL verwendet werden kann.
• Das dritte Argument gibt an, ob eine Anfrage synchron (false) oder in
fast allen Fällen in der Praxis asynchron (true) verarbeitet wird. Sofern
eine synchrone Verarbeitung festgelegt wird, wird der folgende Versand
der Daten mit der send-Methode die Ausführung des Skripts so lange
blockieren, bis die Antwort des Servers vollständig empfangen wurde.
Beachten Sie jedoch, dass Fragen der Synchronisation bei Ajax mittler-
weile mit Promises gelöst werden!
•D  er optionale vierte Parameter ist ein gegebenenfalls benötigter Be-
nutzername für eine geschützte Ressource auf dem Server und der
ebenfalls optionale fünfte Parameter ist entsprechend das zugehörige
Passwort.
send Diese Methode wird zum tatsächlichen Abschicken einer Anfrage ver-
wendet. Sie wird nach dem Aufruf der open-Methode aufgerufen. Der
Parameter ist entweder null (bei GET) oder kann da auch weggelassen
werden oder ein Query-String bei POST mit zum Webserver zu über-
tragenden Daten wie etwa Benutzerdaten. Wenn bei der Methode POST
keine Daten zum Server geschickt werden, kann er auch da weggelassen
oder auf null gesetzt werden.
setRequestHeader Setzen individueller Header-Felder. Dabei steht der erste String-Parameter
für das Label und der zweite für den zugehörigen Wert.
setMimeType Setzen des MIME-Typs der angeforderten Daten (Response). Der Para-
meter ist die übliche Angabe eines MIME-Typs als String (z. B. "text/xml"
oder "text/html").
13.3 Ajax & Web 2.0 307

[Link] Die Eigenschaften eines XMLHttpRequest-Objekts


Ein XHR-Objekt besitzt neben den beschriebenen Methoden eine Reihe von Eigen-
schaften, die Sie für eine Ajax-Applikation benötigen oder die diese optional um einige
Möglichkeiten erweitern (Tab. 13.2):

Tab. 13.2  Die Eigenschaften eines XHR-Objekts


Eigenschaft Kurzbeschreibung
onreadystatechange Ein Eventhandler, der jedes Mal aufgerufen wird, wenn sich der Ver-
bindungsstatus (readyState) eines XHR-Objekts ändert. Man registriert
wie üblich in der Regel einen Callback.
readyState Aktueller Verbindungsstatus einer Transaktion. Mögliche Werte, die
an den Standardmeldungen eines Webservers orientiert sind, sind fol-
gende:
• 0: UNINITIALIZED. Das XHR-Objekt wurde noch nicht initialisiert.
Das bedeutet, die Verbindung wurde noch nicht geöffnet beziehungs-
weise die Methode open noch nicht aufgerufen.
• 1 : LOADING. Das Request-Objekt wurde erstellt und initialisiert,
aber es wurde noch keine Anfrage mit send gesendet.
• 2 : LOADED. Die Anfrage wurde gesendet und der Antwort-Header
sowie der Antwortstatus können ausgewertet werden.
• 3 : INTERACTIVE. Die Daten vom Server treffen gerade ein. Die
Eigenschaften responseText bzw. responseXML enthalten die bereits
empfangenen Daten.
• 4 : COMPLETED. Die Kommunikation mit dem Server ist ab-
geschlossen und alle Daten sind angekommen, wenn kein Fehler
aufgetreten ist. Dies ist der wichtigste Fall bei der Erstellung von
Ajax-Applikationen.
Sie können beispielsweise in einer Entscheidungsanweisung Status-
codes auswerten und entsprechend Reaktionen implementieren. In der
Praxis macht fast nur die Abfrage auf readyState =  = 4 Sinn.
responseText Die vom Server gesendeten Daten als Text.
responseXML Vom Server gesendete XML-Daten (d. h. ein Knotenobjekt vom
Typ node). Wenn die Daten nicht in XML-Form (oder allgemein
als Baum) gesendet worden sind, enthält responseXML den Wert null.
status Der HTTP-Status der Verbindung als Zahl. Das korrespondiert un-
mittelbar mit dem zugehörigen Wert der Serverantwort. Sie können
darüber genauer die Antwort des Webservers verwerten, als es mit
readyState möglich ist. Im Allgemeinen ist diese genauere Verwertung
allerdings nicht notwendig.
statusText Der HTTP-Status als Textmeldung, sofern eine solche übermittelt
wurde. Das korrespondiert unmittelbar mit dem zugehörigen Wert der
Serverantwort.
308 13 Asynchrone Programmierung

13.3.6 Verschiedene Datentypen vom Server per Ajax nachfordern

Wie oben beschrieben, werden Daten mit der open-Methode vom Server nachgefordert.
Obwohl in der Abkürzung Ajax explizit XML steckt, kann man für den asynchro-
nen Datenaustausch jede Form von Klartextdaten nutzen, wie wir es schon besprochen
haben. Es ist genau genommen so, dass man oft die wenigsten Probleme bei Ajax-Ap-
plikationen hat, wenn die angeforderten Daten bereits als HTML-Fragmente oder als rei-
ner Text zum Client geschickt und dort eingefügt werden. Die Aufbereitung der Daten
erfolgt in diesem Fall vollständig auf dem Server und der Client zeigt die fertigen Daten
nur unverändert (maximal in gewisser Weise formatiert) an.

 Beachten Sie, dass Sie bei Ajax keine vollständige Webseite nachfordern sollten.
Das Grundgerüst der nachgeladenen Webseite würde unter Umständen zu Kom-
plikationen führen, wenn es in ein bestehendes Grundgerüst eingefügt und vom
Browser interpretiert wird. Ebenso sollten Sie beachten, dass bei Ajax keine bi-
nären Dateien direkt nachgefordert werden. Wenn Sie Bilder oder andere binäre
Ressourcen nachladen wollen, fordern Sie per Ajax nur die Referenzen auf diese
binären Ressourcen an. Der Browser fordert die neuen binären Ressourcen dann
über die Referenzen als neue Anfrage vom Webserver nach. Das XHR-Objekt
stellt keine Methoden oder Eigenschaften zur Verfügung, um direkt mit binä-
ren Ressourcen umzugehen. Das wäre auch unsinnig. Wobei es rein theoretisch
die Möglichkeit gäbe, in sogenannten CDATA-Bereichen von XML binäre Daten
unterzubringen und damit per Ajax anzufordern. Aber auch das wäre in der Regel
der falsche Weg.

Wenn Sie XML oder JSON zum Client schicken, müssen Sie diese Daten dort ver-
arbeiten. XML-Daten werden im Client als node-Objekt verfügbar gemacht und können
dort verarbeitet werden. JSON wird entweder bereits als Objekt interpretiert oder kann
über [Link] aus einer stringifizierten Form reproduziert werden. Dabei wird JSON
konsistenter und problemloser in den verschiedenen Browsern verarbeitet als XML.

 Tipp Wenn Sie mit Ajax arbeiten, müssen Sie die Daten von einem Webserver
anfordern. Sie können Apache nehmen, was etwa die Ausführung eines PHP-
Skripts erlaubt, um Daten entgegenzunehmen. Aber auch der von uns bereits
selbst programmierte [Link]-Webserver eignet sich und dieser soll die Basis
darstellen. Beachten Sie, dass man diesen Webserver auch erweitern könnte,
um auf Spezifika von Ajax-Anfragen zu reagieren. Etwa so:

[Link]('request', (req, res) = > {


  if ([Link][’x-requested-with’] =  =  = 'XMLHttpRequest') {
     [Link]('AJAX-Anfrage empfangen'); // Dies ist eine AJAX-Anfrage
13.3 Ajax & Web 2.0 309

        [Link](200, { 'Content-Type': 'application/json'});


        [Link]([Link]({ message: 'AJAX-Erfolg'}));
    }
});

Die Prüfung auf XMLHttpRequest bei den Anfrage-Headern ist historisch be-
dingt. XMLHttpRequest war die erste API in JavaScript, die es ermöglichte,
HTTP-Anfragen aus dem Browser heraus zu senden, aber heutzutage nicht
mehr die einzige Möglichkeit. Es gibt auch moderne Alternativen wie die
Fetch API, die Promises verwendet. Die Erweiterung ist aber nicht unbedingt
notwendig, denn die bisherige Version von dem Webserver liefert die ge-
wünschten Daten auch bei Ajax-Anfragen. Die Prüfung akann jedoch nützlich
sein, wenn eine spezielle Reaktion auf Ajax-Anfragen erfolgen soll.

[Link] Eine reine Textdatei mit Ajax nachfordern


Um eine reine Textdatei, die vom Webserver zum Download als statische Datei bereit-
gestellt wird, per Ajax nachzufordern, muss in der open-Methode bloß eine Referenz
auf den URL der Datei notiert werden. Das nachfolgende Beispiel fordert bei Klick auf
einen Button eine statische Textdatei vom Server an (ajax1_1.html), und baut den Inhalt
einfach in die Webseite ein:


    <link rel="stylesheet" type="text/css" href="lib/css/[Link]" />
     <script src="lib/js/[Link]" type="text/javascript"></script>
   <script src="lib/js/ajax_1.js" type="text/javascript"></script></
head>
  <body><h1>Frage den weisen Mann</h1>
    <button id="frage">Was ist das Geheimnis ewiger Jugend?</button>
    <div id="antwort"></div></body></html>

Sie sehen, dass die Webseite neben einer Schaltfläche einen leeren <div>-Container ent-
hält, der im Folgenden mit Ajax gefüllt werden soll. Der <div>-Container hat ein Attribut
id und dieses ist der Schlüssel zum Zugriff. Im Header finden Sie die Referenzen auf eine
CSS-Datei, die einfach nur ein bisschen die Gestaltung vornimmt und hier nicht weiter
ausgeführt werden soll. Dann finden Sie die externe JavaScript-Datei zum Erzeugen eines
XHR-Objekts, wie wir es gerade besprochen haben, und die Referenz auf die zweite ex-
terne JavaScript-Datei ajax_1.js. Diese enthält die Funktionalität, um Daten auf Basis
eines XHR-Objekts asynchron vom Webserver anzufordern und diese über die Ajax-En-
gine in die Webseite zu integrieren, ohne eine neue Webseite laden zu müssen. Beachten
Sie, dass alle eingebundenen JavaScript-Dateien sowie in der Webseite selbst befindliche
JavaScript-Container dem gleichen Scope zugeordnet sind (wir werden in den folgen-
den Beispielen grundsätzlich so eine Verteilung der externen JavaScript-Dateien und des
310 13 Asynchrone Programmierung

internen Skriptbereichs vornehmen). Dies ist die externe JavaScript-Datei zur Daten-
anforderung per Ajax:

var xhr = erzXHRObjekt();


function sndReq() {
  [Link]('get', 'serverantworten/ajax_1.txt', true);
  [Link] = function() {
    if ([Link] == 4) {
      [Link]("antwort").innerHTML = [Link];
    }
  };
  [Link](null);
}
[Link] = function() {
  [Link]("frage").onclick = sndReq;
};

Die externe JavaScript-Datei enthält zuerst den Aufruf für die Erzeugung des XHR-Ob-
jekts, das in einer globalen Variablen referenziert wird. Wir verwenden dazu die uni-
verselle Funktion zum Erzeugen eines XHR-Objekts aus der anderen externen Java-
Script-Datei. Darüber werden in den nachfolgend definierten Funktionen Daten vom Ser-
ver angefordert und mit JavaScript wieder bereitgestellt. Beachten Sie den Eventhandler
[Link]("frage").onclick = sndReq;. Damit wird bei einem Klick
auf die Schaltfläche die JavaScript-Funktion sndReq aufgerufen. Darin wird zuerst eine
HTTP-GET-Verbindung zum Webserver durch [Link] initialisiert. Der erste Parameter
in der open-Methode spezifiziert die Art der HTTP-Anforderung. In unserem Fall wird
die GET-Methode gewählt. Der zweite Parameter ist die Zieladresse der Anfrage. Dies
ist in diesem Fall eine gewöhnlicher URL auf eine statische Textdatei (eine relative Pfad-
angabe). Der dritte Parameter gibt an, ob die Kommunikation asynchron erfolgen soll
oder nicht. Der Wert true gibt an, dass die Kommunikation asynchron laufen soll. Sobald
die HTTP-Antwort beim Client wieder eintrifft, muss man nun darauf reagieren können.
Dazu definieren wir eine Funktion zum Umgang mit der Antwort. Diese wird allgemein
vor dem Aufruf der send-Methode an die onreadystatechange-Eigenschaft des XHR-Ob-
jekts gebunden. Die hier angegebene anonyme Callback-Funktion wird bei jeder Status-
änderung der HTTP-Anfrage durch den Server ausgeführt.
Im Callback wird einfach mit der Eigenschaft readyState ein bestimmter Status der
Datenübertragung überprüft. Der Wert 4 steht, wie wir besprochen haben, für COMPLE-
TED (vollständig)2. Wenn also die angeforderten Daten vollständig übertragen wurden,
greifen wir mit [Link] auf das Element der Webseite zu, dem der

2 Mankann auch auf den Statuscode des Servers prüfen, was wir an späterer Stelle noch machen
werden.
13.3 Ajax & Web 2.0 311

Parameter id mit dem Wert antwort zugeordnet ist. Das ist in der Webseite der <div>-
Container. Das Attribut innerHTML erlaubt den Zugriff auf den Inhalt von diesem Con-
tainer. Wir arbeiten hier also im Grunde mit reinem DHTML – nur die Daten kommen
dynamisch nachgeladen vom Server. Der Wert wird über das Objekt xhr zugewiesen.
Dieses Objekt enthält über die Eigenschaft responseText die Antwort des Webservers.
Die angeforderte Datei besteht nur aus reinem Text (auf den Abdruck kann guten Ge-
wissens verzichtet werden). Ein Blick auf einen Sniffer zeigt, dass die Daten tatsächlich
beim Klick auf die Schaltfläche nachgeladen werden. Beachten Sie, dass im Sniffer unter
Umständen nur dann etwas zu sehen ist, wenn die Daten nicht schon einmal geladen und
vom Browser im Cache zwischengespeichert wurden.

[Link] Textdateien intelligent auswählen und Senden von HTML-


Fragmenten
Sie brauchen natürlich nicht auf die intelligente Steuerung und die gezielte Auswahl von
nachzuladenden Informationen zu verzichten, wenn Sie per Ajax nur reine Textdateien
vom Webserver nachladen. Sie führen bloß die gesamte Logik auf dem Client der Ap-
plikation in den JavaScript-Dateien auf dem Client, statt Logik auf serverseitige Skripte
bzw. Programme zu verlagern oder Logik in das Datenübertragungsformat zu packen.
Betrachten Sie das nachfolgende Beispiel, von dem wir uns zuerst die Teile der HTML-
Datei ajax_2.html ansehen wollen, die sich signifikant geändert haben:


    <script src="lib/js/ajax_2.js" type="text/javascript"></script></
head>
  <body><h1>Zusatzinformationen</h1>
    <table><tr><td><img src="images/[Link]" /></td>
        <td><img src="images/[Link]" /></td>
      <td><img src="images/[Link]" /></td></tr>
    <tr><td id="info0"></td><td id="info1"></td><td id="info2"></td></tr>
    </table></body></html>

Sie haben hier eine Webseite mit mehreren Bildern in einer Tabelle, bei denen Event-
handler in der externen JavaScript-Datei registriert werden. Mit den aufgerufenen Funk-
tionen sollte beim Überstreichen eines Bilds mit der Maus in der Zelle darunter eine Zu-
satzinformation angezeigt werden. Wenn der Mauszeiger den Bereich wieder verlässt,
wird der Anzeigebereich gelöscht (Abb. 13.2).
Hier sehen Sie die JavaScript-Datei (ajax_2.js):

var xhr = erzXHRObjekt();


function sndReq(i) {
  switch(i) {
    case 0: [Link]('get', 'serverantworten/[Link]', true); break;
    case 1: [Link]('get', 'serverantworten/[Link]', true); break;
312 13 Asynchrone Programmierung

Abb. 13.2 Zusatzinformationen zu einem Bild

    case 2: [Link]('get', 'serverantworten/[Link]', true); break;


  }
  [Link] = function() {
    handleResponse(i);
  }
  [Link](null);
}
function handleResponse(i) {
  if ([Link] == 4) {
    [Link]("info" + i).innerHTML = [Link];
  }
}
function out() {
  for ( i = 0; i < 3; i++) {
    [Link]("info" + i).innerHTML = "";
  }
}
function init() {
  with (document) {
    getElementsByTagName("img")[0].onmouseover = function() {
      sndReq(0);
    }
    getElementsByTagName("img")[1].onmouseover = function() {
      sndReq(1);
    }
    getElementsByTagName("img")[2].onmouseover = function() {
      sndReq(2);
13.3 Ajax & Web 2.0 313

    }
    getElementsByTagName("img")[0].onmouseout = out;
    getElementsByTagName("img")[1].onmouseout = out;
    getElementsByTagName("img")[2].onmouseout = out;
  }
}
[Link] = init;

Die JavaScript-Datei enthält nun ein paar sehr interessante Stellen. Beachten Sie zuerst,
dass wir in der init-Funktion anonyme Funktionen bei dem Eventhandler registrieren.
Wir können damit der Funktion sndReq einen Parameter übergeben. Auf jedem Bild-
bereich wird ein spezifischer Parameter der Funktion übergeben. Dieser dient zur Steue-
rung, welche Information wo angezeigt wird. Die Tabelle enthält dazu in der zweiten
Zeile Zellen mit jeweils einer spezifischen Id. Diese Id ist nach einem gewissen Muster
vergeben, das eine Unterscheidung über einen einfachen numerischen Algorithmus er-
laubt, der an dem übergebenen Parameter an die Funktion sndReq orientiert ist. Da wir
der Funktion einen Parameter übergeben, können wir im Inneren der Funktion diesen
Parameter dazu nutzen, gezielt eine spezifische Textdatei auszuwählen, die je nach Para-
meter angefordert wird. Dazu verwenden wir einfach switch-case.

 Die Namen der angeforderten Dateien könnten Sie übrigens auch über einen
analogen numerischen Algorithmus zusammensetzen, der etwas weiter unten
zum automatischen Bestimmen der Id verwendet wird. Das Listing ist hier nur
etwas allgemeiner formuliert.

Die angeforderten Textdateien enthalten übrigens zum Teil HTML-Fragmente, wie Sie
an der Ausgabe erkennen können. Diese HTML-Anweisungen werden dann beim Nach-
laden der Textdatei vom Browser direkt interpretiert. Dies eröffnet alle Möglichkeiten
zur Gestaltung der Antwort des Webservers sowie z. B. zum Nachladen von externen
Ressourcen über HTML-Referenzen. Die nächste interessante Stelle finden Sie beim Re-
gistrieren einer anonymen Funktion beim Eventhandler onreadystatechange. Damit kön-
nen wir der registrierten Funktion handleResponse einen Parameter übergeben und zwar
den Wert, der bereits an sndReq übergeben wurde. Dieser wird in der Funktion handle-
Response zum Zusammensetzen der Id genutzt, welche die Tabellenzelle spezifiziert, in
der die Antwort des Servers angezeigt werden soll. Diese Id war ja bewusst so gewählt,
dass man sie über einen einfachen numerischen Algorithmus aus dem Übergabepara-
meterwert an sndReq bestimmen kann. Die Funktion zum Löschen der Anzeigebereiche
leert einfach alle Anzeigebereiche. Wir brauchen hier keine individuelle Logik! Sie sehen
an diesem Beispiel, dass Sie bereits mit reinen statischen Textdateien auf dem Server
eine komplexe Logik in die Ajax-Applikation einbauen können. Man muss diese Logik
eben nur in JavaScript implementieren und auf dem Client ausführen.
314 13 Asynchrone Programmierung

[Link] Übergabe von Werten an den Webserver


Schauen wir uns nun ein Beispiel an, in dem Daten bei einer Ajax-Anfrage an den Web-
server übergeben werden sollen und die Antwort sich erst aufgrund der übergebenen
Daten ergibt. Die Antwort soll wie im zweiten Beispiel auch zusätzlich HTML-Frag-
mente enthalten. Konkret nehmen wir eine Eingabe aus einem Webformular und reichen
diese an den Webserver weiter, der sie etwa mit einem PHP-Skript oder der von uns bei
dem [Link]-Webserver implementierten Logik verarbeitet. Dieses wird eine Antwort
schicken, die sich aber für den Client in nichts von dem unterscheiden wird, was eine
statische Textdatei liefert. Das ist der Teil der HTML-Datei, der sich signifikant von den
Vorgängern unterscheidet (ajax_3.html). Es wird einfach ein Webformular und einen lee-
ren <div>-Container als Bereich für die Antwort der Ajax-Anfrage bereitstellt:


    <script src="lib/js/ajax_3.js" type="text/javascript"></script></
head>
  <body><h1>Geben Sie Ihren Namen ein</h1>
    <form>Name: <input type="text" id="n" /></form><hr />
    <button id="frage">OK</button><div id="antwort"></div></body></
html>

In der externen JavaScript-Datei zur Kommunikation mit dem Webserver müssen wir nur
ein paar wenige Stellen verändern (ajax_3.js):

var xhr = erzXHRObjekt();


function sndReq() {
  [Link]('get', 'serverantworten/[Link]?n=' +
    encodeURIComponent([Link]("n").value), true);
  [Link] = function() {
    if ([Link] == 4) {
      [Link]("antwort").innerHTML = [Link];
    }
  };
  [Link](null);
}
[Link] = function() {
  [Link]("frage").onclick = sndReq;
};

In der open-Methode wird einfach ein Pseudo-URL aus einem Namen für das Ein-
gabefeld, über den der Wert auf dem Server entgegengenommen werden kann (n),
und dem Wert zusammengesetzt (über die Id abgefragt). Zusätzlich wird der Wert mit
encodeURIComponent umgewandelt, um Sonderzeichen zu maskieren. Das war alles.
Die Anfrage wird an eine Dummy-Datei geschickt, die aber durch den Query aufgrund
13.3 Ajax & Web 2.0 315

der individuellen Logik unseres [Link]-Webserver ignoriert wird. Man kann aber auch
etwa an ein ein passendes PHP-Skript die Anfrage schicken, wenn man mit Apache
arbeitet oder sonst eine individuelle Logik auf Serverseite ausbauen.

[Link] Nachfordern von XML-Daten


Wenn XML schon in der ausgeschriebenen Form von Ajax drinsteckt, können Sie natür-
lich XML vom Server als Antwort auf eine Ajax-Anfrage fordern. Die XML-Daten kön-
nen wie auch HTML-Fragmente sowohl in Form einer dynamisch generierten Antwort
als auch in einer statisch auf dem Server zum Download bereitgestellten XML-Datei zur
Verfügung stehen. Das spielt für die Verwertung der Daten auf dem Client in der Folge
keine Rolle. In der open-Methode muss bloß eine Referenz auf den URL der Datei no-
tiert werden, die die Antwort generiert, oder eine Referenz auf den URL der zum Down-
load verfügbaren statischen XML-Datei. Wenn Sie nun bei einer Ajax-Anfrage dem
Client XML-Daten senden wollen, ist die rein auf die Ajax-Anfrage bezogene Arbeit auf-
seiten des Servers also trivial, zumindest was die reine Versendung der Daten nach der
Ajax-Anfrage vom Webserver zum Client angeht – die Erstellung der XML-Daten kann
komplexeste Prozesse erfordern. Es gibt aber sonst kaum einen Unterschied zu dem Fall,
dass Sie HTML oder einen Text anfordern wollen. Betrachten Sie die nachfolgenden Ab-
wandlungen unserer Standardfunktion, die die Datei [Link] anfordert:


    [Link]('get', 'serverantworten/[Link]',true);

Ansonsten entspricht das Beispiel in allen relevanten Bereichen unserer ersten Version
ajax_1.js.

[Link].1 Direktes Entgegennehmen und Verarbeiten von XML-Daten


Wenn Sie nun die Daten in XML-Form erhalten, müssen Sie diese im Client entgegen-
nehmen und adäquat verarbeiten. Sie können zum Beispiel mit [Link]-
ById("…").innerHTML = [Link]; in die Webseite übernommen werden.
Hier ist rein gar kein Unterschied zu Fällen zu sehen, in denen HTML oder Text ge-
sendet wird. Die Vorteile liegen bisher rein beim Server, auf dem Sie sämtliche Möglich-
keiten von XML zur Abbildung von Geschäftslogik ausreizen können. Betrachten wir
noch die XML-Datei, damit deutlich wird, was da vom Server kommt ([Link]):

<?xml version="1.0" encoding="UTF-8" ?>


<webseiten><seite><titel>RJS EDV-KnowHow</titel><url>[Link]
url>
     <bemerkung>Homepage</bemerkung></seite>
   <seite><titel>RJS-Blog</titel><url>[Link]
     <bemerkung>Blog</bemerkung></seite>
316 13 Asynchrone Programmierung

   <seite><titel>Fliegerblog</titel><url>[Link]
url>
     <bemerkung>Flugtagebuch</bemerkung></seite></webseiten>

Wenn Sie die XML-Daten allerdings einfach so in die Webseite befördern, werden Ihnen
nur die Inhalte der XML-Datei unformatiert aneinandergereiht dargestellt. Die Elemente
selbst sehen Sie nicht. Genau genommen werden die XML-Elemente als unbekannte
Tags verstanden. Wenn die XML-Elemente nun zufällig oder bewusst wie HTML-Ele-
mente genannt werden, werden sie interpretiert und ausgeführt. Andernfalls werden sie
als unbekannte HTML-Tags nach dem Prinzip der Fehlertoleranz ignoriert. Deshalb
sehen Sie nur die Inhalte der XML-Elemente. An eventuelle Attribute, die bei den Ele-
menten angegeben wurden, kommen Sie auf diese Weise überhaupt nicht. Ein Blick in
die Entwicklertools eines Browsers zeigt aber die tatsächliche Struktur, wie sie im Brow-
ser vorliegt. Wir könnten nun den Versuch wagen und die Namen der XML-Elemente in
die CSS-Datei aufnehmen. Wir würden also neben den HTML-Elementen, die von An-
fang an in der Webseite vorhanden waren (oder auch per Ajax nachgeladen werden), dem
Browser unbekannte Elemente zur Formatierung anbieten, etwa so, um einen Auszug aus
der Datei [Link] zu zeigen:


seite{ display:block; background:yellow; }
titel, url, bemerkung{ display:inline-block; width:250px; }

Wenn Sie sich das im Browser ansehen, sieht das vielversprechend aus (Abb. 13.3).
Dennoch bleibt bei der Formatierung über CSS das Problem, dass Sie ausschließlich
an die Inhalte der Elemente kommen, aber wie gesagt nicht an eventuell vorhandene At-
tribute oder die Elementnamen. Von daher bleibt oft sowieso nur die gezielte Navigation
auf dem DOM-Baum des nachgeforderten XML-Dokuments.

[Link].2 Die Navigation auf dem XML-Baum


Die Verwendung von XML-Daten mit responseText im Client ist also nicht der Regel-
fall. Sie geben damit eigentlich sämtliche Vorteile und Gründe auf, warum Sie über-
haupt strukturierte Informationen in Form von XML nachladen! Sie werden in der Regel
mit den Daten, die Sie im Client erhalten, auf andere Weise geeignet umgehen wollen.
Dazu steht Ihnen das Objekt node und responseXML als Eigenschaft eines XHR-Ob-
jekts zur Verfügung. Gerade wenn Sie vom Server Daten im XML-Format erhalten, ist
die Möglichkeit der differenzierten Auswertung der Antwort im Client gegeben. Statt
pauschal einen Bereich der Webseite mit der kompletten Antwort zu füttern, suchen Sie
auf dem Client den Teil aus einer XML-Antwort heraus, der an eine ganz bestimmte
Stelle gehört. Aus Sicht von Ajax ist es besonders interessant, dass das XHR-Objekt
selbst einen Knoten liefert, wenn Sie die responseXML-Eigenschaft betrachten. Dies
ist der Wurzelknoten des XML-Dokuments, das als Antwort vom Server geliefert wird.
13.3 Ajax & Web 2.0 317

Abb. 13.3 Die XML-Daten wurden formatiert dargestellt

Ausgehend davon können Sie die Attributknoten, Textknoten und weitere Kindknoten
eines Knotens in der Antwort des Servers ansprechen. Selbst wenn der Server HTML-­
Fragmente als Antwort schickt, können Sie diese in der Regel über die responseXML-
Eigenschaft im Client verwerten und dann auf dem resultierenden Knoten die nach-
folgenden Techniken einsetzen. Verwenden wir eine Abwandlung des letzten Beispiels,
um XML-Daten anzufordern und darauf im Client zu navigieren. In der neuen HTML-
Datei ajax_5.html müssen Sie bloß gegenüber ajax_4.html die externe JavaScript-Datei
ajax_5.js referenzieren, die Sie hier sehen:


   [Link] = function() {
     if ([Link] == 4) {
       var erg = [Link];
       [Link]("antwort").innerHTML =
         [Link]("titel")[0].[Link] + "<br />" +
         [Link]("url")[0].[Link] + "<hr />" +
         [Link]("titel")[1].[Link] + "<br />" +
         [Link]("url")[1].[Link];
     }
318 13 Asynchrone Programmierung

  };
   [Link](null);
}

In dem Beispiel navigieren wir auf einem node-Objekt, das über responseXML geliefert
wird. Dabei nutzen wir die schon bekannten Methoden und Eigenschaften des node-Ob-
jekts, wie wir sie schon an diversen Stellen im Buch verwendet haben, nur eben auf einem
auf XML basierenden node-Objekt statt einem erzeugenden HTML-Fundament. Aller-
dings kann diese Navigation in älteren Browsern Probleme machen. Den meisten Proble-
men gehen Sie aus dem Weg, wenn Sie zur Selektion eines Elements in der Serverantwort
mit getElementById oder getElementsByTagName arbeiten und konkret damit den Knoten
selektieren, den Sie aktuell in einem Schritt verarbeiten (Textinhalt oder Attributwerte)
wollen. Über die Knoteneigenschaft firstChild erhalten Sie den Textknoten eines Element-
knotens und dessen Eigenschaft data ist der Inhalt. Genau das nutzen wir hier. Auf Attri-
bute und deren Werte greifen Sie mit getAttributeNode und dann der Eigenschaft nodeVa-
lue zu. Sie können auch mit innerHTML etc. arbeiten (Abb. 13.4). Im Grunde können Sie
alle Techniken anwenden, die Sie bei der Navigation auf einem DOM-Baum kennen.

Abb. 13.4 Gezielter Zugriff auf Elementinhalte von XML-Daten


13.3 Ajax & Web 2.0 319

[Link] JSON-Daten anfordern und verarbeiten


Wie erwähnt, bietet JSON Funktionalität und Flexibilität wie XML, ohne dessen
Komplexität und problematische Auswertung in älteren Browsern. Aus diesem Grund
hat sich JSON als Standardformat bei RIAs durchgesetzt, wenn tatsächlich eine gewisse
Funktionalität zur Auswertung der Antwort im Client gefordert wird. Wir wollen nun
als Basis eine JSON-Struktur verwenden, die die Informationen aus der XML-Datei im
vorletzten Beispiel reflektiert ([Link]) – die [Link] ist üblich, aber für die
Datennachforderung per Ajax irrelevant:

{ "webseiten": { "seite": [ {
        "titel": "RJS EDV-KnowHow", "url": "[Link]
        "bemerkung": "Homepage"
      }, { "titel": "RJS-Blog", "url": "[Link]
        "bemerkung": "Blog"
      }, { "titel": "Fliegerblog", "url": "[Link]
        "bemerkung": "Flugtagebuch" } ] } }

Sie sehen auch in der JSON-Struktur ein Root-Element webseiten, das als Inhalt ein
Array mit Namen seiten enthält. Und jedes Element im Array seiten besteht aus den
Elementen titel, url und bemerkung. Die Webseite ajax_6.html bleibt bis auf irrelevante
Änderungen gleich. Von Interesse ist nur die hier jetzt referenzierte JavaScript-Datei
ajax_6.js:

var xhr = erzXHRObjekt();


function sndReq() {
   [Link]('get', 'serverantworten/[Link]', true);
   [Link] = function() {
     if ([Link] == 4) {
       var obj = [Link]([Link]);
       [Link]("antwort").innerHTML =
         [Link][0].titel + "<br/>" + [Link][0].url
         + "<hr/>" + [Link][1].titel + "<br/>" +
         [Link][1].url + "<hr/>" + [Link][2].titel
         + "<br/>" + [Link][2].url;
     }
   };

Sie sehen, dass wir im Fall von JSON wieder responseText verwenden – im Gegensatz
zu der Verwertung von XML. Sie sehen aber auch, dass wir über die Klasse JSON einen
Parser verwenden, der aus dem JSON-String ein Objekt erzeugt. Denn indem wir diese
Funktion auf die JSON-Struktur in responseText anwenden, erhalten wir ein Objekt,
320 13 Asynchrone Programmierung

auf dem wir navigieren können. Die verschachtelten JSON-Strukturen sind in so einem
Objekt über eine gewöhnliche Punktnotation zugänglich, einfach und vollkommen ana-
log zur normalen objektorientierten Vorgehensweise. Sie gehen also vom Root-Element
webseiten zum entsprechenden Array-Element (über den Index) und dann zu dessen
Inhaltselement, das Sie interessiert (Abb. 13.5).

[Link] Header-Auswertung
Es kann interessant sein, wenn man die vom Server gesendeten Header-Felder auswertet.
Das kann man bei einer Ajax-Anfrage gut machen, wenn man die Methode getAllRe-
sponseHeaders verwendet ([Link]).


     <script src="lib/js/ajax_7.js" type="text/javascript"></script></
head>
  <body><h1>Klicken Sie auf den Button</h1><button id="frage">OK</
button>
      <div id="antwort"></div></body></html>

Abb. 13.5 JSON-Daten lassen sich noch einfacher und zuverlässiger wie XML-Daten im Brow-
ser verarbeiten
13.4 Deferred Object und Promises 321

Das ist der JavaScript-Code (ajax_7.js):

const xhr = erzXHRObjekt();


function ajaxRequest(url, method) {
   return new Promise((resolve, reject) => {
     [Link](method, url);
     [Link] = function() {
     if ([Link] >= 200 && [Link] < 300) {
       resolve([Link]);
     } else {
       reject(new Error('Anfrage fehlgeschlagen mit Status ${[Link]}'));
     }
     };
     [Link] = function() {
       reject(new Error('Fehler'));
     };
     [Link]();
   });
   }
function init() {
   [Link]("frage").onclick = function(){
     ajaxRequest('[Link]
'GET')
      then((response) => {
       [Link]('Daten erfolgreich geladen:', response);
           [Link]("antwort").innerHTML = response;
       // Verarbeite die Antwort hier weiter
     })
      catch((error) => {
       [Link]('Fehler beim Laden der Daten:', [Link]);
     });
   };
}
[Link]=init;

Je nach verwendetem Server erhalten Sie eine Menge aussagekräftiger Daten wie hier
bei unserem selbstprogrammierten [Link]-Webserver (Abb. 13.6).

13.4 Deferred Object und Promises

Bei asynchroner Kommunikation wie bei Ajax oder Web Workern kommt es unter Um-
ständen zu „Verzögerungen“, denn ein angefragter „Server“ antwortet oft nicht unmittel-
bar, sondern wenn es die Bedingungen gestatten. Oder durch Engpässe im Netzwerk
322 13 Asynchrone Programmierung

Abb. 13.6 Daten, die ein Server sendet

kommt eine Antwort verzögert an. Darauf muss man reagieren. Deferred Objects und
Promises sind Konzepte, die in JavaScript verwendet werden, um asynchrone Opera-
tionen zu verwalten und zu steuern. Sie ermöglichen es, besser mit asynchronen Code-
Strukturen umzugehen und vermeiden dabei sogenannte „Callback-Höllen“. Das so-
genannte „verzögerte Objekt“ (Deferred Object) ist eine Möglichkeit. Es ist ein verkett-
bares Dienstprogrammobjekt. Es kann mehrere Callbacks in Callback-Warteschlangen
registrieren, Callback-Warteschlangen aufrufen und den Erfolgs- oder Fehlerstatus einer
synchronen und vor allen Dingen auch asynchronen Funktion weiterleiten. Eine Promise
ist ein Objekt, das eine asynchrone Operation darstellt und entweder den Abschluss oder
das Scheitern dieser Operation darstellt. Es repräsentiert einen Wert, der möglicherweise
jetzt, später oder niemals verfügbar sein kann.

13.4.1 Promises

Wenn man verzögerte Aktionen betrachtet, kommt man in modernem JavaScript fast un-
weigerlich zu Promises. Promises bieten allgemein einen Mechanismus zum Verfolgen
des Status einer asynchronen Aufgabe mit bedeutend besserer Robustheit und weniger
13.4 Deferred Object und Promises 323

Chaos als reine Eventhandler. Die Syntax zur Umsetzung in JavaScipt ist dabei aus mei-
ner Sicht eine Mischung aus einer Entscheidungsstruktur und Ausnahmebehandlung.
Erst einmal braucht man ein Promise-Objekt und das wird einfach mit dem Konstruktor
erzeugt, dem ein Callback übergeben wird. Dieser bekommt selbst zwei Callback-Para-
meter übergeben. Diese beiden Callbacks stehen für den Erfolgsfall und den Fehlerfall.
Man kann sie in dem Konstruktor redefinieren, muss es aber nicht. Schematisch sieht das
in der Regel so aus:

Beispiel

var mypromise = new Promise(function(resolve, reject){


// Asynchroner Code – Aufruf resolve wenn die Aufgabe erfolgreich
erledigt wurde
// Aufruf reject wenn die Aufgabe fehlgeschlagen ist
}) ◄

Die Promise-Syntax bietet einen sauberen und lesbaren Stil für asynchrone Operatio-
nen, indem sie eine Kette [Link]-Methoden mit optionalem catch ermöglichen, um auf
den Erfolg oder das Scheitern einer Operation zu reagieren. Promises können auch mit
async/await verwendet werden, um asynchronen Code synchroner aussehen zu lassen.
Das Promise-Objekt wird in der Praxis meist für asynchrone Aktionen wie den Um-
gang mit Web Workern oder Ajax-Anfragen verwendet. Ein Promise kann sich in einem
von drei oder vier (je nach Quelle) Zuständen befinden:

• pending: Der initiale Status – weder fulfilled noch rejected.


• fulfilled: Die Operation war erfolgreich und die Aufgabe wird als "erfüllt" betrachtet.
• rejected: Die Operation ist gescheitert und die Aufgabe wird als "abgelehnt" be-
trachtet.
• Ein weiterer Begriff beschreibt in einigen Konzepten den Zustand settled (erledigt):
Der Promise ist entweder fulfilled oder rejected, aber nicht pending. In jQuery wird
dieses Zustand aber nicht auftauchen.

Ein Promise mit dem Status pending kann entweder zu fulfilled mit einem Wert oder re-
jected mit Fehlergrund werden. Wenn einer dieser Fälle eintritt, werden die assoziierten
Handler aufgerufen. Ist ein Promise bereits in dem Zustand fullfilled oder rejected und
wird erst dann ein entsprechender Handler hinzugefügt, dann wird dieser Handler auf-
gerufen.
Wann immer Sie ein Promise-Objekt instanziieren, stehen Ihnen darüber dann zwei
Methoden zur Verfügung, die daran gekettet werden können:

• Die Methode then kann man aufrufen um anzugeben, was passiert, wenn die Aufgabe
erfolgreich gelöst wurde. Diese kann wie gesagt auch verkettet werden.
324 13 Asynchrone Programmierung

• Mit der Methode catch, deren Aufruf äquivalent zum Aufruf von then ist, kann man
vor allen Dingen Fehler bequem behandeln. Man kettet dazu den Aufruf von catch
einfach an den Aufruf von then hinten dran.

Damit können Sie entscheiden, was als nächstes nach dem Abschluss einer asynchronen
Aufgabe passiert. Angenommen, Sie wollen ein Bild laden und das machen Sie in einer
Methode. Dann ginge eine Zusicherung der nächsten Schritte im Erfolgsfall mit then
schematisch so:

Beispiel

getImage('[Link]').then(
  function(successurl){
    [Link]('info').innerHTML = 'Bild geladen';
    },
    function(errorurl){
      [Link]('info').innerHTML = 'Fehler beim Laden';
    }
); ◄

Soll eine Fehlerbehandlung mit catch erfolgen, geht das etwa schematisch so:

Beispiel

getImage('[Link]').then(function(successurl){
  [Link]('info').innerHTML = 'Bild geladen';
}).catch(function(errorurl){
  [Link]('info').innerHTML = 'Fehler beim Laden';
}) ◄

[Link] Ein einfaches Promise


Hier ist noch ein konkretes Beispiel für eine Promise unter [Link] ([Link]):

"use strict";
[Link]("Erzeuge Promise.");
const promise = new Promise((resolve, reject) => {
    // Asynchrone Operation, z.B. API-Aufruf
    setTimeout(() => {
      // Erfolgreiche Beendigung
      resolve('Daten erfolgreich geladen.');
      // Oder im Fehlerfall
      reject(new Error('Fehler beim Laden der Daten.'));
    }, 2000);
  });
13.4 Deferred Object und Promises 325

  [Link]("Promise in Wartestellung.");
  [Link]((result) => {
    [Link](result);
  }).catch((error) => {
    [Link](error);
  });  

Die Verzögerung wird natürlich simuliert. Beachten Sie, dass auch [Link] eine Methode
setTimeout kennt und das nicht (!) die DOM-Methode [Link] ist.
Das ist die Ausgabe nach erfolgreichem Abschluss der verzögerten Aktion:
Erzeuge Promise.
Promise in Wartestellung.
Daten erfolgreich geladen.

[Link] Ajax-Anfrage mit Promise


Hier ist ein Beispiel für eine Ajax-Anfrage unter Verwendung von Promises in Java-
Script, um Daten von einem Server abzurufen. Dabei verwenden wir auch teils eine
alternative Verarbeitung der Ajax-Anfrage (ajax_8.js):

const xhr = erzXHRObjekt();


function ajaxRequest(url, method) {
   return new Promise((resolve, reject) => {
     [Link](method, url);
     [Link] = function() {
     if ([Link] >= 200 && [Link] < 300) {
       resolve([Link]);
     } else {
       reject(new Error('Anfrage fehlgeschlagen mit Status ${[Link]}'));
     }
     };
   [Link] = function() {
     reject(new Error('Fehler'));
     };
     [Link]();
   });
   }
function init() {
   [Link]("frage").onclick = function(){
     ajaxRequest('serverantworten/ajax_1.txt', 'GET')
     .then((response) => {
       [Link]('Daten erfolgreich geladen:', response);
       [Link]("antwort").innerHTML = response;
     })
     .catch((error) => {
326 13 Asynchrone Programmierung

       [Link]('Fehler beim Laden der Daten:', [Link]);


     });
   };
}
[Link]=init;

In diesem Beispiel wird die Funktion ajaxRequest verwendet, um eine Ajax-Anfrage an


eine bestimmte URL mit einer bestimmten HTTP-Methode zu senden. Diese Funktion
gibt eine Promise zurück, die aufgelöst wird, wenn die Anfrage erfolgreich ist, und ab-
gelehnt wird, wenn ein Fehler auftritt. [Link]-Methode wird verwendet, um die erfolg-
reiche Antwort zu verarbeiten, während [Link]-Methode aufgerufen wird, um Fehler
zu behandeln, die während der Anfrage oder Verarbeitung auftreten könnten.

13.4.2 Deferred Objects

Deferred Objects bezeichnet ein Muster bzw. Pattern, das die Erstellung und Verwaltung
von Promises vereinfachen soll. Es handelt sich also im Grunde um ein „gewöhnliches“
Objekt, das aber als Eigenschaft ein Promise verwaltet. Im Wesentlichen stellt ein Defer-
red-Objekt eine Promise dar, die von externem Code manuell aufgelöst oder abgelehnt
werden kann. Das ermöglicht die manuelle Kontrolle über den Status und das Ergebnis
einer asynchronen Operation.

[Link] Beispiel für ein Deferred-Objek

"use strict";
function fetchData() {
  const deferred = {};
  [Link] = new Promise((resolve, reject) => {
    [Link] = resolve;
    [Link] = reject;
});
setTimeout(() => {
  [Link]('Daten erfolgreich geladen');
  // Oder im Fehlerfall
  [Link](new Error('Fehler beim Laden der Daten'));
  }, 2000);
  return deferred;
}
[Link]("Erzeuge Deferred Object");
const deferred = fetchData();
[Link]((result) => {
  [Link](result); // Ausgabe: Daten erfolgreich geladen
}).catch((error) => {
13.5 Web Worker 327

  [Link](error);
});
[Link]("Wir warten");

Beachten Sie, dass die Benennungen der Eigenschaft, aber auch Parameter der Callback-
Funktion zwar sprechend, aber willkürlich sind. Das Verhalten resultiert ausschließlich
aus dem Promise. Das ist die Ausgabe nach dem Abschluss der verzögerten Aktion:
Erzeuge Deferred Object.
Wir warten.
Daten erfolgreich geladen.
In der Praxis werden Promises häufiger verwendet als Deferred-Objekte, da Promises
einen standardisierten und einfacheren Ansatz bieten. Deferred-Objekte können jedoch
in speziellen Fällen nützlich sein, z. B. wenn Sie eine asynchrone Operation manuell
steuern müssen.

13.5 Web Worker

Bei Ajax kommunizieren ein Client via HTTP mit einem Webserver, aber man kann sich
auch vorstellen, dass so eine asynchrone, ereignisgesteuerte Kommunikation zwischen
zwei Skripten auf einem Rechner oder gar in einem Programm oder Skript ausgeführt
wird. Etwa mit Web Workern.

13.5.1 Was ist ein Web Worker?

Bei einem Web Worker handelt es sich laut ECMAScript um ein Skript, das im Hinter-
grund laufen kann. Der wesentliche Vorteil ist, dass so ein Skript die normale Benutzer-
interaktion beziehungsweise Webseite nicht blockiert. Es läuft fast wie im Fall von ech-
tem Multithreading parallel dazu ab.

Multithreading nennt man auch Nebenläufigkeit und der Begriff bezeichnet das gleichzeitige Ab-
arbeiten mehrerer Ausführungsstränge (Threads) innerhalb eines einzelnen Prozesses. Diese Aus-
führungsstränge sind nicht zwingend voneinander abgeschottet und müssen gegebenfalls vom Pro-
zess selbst synchronisiert werden. Bei einer scheinbar gleichzeitigen Abarbeitung von Aufgaben
kann auch durch geschickte (und meist schnelle) Wechsel zwischen sequentiell ausgeführte Auf-
gaben gewechselt werden, was insbesondere bei nur einem Prozessor oder allgemein einer nicht
multithreadingfähigen Umgebung auch so gemacht werden muss. Für den Anwender oder An-
wendungsprogrammierer ist dieses Hintergrundprozedere aber in der Regel weder erkennbar noch
interessant.

Es obliegt natürlich der Browser-Engine und der Plattform, ob das Verfahren als ech-
tes Multithreading durchgeführt wird und wie genau die parallelen Prozesse organisiert
und abgearbeitet werden. Was letztendlich für JavaScript-Programmierer gleichgültig ist,
328 13 Asynchrone Programmierung

da „oberhalb“ dieser internen Vorgänge programmiert wird. Natürlich muss aber voraus-
gesetzt werden, dass Web Worker überhaupt unterstützt werden.

13.5.2 Erzeugen von Web Workern

Erzeugt werden Web Workers ganz einfach als Objekte vom Typ der Klasse (oder ge-
nauer Prototyp) Worker. Man instanziiert also einfach den Prototypen mit einem Konst-
ruktor. Beispiel:

Beispiel

var th = new Worker("lib/js/worker/[Link]"); ◄

Als Parameter wird ein Skript übergeben, das im Hintergrund ausgeführt werden soll.
Dabei adressieren Sie das Worker-Skript relativ zur Position der Webseite bzw. dem
Wurzelskript! Die Anweisung zur Erzeugung von dem Objekt startet auch unmittelbar
den Worker-Prozess!

 Das Nachladen eines Skripts, was ja bei einem Web Worker stattfindet, ist in
ein Sicherheitskonzept eingegliedert. Wenn Sie etwa über das file-Protokoll
arbeiten, wird das Nachladen normalerweise unterbunden.

Was Sie konkret für eine Aufgabe an einen Web Worker übergeben, ist vollkommen frei-
gestellt. In der Regel wird es aber eine Aufgabe sein, die relativ zeitaufwändig ist und den
normalen Ablauf der Interaktion mit einem Anwender unnötig blockieren würde. Etwa
eine umfangreiche Berechnung. Sie können Web Worker schon beim Laden der Webseite
starten, aber in der Regel ist es sinnvoll, einen Web Worker wie eine klassische Funktion
aufgrund irgendeines Ereignisses (etwa den Klick auf eine Schaltfläche) zu starten.

 Aus einem Web Worker heraus haben Sie keinerlei Zugriff auf den DOM. Sie sind
auf pures JavaScript beschränkt. Das entspricht der Situation, die Sie ja auch bei
[Link] haben. Wenn Sie Aktionen durchführen wollen, die sich auf den DOM
auswirken sollen, müssen Sie Nachrichten an den Aufrufer des Workers schicken
und der muss sich darum kümmern.

13.5.3 Die Kommunikation mit einem Web Worker

Sie müssen sich beim Einsatz eines Web Workers darüber klar sein, dass Sie im Grunde
auch hier wie bei Ajax einen Masterprozess haben, der die normale Logik der Appli-
kation steuert (etwa die Anwenderaktionen überwacht), und einen parallelen Prozess
13.5 Web Worker 329

(Slave), der diesem Masterprozess zuarbeiten soll. Nur läuft der halt nicht auf dem Web-
server. Dabei wird der Masterprozess nicht nur den Worker meist irgendwie überwachen,
steuern und ggfls. beenden – diese beiden Prozesse müssen in der Regel auch mit einan-
der kommunizieren. Die Kommunikation mit so einem Hintergrundprozess erfolgt mit
einem JavaScript-Eventhandler onmessage, der onstatereadychange bei Ajax ähnlich ist
und bei dem wie üblich ein Callback registriert wird.

Beispiel

[Link] = function(ev){…}; ◄

Wenn von dem Web Worker ein Ergebnis geliefert wird, wird die entsprechend referen-
zierte Funktion ausgeführt. Sollte der Callback einen Parameter haben, ist darüber ein
Mitteilungsobjekt verfügbar.

13.5.4 Kommunikation mit einem Web Worker

Das Worker-Objekt selbst kann nun mittels postMessage Nachrichten an seinen Er-
zeuger versenden und von diesem auch erhalten. Dazu dient ein Parameter, der der Me-
thode übergeben werden kann. Wenn man vom Masterprozess aus Daten an den Wor-
ker übergeben will, notiert man die Methode postMessage im Masterprozess selbst über
das vorangestellte Objekt des Workers. Im Worker kann man die Methode ohne voran-
gestelltes Objekt notieren oder this bzw. self verwenden.

13.5.5 Einen Worker mit terminate beenden

Ein gestarteter Web Worker kann auf Befehle und Nachrichten von seinem Master-
prozess warten. Deshalb muss man einen Web Worker in der Regel wieder explizit
anhalten – sofern er nicht so programmiert ist, dass es nach einer gewissen Aufgabe sich
selbst beseitigt. Wir werden das in einem Beispiel sehen. Um einen Hintergrundprozess
anzuhalten beziehungsweise zu beenden, stellen Worker-Objekte die Methode terminate
zur Verfügung.

13.5.6 Beispiele zu Web Worker

Kommen wir zu ein paar Varianten, wie man Web Worker einsetzen kann. Diese werden
in modernen Browsern unterstützt. Die Art der Nutzung von Web Workers ist nur durch
Ihre Fantasie vorgegeben. Hier finden Sie also nur ein paar Anregungen, wie man da vor-
gehen kann.
330 13 Asynchrone Programmierung

[Link] Ein einfacher Worker


Betrachten wir das folgende Beispiel, das in den meisten Browsern neuer Versionen
läuft, wobei Sie die Webseite über einen Webserver und das HTTP(s)-Protokoll aufrufen
sollten. Die Webseite heißt ww_1.html:


     <link rel="stylesheet" type="text/css" href="lib/css/[Link]" />
    <script type="text/javascript" src="lib/js/ww_1.js"></script></head>
  <body><h1>Web Worker</h1><button>Starte Hintergrundprozess</button>
     <button>Ende Hintergrundprozess</button>
     <button>Weitere Webseitenaktivität</button>
     <div id="antwort1"></div><div id="antwort2"></div></body></html>

Über den ersten Button wollen wir einen Hintergrundprozess starten, der über den zweiten
Button wieder angehalten werden kann. Die dritte Schaltfläche soll eine parallel weiter
mögliche Interaktion mit dem Anwender demonstrieren. Wir werden darüber parallel zur
Ausführung des Hintergrundprozesses Daten vom Anwender entgegennehmen und in der
Webseite ausgeben. Die CSS-Datei ist irrelevant und sorgt nur für etwas Gestaltung. In der
referenzierten JavaScript-Datei ww_1.js wird das Worker-Objekt erzeugt und gesteuert.

var th = null;
function init() {
  [Link]("button")[0].onclick = function() {
    th = new Worker("lib/js/worker/[Link]");
    [Link] = function(ev) {
      [Link]("antwort1").innerHTML = [Link];
    };
    [Link] = function(ev) {
      [Link]("antwort1").innerHTML = [Link];
    };
  };
  [Link]("button")[1].onclick = function() {
    [Link]();
    alert("Ende Web Worker");
  };
  [Link]("button")[2].onclick = function() {
    [Link]("antwort2").innerHTML = [Link]();
  };
}
[Link] = init;

Wie beschrieben wird ein Worker-Objekt erzeugt und zwar dann, wenn man in der Web-
seite auf die erste Schaltfläche klickt. Die Referenz auf den Web Worker ist in einer
globalen Variablen th abgelegt. Darüber wird eine anonyme Funktion mit onmessage bei
13.5 Web Worker 331

diesem Objekt registriert. Diese wird die vom Web Worker zurückgegebenen Daten in
der Webseite anzeigen. Dazu dient der Parameter der anonymen Funktion, der in diesem
Konzept über data die Antwort enthält. So ein Standardverhalten finden Sie ja in Java-
Script sehr oft. Vollkommen analog implementieren wir mit onerror eine Fehlerreaktion.
Solch ein Fehlereignis hat drei interessante Eigenschaften.

• In message steht eine lesbare Beschreibung des Fehlers,


• in filename der Name des Skripts mit dem Fehler und
• in lineno die Zeilennummer, wo der Fehler aufgetreten ist.

 Tipp Statt mit onmessage können Sie zur Registrierung eines Callback auch
mit addEventListener für das Event message oder das Event error arbeiten. Das
ist gleichwertig. Beispiel:

[Link]('message', function(ev) {

}, false);

Bei der Ereignisbehandlung für den Klick auf die zweite Schaltfläche sehen Sie die Be-
endigung des Hintergrundprozesses über die Methode terminate. Da der Web Worker
in einer globalen Variablen th referenziert wird, können sie darüber auf den Web Wor-
ker zugreifen. Die Reaktion auf die dritte Schaltfläche zeigt in diesem Beispiel ziemlich
deutlich, dass der Hintergrundprozess auch weiterläuft, wenn eine andere Aktion auf der
Webseite ausgeführt wird. Hier ist das Listing des Quellcodes [Link], der
die Funktionalität des Web Workers beschreibt:

var i = 0;
var zufall = 0;
while (true) {
  i++;
  zufall = [Link]();
  postMessage("Schritt " + i + ", Wert der Zufallszahl: " + zufall);
  if (i > 1000000) {
    postMessage("Fertig");
    break;
  }
}

Das Skript macht nicht viel Spannendes. In einem Zufallsprozess werden Zahlen er-
mittelt und das Skript abgebrochen, wenn ein Counter größer als 1.000.000 ist. Die ein-
zig interessanten Stellen sind die mit Rückgabe einer Meldung an den Aufrufer des Web
Workers mit postMessage. Diese Meldung an den Aufrufer des Web Workers beendet
den Hintergrundprozess nicht (Abb. 13.7).
332 13 Asynchrone Programmierung

Abb. 13.7 Ein Hintergrundprozess bei der Arbeit – trotzdem kann der Anwender mit der Web-
seite interagieren

 Beachten Sie, dass Sie während der Arbeit des Web Worker natürlich nicht perma-
nent Meldungen zurückgeben müssen, ganz im Gegenteil. Nur zeigt diese perma-
nente Ausgabe in dem Beispiel deutlich, wie Web Worker arbeiten, und sie macht
die Ausführung des Hintergrundprozesses ausreichend zeitintensiv, dass Sie etwas
erkennen können. Bei vielen Aufgaben in der Praxis wird es jedoch ausreichend
sein, wenn die Methode postMessage zur Rückgabe eines Ergebnisses nur einmal
am Ende des Algorithmus aufgerufen wird. Theoretisch muss die Methode post-
Message gar nicht aufgerufen werden, wenn die Arbeit des Web Worker ohne eine
Ergebnismeldung erledigt werden kann. Das ist aber eher unüblich.

[Link] Daten austauschen und Logik im Worker steuern


Wir haben gerade gesehen, dass der Web Worker an die Callback-Funktion Daten per
postMessage schicken kann, die per onmessage oder onerror diese bindet. Dabei kann
jeder Datentyp übergeben werden, den JavaScript unterstützt, also nicht nur Strings, son-
dern vor allen Dingen auch JSON bzw. allgemein Objekte. Das macht die Sache natür-
lich flexibel. Vor allen Dingen geht die Kommunikation nicht nur vom Worker zum Auf-
rufer, sondern der Aufrufer kann dem Worker per postMessage Daten übergeben. Dort
stehen die Daten über den Parameter des Callback bei [Link] oder [Link]-
entListener genauso zur Verfü[Link] wir das folgende Beispiel (ww_2.html):


    <script type="text/javascript" src="lib/js/ww_2.js"></script></head>
  <body>
    <h1>Web Worker</h1><div id="antwort1"></div><div id="antwort2"></
div>
    <button id="a">Eins</button><button id="b">Zwei</button></body></
html>
13.5 Web Worker 333

Sie sehen hier zwei Buttons und zwei Ausgabebereiche. In den einen Ausgabebereich
wollen wir rekursiv, aber mit setTimeout verzögert, aus dem Hauptskript schreiben. Das
hat noch nichts mit dem Worker zu tun, sondern soll die Aktionen in dem Hauptstrang
simulieren.
Mit Klicks auf die beiden Schaltflächen soll jeweils ein Web Worker gesteuert wer-
den, der in den anderen Ausgabebereich schreibt. Dem Web Worker wollen wir dabei
gezielt einen Wert übergeben. Das ist erst einmal das Skript ww_2.js:

var th1 = new Worker("lib/js/worker/[Link]");


var i = 0;
function rek() {
  [Link]("antwort2").innerHTML = [Link]();
  if (i++ > 1000) return;
  setTimeout(rek, 30);
}
[Link] = function() {
  [Link] = function(ev) {
    [Link]("antwort1").innerHTML = [Link];
  };
  [Link]("a").onclick = function() {
    [Link](0);
  };
  [Link]("b").onclick = function() {
    [Link](1);
  };
  rek();
};

Beim Laden des Skripts wird automatisch bereits ein Web Worker gestartet. Wenn auf
den ersten Button geklickt wird, bekommt der Worker mit postMessage den Wert 0 über-
geben und beim zweiten Button den Wert 1. Parallel zu dem Web Worker läuft die rekur-
sive Funktion rek, die mit setTimeout eingebremst wird. Schauen wir uns die Worker-
Syntax an ([Link]):

var abbruchwert = 1000000000;


var nachrichtwert = 100000000;
[Link] = function(ev) {
  [Link]("Gesendeter Wert: " + [Link]);
  if ([Link] == 0) fkt1();
  else fkt2();
};
function fkt1() {
  var i = 0;
  while (true) {
334 13 Asynchrone Programmierung

    i++;
    if (i % nachrichtwert == 0)
      postMessage("Wert von i: " + i);
    if (i > abbruchwert) {
      postMessage("Funktion 1 ist fertig"); break;
    }
  }
}
function fkt2() {
  var i = 0;
  while (true) {
    i++;
    if (i % nachrichtwert * 3 == 0)
      postMessage("Zufallszahl: " + [Link]());
    if (i > abbruchwert) {
      postMessage("Funktion 2 ist fertig"); break;
    }
  }
}

Der Worker führt beim Start noch keine weitere Aktion aus, als auf ein message-Si-
gnal zu hören, das vom Starter per postMessage gesendet wird. Statt der direkten Ab-
arbeitung einer Schleife, wie im letzten Beispiel, haben wir hier also einen Worker, der
bloß als eine Art „Server“ mit einem Listener auf Eingaben wartet, um dann erst gezielt
Aufgaben auszuführen. Es wird auch in der Zeit keine Ausgabe oder sonst ein Ergebnis
einer sinnvollen Aktion geliefert (Abb. 13.8). Kommen dann die Eingaben bzw. Signale
vom Starter, wenn ein Anwender auf eine der beiden Schaltflächen klickt, wird je nach
dem Wert in [Link] eine andere Funktion aufgerufen. Den Wert von [Link] sendet der
Worker in diesem Beispiel zu Demonstrationszwecken wieder an den Aufrufer, der ihn
dann anzeigen kann. Beide Funktionen in dem Beispiel erfüllen keine wirklich sinnvollen

Abb. 13.8 Der Worker ist gestartet


13.5 Web Worker 335

Aufgaben, sind aber wieder ausreichend zeitintensiv, dass Sie die Ausführung beobachten
können (die Funktionen geben auch wieder Zwischenergebnisse zurück). Ist eine Funk-
tion fertig, wird eine abschließende Meldung zum Aufrufer geschickt. Beachten Sie, dass
mit dem Ende der jeweiligen Funktion der Worker nicht beendet wird. Jeder Klick auf
einen Button sendet ein Signal an den Worker und er wird die entsprechende Funktion
aufrufen. Da diese im Worker selbst allerdings nicht in verschiedene Prozesse aufgeteilt
sind, blockiert die Ausführung einer Funktion erst einmal die Ausführung der nächsten
Funktion. Diese werden in eine Aufrufschlange einsortiert und sukzessive abgearbeitet.

[Link] Gezielt Worker-Aufgaben abbrechen und neue starten


Wenn Sie eine chronologische Einsortierung von verschiedenen Aufgaben eines Workers
nicht haben wollen, müssen Sie den Worker anders aufbauen. Dazu schauen wir uns eine
kleine Abwandlung ww_3.html an, bei der sich nur die Erzeugung und die Verwaltung
des Worker ändern (ww_3.js):

var th1 = null;


var i = 0;
function rek() {
  [Link]("antwort2").innerHTML = [Link]();
  if (i++ > 1000) return;
  setTimeout(rek, 30);
}
[Link] = function() {
  [Link]("a").onclick = function() {
    if(th1!=null) [Link]();
    th1 = new Worker("lib/js/worker/[Link]");
    [Link] = function(ev) {
      [Link]("antwort1").innerHTML = [Link];
    };
    [Link](0);
  };
  [Link]("b").onclick = function() {
    if(th1!=null) [Link]();
   th1 = new Worker("lib/js/worker/[Link]");
    [Link] = function(ev) {
      [Link]("antwort1").innerHTML = [Link];
    };
    [Link](1);
    };
  rek();
};

Sie sehen, dass wir in dieser Variante zwar auch eine globale Variable (th1) zur Referen-
zierung auf den Worker haben, diese aber zuerst mit null initialisiert wird. Erst beim Klick
auf eine der Schaltflächen erzeugen wir jeweils den Worker und schicken ihm direkt das
336 13 Asynchrone Programmierung

gewünschte Signal. Wichtig ist, dass wir vorher bei jedem Klick zuerst einen eventuell lau-
fenden Worker mit terminate beenden. Beachten Sie, dass die Variable immer den gleichen
Worker referenziert. Damit können wir die Aufgaben des Worker gezielt abbrechen und
eine neue Aufgabe unmittelbar starten, wobei auch das natürlich ein spezielles Verhalten
ist, das nicht immer sinnvoll sein muss. Sie müssen Ihre eigene Logik hier anwenden.

[Link] Nichtblockierende, parallele Abarbeitung verschiedener


Aufgaben in einem Worker
Eine weitere Variante des Aufbaus unseres Worker ist vielleicht auch noch ganz interessant
(ww_4.html), bei der wir in ww_4.js gegenüber ww_3.js nur die Beendigung des Worker
bei jedem Klick auf einen Button weglassen (if(th1! = null) [Link]();) und hier nicht
abdrucken. Dann sorgen aber mehrere zeitnahe Klicks auf die Buttons dafür, dass die ver-
schiedenen Funktionen parallel gestartet und simultan (nicht mehr chronologisch über eine
Aufrufschlange) ausgeführt werden, wobei so ein Verhalten schwer kontrollierbar ist.

13.6 WebSockets

Grundsätzlich existiert bei RIAs das Problem, dass die Verbindung zwischen Client und
Server mit einer gewissen Latenz (Verzögerungszeit) behaftet ist. Das liegt im Wesent-
lichen an der Arbeitsweise von HTTP. Grundsätzlich arbeitet man dort ja bekanntlich mit
einem Anforderungs- und Antwortmuster, bei dem immer der Client die Anforderung star-
tet. Selbst beim Einsatz von Ajax ändert sich dieser grundsätzliche Vorgang nicht – die
gesamte HTTP-Kommunikation wird beharrlich vom Client gesteuert. Damit ist eine
Ajax-Applikation zwar in vielen Situationen viel schneller als eine konventionelle Web-
applikation, aber es gibt immer noch relevante Verzögerungen, da jede neue Aktion vom
Client angestoßen werden muss und viel überflüssige Protokolldaten hin- und hergeschickt
werden. Mit WebSockets versucht man eine möglichst geringe Latenz zu implementieren,
die auch im Web (fast) eine Echtzeitverbindung zwischen Client und Server aufbauen.

Socket-Verbindungen allgemein kennt man schon lange aus vielen Programmiersprachen. Sockets
gehen im Kern auf UNIX zurück. UNIX-Sockets stellen eine Art rudimentäres (und damit leicht-
gewichtiges und performantes) Grundobjekt des Betriebssystems für die Internetkommunikation dar,
welches das TCP-Protokoll unterstützt. Sockets verfügen dazu über diverse leichtgewichtige Daten-
strommethoden zur Ein- und Ausgabe, was das Auslesen und Schreiben von Daten recht einfach macht.

13.6.1 Allgemeiner Ablauf

Um allgemein eine Verbindung über Sockets aufzubauen, erstellen Sie in den meisten
Programmiersprachen für den Client eine Instanz eines Socket. Dabei geben Sie meist
einfach nur den gewünschten Zielserver und einen Port für die Kommunikation sowie
eventuell eine Information über die Art der ausgetauschten Daten an. Auf der Gegen-
seite (dem Server) muss nun auf dem angegebenen Port ein ServerSocket lauschen, das
13.6 WebSockets 337

sich um Client-Anforderungen kümmert. Man nennt diese Objekte auch serverseitige


Sockets und so ein Socket stellt meist eine Methode zum Akzeptieren oder Ablehnen
einer Client-Anfrage bereit. Wenn eine Client-Anfrage vom Server akzeptiert wurde,
ist eine Verbindung etabliert, über die in der Folge der Client und der Server kommu-
nizieren können. Am Ende der Kommunikation sollte in der Regel die Verbindung mit
einer passenden Methode explizit geschlossen werden. Wichtig ist bei einer Socket-Ver-
bindung, dass auch der Server „ungefragt“ etwas an den Client schicken kann – wenn
die Verbindung einmal etabliert ist (Push-Service). Beim klassischen HTTP-Turn-around
ist das eben nicht möglich. Seit einiger Zeit gibt es aber auch im Web Versuche, dass der
Server Daten bereits in dem Moment an den Client senden kann, in dem diese verfügbar
sind. Und das ist eben eine „ungefragte“ Übermittlung, die als Push- oder Comet-Ser-
vice bekannt ist und gewisse Kommunikationszeiten eklatant verkürzen kann. Eine der
bekanntesten Behelfslösungen wird lange Abfrage (long polling) genannt. Dabei öffnet
der Client ganz konventionell eine HTTP-Verbindung mit dem Server, aber sie bleibt in
irgendeiner Form geöffnet. Es wird also eine Art virtuelle Verbindung verwaltet. Aber alle
diese Behelfslösungen haben einmal das Problem, dass im Client das Hören auf die Mel-
dungen des Servers irgendwie implementiert werden muss und vor allen Dingen das rela-
tiv aufwendige HTTP als Basis genutzt werden muss, was solche Behelfslösungen nicht
besonders gut für Anwendungen mit geringer Latenz qualifiziert. Bei WebSockets wird
nun ein schlankeres Protokoll verwendet, das zudem eine persistente Verbindung etabliert.

13.6.2 Die WebSocket-Spezifikation

Beim W3C gibt es eine offizielle WebSocket-Spezifikation ([Link]


websockets/). Diese legt sowohl Protokolle als auch ein API fest, über das Socket-Ver-
bindungen zwischen einem Webbrowser und einem Server hergestellt werden können.
Und dann besteht wie bei allen Sockets nach der Etablierung einer Verbindung eine per-
sistente Leitung zwischen Client und Server. Damit können dann in der Folge beide Par-
teien jederzeit mit dem Senden von Daten beginnen.

[Link] Same-Origin-Policy und das Sprengen der Grenzen


Bei der Verwendung von clientseitigen Skriptsprachen wie JavaScript, aber auch anderen
Techniken, gibt es ein Sicherheitskonzept mit Namen Same-Origin-Policy (SOP), das dem
Browser untersagt, auf Objekte zuzugreifen, die von einer anderen Webseite stammen oder
deren Speicherort nicht der originalen Quelle (Origin) entspricht. Das SOP-Konzept stellt
ein wesentliches Sicherheitselement in modernen Browsern und Webanwendungen zum
Schutz vor Angriffen dar und wurde bereits 1996 von Netscape eingeführt. Es gewähr-
leistet, dass keine Informationen aus einem Kontext (zum Beispiel der Verbindung des
Browsers zu der Seite einer Bank) von einem Skript aus einem anderen Kontext zugreifbar
oder manipulierbar sind. Um dies zu erreichen, wird beim Zugriff eines Skripts auf ein
Objekt einer Webseite die Herkunft (origin) von beiden verglichen. Die Begrenzungen der
SOP sind allerdings in vielen modernen Webseiten unerwünscht und werden mittlerweile
338 13 Asynchrone Programmierung

durch verschiedene Techniken wie Cross-Origin Resource Sharing ausgehebelt, wenn das
ein Webbrowser unterstützt. Die SOP-Begrenzung betrifft aber theoretisch auch die Web-
Socket-Verbindung, wenn eine Kommunikation über die Begrenzung auf den Ursprungs-
ort des Clients möglich sein soll. Allerdings hat man sich bei der Spezifikation der Web-
Socket-Technik dafür entschieden, dass die Kommunikation zwischen Parteien in jeder
Domain möglich ist. Damit muss man bei einer Kommunikation sich selbst darum küm-
mern, dass nur vertrauenswürdige Clients und Server kommunizieren. Grundsätzlich kann
ein Server entscheiden, ob sein Service für alle Clients verfügbar wird oder nur für die-
jenigen, die auf einer Gruppe gut definierter Domains residieren.

13.6.3 Ein WebSocket mit JavaScript erzeugen

Um mit JavaScript ein WebSocket aufzubauen, erzeugen Sie eine Instanz über den
WebSocket-Konstruktor und geben dabei als ersten Parameter den Host samt optional
dem Port sowie als zweiten Parameter optional ein gewünschtes Subprotokoll für das
Kommunikationsprotokoll zwischen Client und Server an. Dabei kann es sich beim
zweiten Parameter um einen String oder um ein Array von Strings handeln. Jeder String
sollte einen Subprotokollnamen darstellen. Der Server akzeptiert nur eines der weiter-
gegebenen Subprotokolle im Array. Die Subprotokollnamen müssen den registrierten
Subprotokollnamen in der IANA-Registrierung entsprechen. Zum Beispiel den seit Fe-
bruar 2012 registrierten Subprotokollnamen soap. Das wäre das Erzeugen einer Web-
Socket-Instanz mit Port und Subprotokoll:

Beispiel

var connection = new WebSocket('[Link] [’soap',


'xmpp’]); ◄

Und so geht das Erzeugen einer WebSocket-Instanz ohne Port und Subprotokoll:

Beispiel

var connection = new WebSocket('[Link] ◄

 Beachten Sie das Protokoll ws. Das ist das URL-Schema für WebSocket-Verbin­
dungen. Außerdem gibt es das Protokoll wss für sichere WebSocket-Verbindungen.

13.6.4 Der WebSocket- Handshake

Zu Beginn einer WebSocket-Verbindung führen Server und Client wie immer einen
Handshake durch, bei dem Header-Informationen getauscht werden. Dieser Handshake
13.6 WebSockets 339

ähnelt auch bei ws vom Aufbau dem HTTP-Header (bzw. bei wss dem HTTPS-Header)
und ist vollständig abwärtskompatibel zu diesem, was die Nutzung des Standard-HTTP-
Ports 80 sowohl für normale HTTP-Kommunikation als auch für die Websocket-Nutzung
ermöglicht.

13.6.5 Senden von Daten an den Server – die Methode send

Das Senden von Daten an den Server erfolgt mit einer Methode send, wie man sie von
Ajax kennen sollte. Als Parameter geben Sie die zu sendenden Daten an.

Beispiel

[Link]('Ping-Pong'); ◄

Die Methode unterstützt Strings, kann aber in neuen Spezifikationen auch Binärmit-
teilungen senden.

13.6.6 Reaktion mit Callbacks und Eventhandlern

Für die Reaktion auf die Antwort des serverseitigen WebSockets implementieren Sie –
ähnlich wie bei Ajax oder Web Workern – ein Reaktionsverfahren über Callbacks, die
Sie einfach an das Verbindungsobjekt per Eventhandler anfügen. Erst nachdem der Call-
back zum Öffnen der Verbindung registriert wurde, sollte die Versendung der ersten An-
frage an den Server erfolgen.

[Link] Der Eventhandler onopen


Der Eventhandler onopen erlaubt die Reaktion auf die Eröffnung der Verbindung.

Beispiel

[Link] = function () {
  [Link](…);
  …
}

[Link] Reaktion auf Fehler mit onerror


Wie üblich gibt es auch einen Eventhandler onerror, um auf Fehler zu reagieren.

Beispiel

[Link] = function (error) { … }◄


340 13 Asynchrone Programmierung

[Link] Reaktion auf Nachrichten mit onmessage


Besonders wichtig ist natürlich, dass Sie auf Nachrichten vom Server reagieren und mit
diesen umgehen. Dazu gibt es den Eventhandler onmessage, den wir gerade bei Web
Workern ja auch schon gesehen haben. Der erste Parameter des Callbacks gibt Zugang
zu der eigentlichen Nachricht über die Eigenschaft data.

Beispiel

[Link] = function (msg) {


  … // die Eigenschaft [Link] enthält die eigentliche Nachricht
}◄

13.6.7 Die Serverseite anhand von [Link]

Um einem WebSocket jetzt eine Gegenstelle zur Verfügung zu stellen, benötigen wir
natürlich einen passenden Server. Nun ist die Nutzung von WebSockets samt dem ws-
Protokoll noch relativ neu und herkömmliche Webserver können manchmal noch nicht
damit umgehen bzw. sind ineffektiv bei der Verwaltung von mehreren offenen Web-
Socket-Verbindungen. [Link] bietet aber entsprechende Unterstützung, wobei Sie u. U.
die benötigten Module noch mit npm installieren müssen. Betrachten wir nun eine ein-
fache WebSocket-Anwendung.

[Link] Die Clientseite
Hier wäre zunächst der Client und dessen Basis-Webseite [Link].


   <script src="js/[Link]"></script></head><body>
   <h1>Ein WebSocket nutzen</h1><button id="btn">Frage den Server</button>
   <hr /><label>Antwort des Servers: </label><div id="info"></div></body>
</html>

Sie sehen eine Webseite mit einer Schaltfläche und einem leeren Div-Bereich, in dem wir
die Antworten vom Server anzeigen wollen. Das folgende Listing zeigt nun eine Java-
Script-Datei, in der eine WebSocket-Verbindung aufgebaut wird. Sowohl beim Laden der
JavaScript-Datei (ws_1.js) als auch nach einem Klick auf den Button wird eine Anfrage
an den Server geschickt.

var connection = new WebSocket('[Link]


[Link] = function() {
  [Link]('btn').onclick = function() {
    [Link]([Link]());
  };
13.6 WebSockets 341

};
[Link] = function() {
  [Link]("Login");
};
[Link] = function(error) {
  [Link]('info').innerHTML = 'WebSocket-Fehler: ' +
error;
};
[Link] = function(e) {
  [Link]('info').innerHTML = [Link];
};

Sie sehen die Erzeugung eines WebSocket-Objekts mit einer URL und einer Portangabe und
dann die Einrichtung der Callbacks für die Etablierung der Verbindung (ein String "Login"
wird dann mit send zum Server geschickt), einen Fehler und die Ankunft von Nachrichten
vom Server. In den letzten beiden Fällen wird die Meldung vom Server einfach in dem
Div-Element in der Webseite angezeigt. Nach dem Fertigstellen des DOM-Baums wird ein
Eventhandling für den Klick auf die Schaltfläche eingerichtet, die einfach eine Zufallszahl
an den Server schickt. Damit kann man jederzeit weitere Anfragen an den Server schicken.

[Link] Einen WebSocketServer mit Nodejs erstellen


Kommen wir nun zum Server für unser WebSocket-Beispiel. Um einem WebSocket eine
Gegenstelle zur Verfügung zu stellen, wollen wir wie gesagt [Link] verwenden und zu-
sätzliche Module eingebinden. Das werden wir für die Unterstützung von WebSockets
auch machen müssen, denn diese ist derzeit nicht in den Standardmodulen von Node.
js integriert. Wie das Hallo-Welt-Beispiel von [Link] zeigt, können wir zwar mit var
http = require('http'); die Unterstützung für HTTP mittels der Standardmodule von Node.
js direkt verwenden und mit [Link](function (req, res) {…}).listen(1337,
'[Link]'); einen einfachen Webserver erzeugen. Aber die Unterstützung von WebSo-
ckets mithilfe eines WebSocketServer-Objekts fehlt zum Zeitpunkt der Bucherstellung in
den Standardmodulen von [Link]. Mit dem Paketmanager npm wird die Unterstützung
für WebSockets nachinstalliert und dann kann man mit [Link] in der Folge ein Server-
Socket betreiben, das mit dem WebSocket-Objekt auf Clientseite kommunizieren kann:

Beispiel

npm install websocket ◄

Dann sollten wir uns einen passenden JavaScript-Code erstellen ([Link]).

var WebSocketServer = require('websocket').server;


var http = require('http');
var server = [Link](function(request, response) {
342 13 Asynchrone Programmierung

});
[Link](1337, function() { });
wsServer = new WebSocketServer({
   httpServer: server
});
[Link]('request', function(request) {
   var connection = [Link](null, [Link]);
   [Link]('message', function(message) {
     if ([Link] === 'utf8') {
       if (message.utf8Data=="Login") {
          [Link]("Verbindung etabliert");
        }else {
          [Link](new Date().toDateString() + ": "
            + message.utf8Data);
         }
       }
   });
   [Link]('close', function(connection) {
   });
});

Der Code basiert in der Grundstruktur auf dem Webserver, den wir bereits nutzen.

• Sie sehen zuerst die Einbindung des WebSocket-Moduls.


• Dann wird ein passender Server erzeugt, der mit WebSockets umgehen kann. Dazu
bauen wir wieder einen Webserver und der Server soll auf einem bestimmten Port (der
natürlich mit der Port-Angabe des WebSocket übereinstimmt) auf die Anfrage eines
Clients hören. Allerdings braucht hier die Callback-Funktion der listen-Methode keine
Funktionalität zu haben, denn wir wollen nicht auf HTTP-Anfragen hören. Wir nehmen
stattdessen den Webserver und übergeben ihn als Parameterwert dem Konstruktor eines
WebSocketServer. Mit diesem reagieren wir jetzt auf die ws-Anfragen vom Client.
• Der Eventhandler zur Reaktion auf die Anfrage ([Link]('request', function(re-
quest) {…})) akzeptiert Verbindungen, aber nur von der Origin, und verwertet dann
in einem Callback die Nachricht, die der Client geschickt hat, über die Eigenschaft
utf8Data. Je nach Nachricht vom Client wird mit sendUTF eine passende Antwort an
den Client geschickt. Beachten Sie die Syntax mit on. Das Event ist message. Das
entspricht genau dem onmessage, das wir bei Web Workern schon gesehen haben.
Insgesamt ist eine hohe Ähnlichkeit zu erkennen, was aber bei Callback-Konzepten
im Web sowieso fast immer gilt.
• Der Rest vom Code ist im Grunde normales JavaScript.

Der Server wird nun in einer Konsole mit node [NameDerJavaScriptDatei] gestartet.
Anschließend können wir aus dem Browser heraus Anfragen an den Server schicken
(Abb. 13.9).
13.7 Webservices 343

Abb. 13.9 Der Klick auf den


Button gibt eine neue Antwort
des Servers in der Webseite aus

Die eigentliche Logik auf der Serverseite ist natürlich in dem Beispiel begrenzt, aber
Sie sehen hier sowohl die Kommunikation per Socket als auch die grundsätzliche Ver-
wendung von JavaScript auf dem Server.

13.7 Webservices

 ebservices bezeichnet eine Technologie, die es Anwendungen ermöglicht, miteinander


W
über das Internet zu kommunizieren und Daten auszutauschen. Sie stellen eine standardi-
sierte Methode dar, um verschiedene Softwareanwendungen unabhängig von Plattformen
und Programmiersprachen miteinander zu verbinden. Ein Webservice besteht typischer-
weise aus zwei Hauptkomponenten:

• Eine Schnittstelle, die definiert, wie auf die Dienste zugegriffen werden kann.
• Eine Implementierung, die die eigentliche Funktionalität bereitstellt.

Die Kommunikation zwischen Anwendungen und Webservices erfolgt in der Regel über
das HTTP-Protokoll, obwohl auch andere Protokolle wie SOAP (Simple Object Access
Protocol) und REST (Representational State Transfer) verwendet werden können.
Um einen Webservice mit [Link] zu erstellen, benötigen wir zunächst ein Modul
wie [Link], das die Erstellung von HTTP-Servern und das Routing von Anfragen er-
leichtert. Bei Bedarf erfolgt die Installation von [Link] wie üblich:

Beispiel

npm install express ◄

Die Idee bzw. Konzeption geht wie folgt:

• [Link] ist ein beliebtes Framework für [Link], das die Erstellung von Web-
anwendungen und APIs vereinfacht, indem es eine einfache Schnittstelle für Routen
und Middleware bereitstellt. Das http-Modul, was wir schon verwendet haben, erlaubt
eigene HTTP-Server zu erstellen und [Link] baut auf diesem Modul auf und bietet
eine abstraktere und benutzerfreundlichere API.
• Mit Express können Sie Endpunkte definieren, auf die der Server reagieren soll, und
die entsprechenden Handler-Funktionen bereitstellen. Das nennt man Routing.
344 13 Asynchrone Programmierung

• Über die [Link]-Methode von Expressjs kann man JSON-Daten als Antwort auf An-
fragen zu senden.
• Die Client-Anfrage basiert auf dem node-fetch-Modul. Damit kann eine HTTP-An-
frage an den Webservice gesendet und die Antwort verarbeitet werden.
• Für eine nichtstandardisierte Kommunikation benötigt man eine Port-Zuweisung. Der
Port, auf dem der Server lauscht, wird entweder durch die Umgebungsvariable PORT
oder standardmäßig auf Port 3000 festgelegt.

Erstellen wir also eine Datei namens [Link] mit folgendem Code:

const express = require('express');


const app = express();
const PORT = [Link] || 3000;
// Statische Dateien (Client-seitige Ressourcen)
[Link]([Link]('public'));
// Routing für den Webservice
[Link]('/api/books', (req, res) => {
  const books = [
    { id: 1, title: 'Webanwendungen erstellen mit [Link]',
    ISBN: '978-3-658-27169-5' },
    { id: 2, title: 'Einführung in JavaFX/OpenJFX',
     ISBN: '978-3-658-35538-8' },
    { id: 3, title: 'Programmierung in Python',
    ISBN: '978-3-658-20705-2' }
  ];
  [Link](books);
});
// Server starten
[Link](PORT, () => {
  [Link]('Service listening on port ${PORT}');
});;

Dieser Code erstellt einen einfachen [Link]-Server, der eine Liste von Büchern
zurückgibt, wenn eine GET-Anfrage an `/api/books` gesendet wird. Kommen wir nun
zum Erstellen des Clients über eine Datei namens [Link] im gleichen Verzeichnis wie
[Link]:

import fetch from 'node-fetch';


fetch('[Link] => response.
json())
  .then(books => {
    [Link]('Bücher:', books);
13.7 Webservices 345

  }).catch(error => {
    [Link]('Fehler:', error);
});

Dieser Client verwendet das Modul node-fetch, um eine GET-Anfrage an den Webser-
vice zu senden und die Liste der Bücher auszugeben. Wie Sie sehen, gibt es eine Pro-
mise-Logik mit der Verkettung ü[Link] [Link]. Bei Bedarf erfolgt die Installation
von node-fetch wie üblich:

Beispiel

npm install node-fetch ◄

Das Ausführen des Webservice und des Clients erfolgt so, dass Sie zuerst im Terminal
mit node [Link] den Server bzw. Service und dann in einem anderen Terminal mit
node [Link] den Client starten (Abb. 13.10).
Selbstverständlich ist es üblich, dass in der Praxis eher nicht localhost verwendet
wird.

Abb. 13.10 Service und Client in zwei Terminals


Erweiterte Techniken
14

Die Tatsache, dass man mittlerweile mit JavaScript weit mehr machen kann als
„nur“ Webseiten manipulieren oder Benutzereingaben im Browser zu plausibilisie-
ren, ist zwar noch nicht überall bekannt, aber informierten Stellen klar. Wir haben
ja in den letzten Kapiteln viele interessante Anwendungen jenseits des ursprüng-
lichen Einsatzgebietes von JavaScript kennengelernt. In diesem Kapitel ergänzen
wir noch interessante erweiterte Features, die meist mit HTML5 assoziiert sind.

14.1 Daten im Browser speichern

Ein Browser arbeitet mit einer Sandbox. Das beschreibt im Wesentlichen einen Sicher-
heitsmechanismus, der den Browser und seine Prozesse isoliert, um das Betriebs-
system und andere Anwendungen vor potenziell schädlichem Code zu schützen, der
auf einer Webseite ausgeführt wird. Das Prinzip der Sandbox beruht auf der Idee, dass
jede Webseite in einer eigenen, begrenzten Umgebung ausgeführt wird, die von ande-
ren Webseiten und dem Hauptsystem getrennt ist. Zudem gibt es Beschränkungen für
die Aktionen, die eine Webseite ausführen kann sowie eine Ressourcenkontrolle und eine
Rechteverwaltung samt einer kontrollierten, sicheren Laufzeitumgebung für die Aus-
führung von Skriptsprachen. Die Sandbox unterbindet u. a. auch Zugriffe auf das Datei-
system eines Rechners. Was wiederum bedeutet, dass Schreiben und Lesen von Daten
von einem Rechner via Browser und JavaScript nicht frei möglich ist. Allerdings im
Rahmen von vorgegebenen Strukturen. Damit kann man dann Daten in einem Browser
durchaus speichern als auch wieder auslesen.

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 347
Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
348 14 Erweiterte Techniken

 Über die Entwicklertools moderner Browser können Sie sehen, was sich in den
lokalen Speicherbereichen eines Browsers befindet (Abb. 14.2).

14.1.1 Cookies

Historisch waren Cookies eine wichtige Möglichkeit, um Informationen auf dem


Computer eines Besuchers eine Webseite persistent (dauerhaft) zu machen und bei
einem neuen Besuch oder einer Aktion, die eine andere Webseite angezeigt hat, zu re-
produzieren. Bei einem Cookie handelt es sich um eine kleine Textdatei, welche von
einem Browser in ein vorgegebenes Verzeichnis geschrieben und von dort wieder aus-
gelesen werden kann. Dieser Vorgang kann sowohl vom Client als auch Server initiiert
als auch verwertet werden, wobei man festhalten sollte, dass klassische Cookies mit
der Verbreitung von Ajax respektive moderner RIAs an Bedeutung verlieren. Mit Coo-
kies kann man beispielsweise Sitzungen verfolgen, die sich über mehrere Webseiten er-
strecken. Wenn Sie jedoch Ajax einsetzen, um nur Teile einer Webseite auszutauschen
(SPA), werden Sie in vielen Situationen keine neuen Webseiten laden und damit die
Sitzung nicht verfolgen müssen. Daten können in JavaScript selbt vorgehalten werden.
Zwar müssen derzeit Webseiten aus rechtlichen Gründen mit blockierenden Dialogen vor
der Verwendung von Cookies warnen, aber das ist weder angemessen noch beachten die
Mehrzahl der Besucher diese Hinweise.
Hinsichtlich des lokalen Speicherns von Informationen auf dem lokalen Rechner gab
es über die Jahre diverse Ansätze, die weit über die Möglichkeiten harmloser Cookies
hingegangen sind, aber erst mit HTML5 wurde das lokale Speichern über Local Storage,
Session Storage und lokale Datenbanken sowohl vereinfacht als auch in den Möglich-
keiten erheblich erweitert.
Dennoch sind Cookies nicht vollkommen tot und man sollte die Technik zumindest
noch kennen. Dabei sollen Details zu der unterschiedlichen Umsetzung in verschiedenen
Browsern nicht thematisiert werden, denn rein aus JavaScript ist der Zugriff auf Coo-
kies sowohl trivial als auch überall gleich. Grundsätzlich kann ein Cookie via JavaScript
mit [Link] gesetzt und ausgelesen werden. Der Wert ist ein String und des-
sen Aufbau folgt gewissen vorgegebenen Regeln. Generell besteht ein Cookie mindes-
tens aus der Angabe des Bezeichners des Cookies und einer Wertzuweisung. Über den
Bezeichner wird beim Auslesen ein Cookie wieder identifiziert. Der zugewiesene Wert
ist die Information, welche man eigentlich speichern möchte. Die maximale Länge eines
Strings, der in einem klassischen Cookie gespeichert werden kann, beträgt in der Regel
4096 Bytes (4 KB). Dies gilt jedoch nicht unbedingt für alle Browser und Server und
kann auch weniger sein.

Beispiel

[Link] = "meinCookie=42";.
14.1 Daten im Browser speichern 349

Eine wichtige optionale Eigenschaft ist die expires-Eigenschaft, mit der ein Gültigkeits-
datum eines Cookies gesetzt werden kann oder domain für den URL, für welchen ein
Cookie gültig ist. Cookie-Werte dürfen keine Leerzeichen und keine Sonderzeichen ent-
halten. Deshalb müssen solche Zeichen mit den JavaScript-Toplevel-Funktionen beim
Schreiben maskiert und beim Lesen wieder entschlüsselt werden. Des Weiteren müssen
verschiedene Aktionen wie das Bestimmen der Länge eines Werts oder das Trennen an
bestimmten Zeichen im String durchgeführt werden (dies sind recht analoge Vorgänge,
wie man sie beim Übertragungsprozess mit Formulardaten anwendet). Nun ist es auch
nicht ganz so einfach, wenn man komplexere Informationen in einem Cookie speichern
und wieder auslesen möchte. JavaScript bietet da erst einmal keine große Unterstützung.
Aber es hat sich viele Jahre eine Bibliothek für das Cookie-Management einen Satz von
freien Funktionen etabliert, die ursprünglich ein Programmierer namens Bill Dortch ent-
wickelt hat und kostenlos bereitgestellt wurden. Das soll es zu Cookies aber gewesen
sein, denn diese Technologie ist sowohl veraltet, unzuverlässig und viel zu beschränkt.

14.1.2 Local Storage und Session Storage

Mit HTML5 wurde lokale Speicherung von Daten persistenter, mächtiger, leichter zu
handhaben und schneller abrufbar als mit Cookies. Vor allen Dingen wurde die Menge
der speicherbaren Daten erheblich größer werden. Im Konzept von HTML5 werden drei
Möglichkeiten dem lokalen Speichern von Daten beim Benutzer zugeordnet:

• Local Data Storage oder kurz Local Storage. Grundsätzlich sollten diese Daten per-
manent im Speicher des Browsers bleiben, bis sie mit speziellen Methoden oder auto-
matischen Bereinigungen vom Browser beseitigt werden.
• Session Data Storage oder kurz Session Storage. Hiermit speichern Sie Informationen
nur so lange, wie eine Sitzung im Browser dauert. Da in den Browsern auch jeder
Tab bzw. jedes Browserfenster als eigene Sitzung verwaltet wird, gibt es auch jeweils
einen eigenen Sitzungsspeicher. Deshalb kann man von einem Tab/Fenster nicht auf
die Daten in einem anderen Tab/Fenster zugreifen, was beim Local Data Storage
möglich ist.
• Web SQL Database (worauf wir hier nicht weiter eingehen)

Das Konzept des Local Storage ist wirklich lokal. Das bedeutet, serverseitige Applika-
tionen können diese gespeicherten Daten nicht direkt auslesen bzw. verändern oder er-
stellen. Die gespeicherten Daten werden nicht wie Cookies zum Server geschickt. Der
Weg des Zugriffs führt immer über JavaScript und damit rein clientseitige Programmie-
rung. Zudem sind gespeicherte Daten allgemein nur für jeweils eine spezifische Domain
und die eigene Browserinstanz zugänglich, wobei sich Browser hier in den genauen De-
tails unterscheiden.
350 14 Erweiterte Techniken

Hintergrundinformation
Ganz alte Browser unterstützen diese Form des lokalen Speicherns nicht. Aber man kann Zugriffe
absichern bzw. testen, ob Unterstützung vorhanden ist. Das sieht dann so aus:

try {
   return 'localStorage' in window && window['localStorage'] !== null;
} catch(e){
      return false;
    }
}

Sofern der Browser das lokale Speichern unterstützt, erhalten Sie den Wert true, andernfalls false.
Im Erfolgsfall können Sie das lokale Speichern durchführen, im Fehlerfall greifen Sie z. B. auf
Cookies zurück oder serverseitiges Speichern per Ajax. Analog gehen Sie beim Speichern im Ses-
sionfall vor.

[Link] Die Objekte localStorage und sessionStorage sowie deren


Methoden und Eigenschaft
Die eigentliche Verwendung von lokalem Speichern ist denkbar einfach. Es gibt haupt-
sächlich ein Objekt localStorage, das im Zentrum des gesamten Verfahrens steht. Bei
der sitzungsbezogenen Speicherung arbeiten Sie mit dem Objekt sessionStorage. Beide
stellen einige identische Methoden und eine Eigenschaft zur Verfügung. Die einzige
Eigenschaft dürfte keine Überraschung sein. Mit length erhalten Sie die Anzahl der ge-
speicherten Elemente (Tab. 14.1).

Tab. 14.1  Methoden eines Objekts vom Typ localeStorage bzw. sessionStorage


Methode Beschreibung
clear Damit wird der gesamte Speicher gelöscht.
getItem Damit erhalten Sie den Wert, der hinter dem als Parameter angegebenen Schlüssel
abgelegt ist. Gibt es den Schlüssel nicht, erhalten Sie null.
key Name des Schlüssels an der als numerischer Parameter angegebenen Position. Ist
der Wert des Parameters größer als die Anzahl der Einträge im Speicher, erhalten
Sie null.
removeItem Löschen des spezifizierten Schlüssel-Werte-Paar aus dem Storage. Der Parameter
ist der Schlüssel.
setItem Die Methode verwendet zwei Parameter, die ein klassisches Schlüssel-Werte-Paar
darstellen. Man speichert damit eine neue Information (2. Parameter), die über
den Schlüssel (1. Parameter) adressiert wird. Gibt es den Schlüssel schon, wird
der vorhandene Wert überschrieben. Als Werte können Sie Strings speichern,
aber auch andere Formate. JSON-Objekte bieten sich für komplexere Strukturen
geradezu an.
14.1 Daten im Browser speichern 351

[Link] Ein Beispiel, um einfache Daten in einem Local Storage


abzulegen
Betrachten wir ein Beispiel für den Umgang mit localStorage (ls_1.html):


    <script src="lib/js/ls_1.js"></script></head>
  <body><h1>Local Data Storage mit HTML5</h1>
    <button id="b1">Speichere Zufallswert</button>
    <button id="b2">Lese lokalen Speicher aus</button>
    <button id="b3">Lösche lokalen Speicher</button>
    <div id="ausgabe"></div></body></html>

Sie sehen eine einfache Webseite mit drei Schaltflächen. Damit wollen wir Daten lokal
speichern, auslesen und löschen. Hier ist die referenzierte externe JavaScript-Datei ls_1.js:

var counter = 0;
function testlokaleSpeicherung() {
  try {
    return 'localStorage' in window && window['localStorage'] !== null;
  } catch(e) {
    return false;
  }
}
function init() {
  if (testlokaleSpeicherung() == false) {
    alert("Die lokale Speicherung ist nicht möglich"); return;
  }
  [Link]("b1").onclick = function() {
    [Link]("key" + counter++, [Link]());
  };
  [Link]("b2").onclick = function() {
    [Link]("ausgabe").innerHTML = "";
    var j = 0;
    for (i in localStorage) {
      [Link]("ausgabe").innerHTML +=
      [Link](j++) + ": " + [Link](i) + "<br />";
    }
  };
  [Link]("b3").onclick = function() {
    counter = 0;
    [Link]();
  };
}
[Link] = init;
352 14 Erweiterte Techniken

Mit dem Klick auf die erste Schaltfläche speichern wir eine Zufallszahl. Deren Schlüssel
ergibt sich aus dem Token key und dem Wert einer globalen Variablen counter, die wir
am Beginn des Skripts anlegen und nach jedem Speichern eines Werts mittels setItem
um den Wert 1 erhöhen. Mit jedem Klick auf den ersten Button wird also ein neuer Wert
im lokalen Speicher persistent angelegt. Mit Klick auf die dritte Schaltfläche löschen wir
den lokalen Speicher mittels clear und setzen den Zähler zurück, damit über die erste
Schaltfläche neu angelegte Schlüssel-Werte-Paare wieder mit dem Schlüssel key0 be-
ginnen werden. Die Funktionalität zum Auslesen des Inhalts des lokalen Speichers ist
etwas umfangreicher. Wir wollen hier nicht gezielt einen Wert auslesen (was man natür-
lich auch leicht machen kann – beachten Sie das folgende Beispiel), sondern alle ge-
speicherten Werte inklusive der Namen des jeweiligen Schlüssels auslesen und einfach
ausgeben. Dazu arbeiten wir mit einer for…in-Schleife, bei der i den Schlüssel repräsen-
tiert. Synchron zum jeweiligen Schleifendurchlauf zählen wir die lokale Variable j hoch,
die wir als Index in der Methode key verwenden, um darüber den Namen des Schlüssels
abzufragen. Mit getItem(i) ermitteln wir den jeweils gespeicherten Wert.

[Link] Ein Beispiel zum Speichern von komplexen Informationen


Betrachten wir ein weiteres Beispiel, das komplexere Informationen ablegt und dazu
diesesmal das sessionStorage-Objekt verwendet. Wir wollen Eingaben aus einem Web-
formular in einem Sitzungsspeicher ablegen und dabei gleich einige unsichtbar er-
hobenen Metainformationen über den Browser und den Zeitpunkt der Speicherung spei-
chern. Hier ist das Webformular, dessen Daten für die Sitzung lokal gespeichert werden
sollen (ls_2.html):


    <script src="lib/jsl1_2.js"></script></head>
  <body><h1>Session Data Storage mit HTML5</h1>
    <form><div id="label">Name</div><input id="name"/><br />
      <div id="label">Vorname</div><input id="vorname"/><br />
      <div id="label">Strasse</div><input id="strasse"/><br />
      <div id="label">PLZ</div><input id="plz"/><br />
      <div id="label">Ort</div><input id="ort"/><br />
      <div id="label2">Kommentar</div>
      <textarea id="kommentar" rows="10" cols="50"></textarea>
    </form><hr /><button id="b1">Weiter</button></body></html>

Sie sehen in der Webseite ein konventionelles Formular – aber ohne die Angabe von ac-
tion etc., denn wir wollen die Daten ja nicht zum Server schicken (Abb. 14.1).
Beim Klick auf die Schaltfläche sollen die Daten in dem Sitzungsspeicher abgelegt
und eine neue Seite im Browser aufgerufen werden, die mit diesen Daten dann arbei-
ten kann. Das kennt man so ja von der Sitzungsverfolgung mit serverseitigen Techniken,
aber hier geht das wirklich explizit nur über den Client. Hier ist nur die Datei ls_2.js, in
der die Daten gespeichert werden:
14.1 Daten im Browser speichern 353

Abb. 14.1 Eingaben in einem Webformular

function testlokaleSpeicherung() {
  try {
    return 'sessionStorage' in window && window['sessionStorage'] !==
null;
  } catch(e) {
    return false;
  }
}
function init() {
  if (testlokaleSpeicherung() == false) {
    alert("Die lokale Speicherung ist nicht möglich"); return;
  }
  [Link]("b1").onclick = function() {
    with (sessionStorage) {
      setItem("name", [Link]("name").value);
      setItem("vorname", [Link]("vorname").value);
354 14 Erweiterte Techniken

      setItem("strasse", [Link]("strasse").value);
      setItem("plz", [Link]("plz").value);
      setItem("ort", [Link]("ort").value);
      setItem("kommentar", [Link]("kommentar").
value);
      setItem("metadaten", [Link]({
        'browser' : [Link], 'zeit' : new Date().toS-
tring()
      }));
    }
    [Link] = "ls_3.html";
  };
}
[Link] = init;

Wie gehabt testen wir, ob die Speicherung überhaupt möglich ist, nur hier auf Basis des
sessionStorage-Objekts, was aber wie schon erwähnt sonst vollkommen identisch mit
einem persistenten Speichern ist. Danach setzen wir für jedes Formularfeld ein eigenes
Schlüssel-Werte-Paar, das wir jeweils mit setItem in dem Sitzungsspeicher ablegen. Das
könnten wir durch die potenziell riesige Datenmenge natürlich auch in einem Schlüssel-
Werte-Paar machen, wenn wir in dem Wert eine geeignete Struktur anlegen – wozu sich
JSON anbietet. Wie das geht, demonstrieren wir hier mit den Metadaten. Mittels JSON.
stringify erzeugen wir aus der JavaScript-Datenstruktur mit zwei Schlüssel-Werte-Paaren
einen JSON-Text, den wir dem Schüssel "metadaten" zuweisen. Das können Sie natür-
lich beliebig komplex werden lassen.
Nach der Speicherung der Daten im Sitzungsspeicher leiten wir mit [Link] zu
einer neuen Webseite weiter. Wenn nun die neue Seite ls_3.html im Browser geladen
wird, werden beim Laden der Seite die gespeicherten Sitzungsdaten ausgelesen und ein-
fach in der Webseite angezeigt. Dabei kann man auch in den Entwicklertools sehen, was
sich gerade in den verschiedenen verfügbaren lokalen Speichern des Browsers befindet
(Abb. 14.2).
Das machen wir so (ls_3.js):

function init() {
  [Link]("ausgabe").innerHTML = "";
  with (sessionStorage) {
    [Link]("ausgabe").innerHTML += getItem("name") +
"<br />" + getItem("vorname") + "<br />" + getItem("strasse") + "<br
/>" + getItem("plz") + "<br />" + getItem("ort") + "<br />" + getI-
tem("kommentar") + "<hr />" + [Link](getItem("metadaten")).browser
+ "<hr />" + [Link](getItem("metadaten")).zeit;
  }
}
[Link] = init;
14.2 Geolocation 355

Abb. 14.2 Auslesen des Sitzungsspeichers

Sie sehen, dass wir mit getItem gezielt die einzelnen Schlüssel auslesen. Insbesondere
die Zeilen sind interessant, wo wir den JSON-Text jeweils mit [Link] in ein JSON-
Objekt umwandeln, um darüber dann wie bei JSON üblich per Punktnotation gezielt die
einzelnen Eigenschaften des JSON-Objekts zu verwerten.

14.2 Geolocation

Geolocation oder auch Geodating bzw. Geolokalisierung nutzen diverse Anwendungen,


die eine Lokalisierung des Besuchers einer Webseite oder die allgemeine Bereitstellung
von ortsbezogenen Informationen benötigen. Dabei ist das insbesondere für mobile Ge-
räte von Bedeutung, aber auch bei Festnetzgeräten lässt sich eine fast metergenaue Loka-
lisierung rein über den Internetzugang realisieren. Solch eine native Lokalisierung über
den Browser war eine der Neuerungen von HTML5, indem das JavaScript-API (bzw.
der DOM) um eine neue Eigenschaft des navigator-Objekts erweitert wurde – navigator.
geolocation. Bei geolocation handelt es sich um ein Objekt, über das verschiedene Me-
thoden zur Lokalisierung bereitgestellt werden und das die Lokalisierung vollkommen
vor dem Anwender verbirgt sowie die verschiedenen Möglichkeiten, die Position zu be-
stimmen, über eine gemeinsame Schnittstelle vereinheitlicht. Sie brauchen sich als Web-
seitenprogrammierer überhaupt nicht darum zu kümmern, wie konkret die Position er-
mittelt wird. Trotzdem sollten die Hintergründe der Geolokalisierung zumindest grund-
sätzlich klar sein.
356 14 Erweiterte Techniken

14.2.1 Hintergründe der Geolokalisierung

Nun stellt sich die Frage, wie die Lokalisierung ohne GPS überhaupt erfolgt? Denn ins-
besondere dynamisch von einem Provider zugewiesene IP-Nummern ändern sich ja
und viele Anwender glauben immer noch, dass sie damit nicht zu identifizieren seien.
Das ist ein Trugschluss, wie die Geolokalisierung erschreckend deutlich zeigt. Die ver-
schiedenen Techniken zur Ortsbestimmung sind ziemlich ausgefeilt. Dabei geht es
grundsätzlich darum, automatisiert herauszubekommen, wo Sie (oder genauer – Ihr
Gerät) sich gerade aufhält und wohin Ihr Weg im Fall eines mobilen Endgeräts führt.
Die Geolokalisierung umfasst von der Bedeutung alle denkbaren Verfahren zur Loka-
lisierung, sei es die lokale Zuordnung eines Besuchers einer Webseite, sei es die Orts-
bestimmung eines Anwenders, der eine App auf einem Smartphone ausführt, sei es die
Routenführung mit einem Navigationsgerät.

[Link] GPS
Besonders einfach wird Lokalisierung, wenn GPS (Global Positioning System) verfüg-
bar ist. Dies ist bei mobilen Geräten fast immer der Fall. Lokalisierung per GPS ist exakt
(in der Regel auf wenige Meter genau) und man kann auch die Geschwindigkeit eines
Objekts ziemlich genau bestimmen.

[Link] Funkzellen
Mobile Geräte sind oft in Funkzellen angemeldet und auch darüber zu orten, allerdings
nicht so genau wie mit GPS und die Geschwindigkeit kann nicht bestimmt werden. Die
Genauigkeit der Ortsbestimmung über Funkzellen hängt an der Infrastruktur der Funk-
masten. Bei einer großen Dichte an Funkmasten (etwa in der Stadt) kann die Position
unter Umständen auf einige Meter genau ermittelt werden, während es bei großen Funk-
zellen möglichweise eine Abweichung von mehreren Kilometern geben kann.

[Link] W-LAN
Eine weitere Möglichkeit zu Lokalisierung verwendet W-LAN-Router, wenn deren
Positionen bekannt sind. Diese Technik funktioniert bei allen Geräten, wenn diese mit
dem W-LAN-Router verbunden sind. Die Genauigkeit der Lokalisierung eines einzel-
nen Geräts kann auf wenige Meter genau sein, wobei in einem großen lokalen Netz-
werk (etwa in einer Firma) durchaus große Abweichungen von der tatsächlichen Position
eines einzelnen Geräts auftreten (auch wenn der Router selbst auf wenige Meter genau
lokalisiert werden kann). Nun stellt sich die Frage, wie die Bestimmung der Position
eines W-LAN-Routers und damit der angeschlossenen Geräte überhaupt erfolgt? Und
wer überhaupt diese Positionen kennt? Die Beantwortung der Fragen führt unter ande-
rem zuz MAC-Adresse (Media-Access-Control), die man berücksichtigen kann, und
natürlich die IP-Nummer, und die Angabe, wo diese gesammelt und lokalisiert werden.
14.2 Geolocation 357

Die MAC-­Adresse ist eine (im Prinzip) eindeutige Hardwareadresse jedes einzelnen
Netzwerkadapters auf der Welt. Diese kann zur Identifizierung des zugehörigen Geräts
in einem Rechnernetz verwendet werden. Wenn so eine MAC-Adresse eines W-LAN-
Routers an einen Empfänger gesendet wird, kann dieser den Ort bestimmen, woher die
Sendung kam. Aber woher kennt man den Ort des W-LAN-Routers und wer kennt ihn?
Es gibt spezielle GeoLocation Services, etwa von Apple den GeoLocation-Dienst Sky-
hook bzw. WiFi Access Point Database, von Nokia oder Google Gears bzw. die Google
GeoLocation-Dienste. Diese liefern die genaue Position eines W-LAN-Routers, wenn
Sie dessen MAC-Adresse bei einer Anfrage zugesendet bekommen.
Nur wie können diese GeoLocation-Dienste die Position von einem W-LAN-Router
kennen? Am einfachsten ist offensichtlich die manuelle Eingabe einer Adresse durch An-
wender in einem Webformular. Es gibt aber auch Scanner, die die MAC-Adressen von
Routern ausspionieren und dann samt den geografischen Positionen den GeoLocation-
Services-Betreibern zukommen lassen. Letzteres ist nicht etwa an den Haaren herbei-
gezogen und ein Ausnahmefall – das ist der Regelfall. Darüber erhalten die GeoLocation
Services die geografischen Positionen von fast allen W-LAN-Routern weltweil. Denn
einmal senden viele mobile Geräte mit aktiviertem GPS und eingeschaltetem W-LAN
automatisch die MAC-Adressen aller W-LANs in Reichweite und ihre aktuelle Position
an die besagten GeoLocation-Dienste. Eine explizite Verbindung mit den jeweiligen
W-LANs ist dazu nicht vonnöten, denn zur Identifizierung senden die W-LAN-Router die
MAC-Adressen offen mit. Viele Millionen Smartphone-Besitzer betätigen sich damit –
oft unbewusst – als Zuträger für die GeoLocation-Dienste. Selbst wenn sich die Posi-
tion eines bereits bekannten W-LAN-Routers ändert (etwa durch Umzug) – irgendwann
kommt eine Person mit Smartphone und angeschaltetem W-LAN-Scanner/GPS vorbei
und die Position kann in den Datenbanken der GeoLocation-Dienste korrigiert werden.
Aber auch Projekte wie Google StreetView scannen gezielt die Positionen von Routern.

[Link] Weitere Möglichkeiten wie RFID, Bluetooth


Für die Ortsbestimmung in kleinen Abständen kann man auch die RFID-Technik (ra-
dio-frequency identification) verwenden. Aber auch andere Funkschnittstellen wie
Bluetooth und Co kann man zur Ortsbestimmung verwenden. Kommt ein mobiles
Gerät mit aktivierter Bluetooth-Kennung an einem Gerät mit Bluetooth vorbei, ist des-
sen Position natürlich auch ziemlich genau zu bestimmen. Offensichtlich bieten sich
diese Lokalisierungstechniken bei mobilen Geräten an, aber sie sind nicht darauf be-
schränkt – gerade wenn man an die besagten (meist unbemerkten) Spionagefunktionen
vieler Smartphones denkt, die ihre Umgebung scannen und die Daten weitersenden.

[Link] IP-Adressen
Wenn alle diese Quellen nicht verfügbar sind – selbst über IP-Nummern kann man über
Laufzeitmessungen und Triangulierung Positionen bestimmen und das geht dann selbst
358 14 Erweiterte Techniken

für reine PCs, die ganz ohne W-LAN-Router etc. online gehen. Die Genauigkeit ist da
in der Regel zwar recht gering, aber zumindest die Region lässt sich bestimmen und teils
sogar mehr, wenn bestimmte Voraussetzungen gegeben sind.

[Link] Viel Auswahl – von genau zu ungenau über Fallbacks


Es gibt also mehrere Varianten, wie automatisch ein Gerät bzw. Standort mehr oder we-
niger genau lokalisiert werden kann. Die Techniken im Hintergrund werden möglichst
automatisch das Verfahren zur Lokalisierung auswählen, das die bestmöglichen Stand-
ortdaten liefert, wenn mehrere Möglichkeiten zur Verfügung stehen, und nutzen in einem
Fallback-Prozess dann immer ungenauere Verfahren.

14.2.2 Die konkreteGeolokalisierung

Kommen wir nun zu den konkreten Möglichkeiten der Geolokalisierung. In der Geo-
lokalisierung [Link] gibt es eine Methode getCurrentPosition. Darüber
wird die aktuelle Position eines Besuchers einer Webseite ermittelt. Die Methode get-
CurrentPosition bekommt als Parameter zwei Callback-Funktionen übergeben. Der erste
Callback steht für den Erfolgsfall bei der Lokalisierung und der zweite für den Fehler-
fall. Wird der Callback für den Erfolgsfall aufgerufen, so bekommt die dann aufgerufene
Funktion ein Positionsobjekt mit Geodaten als Parameter übergeben. Dieses Objekt ent-
hält eine Reihe von Geoattributen, wobei das Unterobjekt coords mit den Koordinaten
wohl am interessantesten ist. In coords können Sie etwa auf latitude oder longitude für
die Breite und Länge zugreifen. Im Fehlerfall erhalten Sie ein Fehlerobjekt.

 Latitude und Longitude ergeben zusammen eine geokodierte Position. Breiten- (La-
titude) und Längengrade (Longitude) werden in Form von Zahlen mit einer Genauigkeit
von bis zu sechs Dezimalstellen angegeben. Die Angabe für die Länge basiert auf der
Entfernung vom Ort Greenwich in England (der Nullmeridian). Breitengrade können
einen beliebigen Wert zwischen –90 und 90 und Längengrade einen beliebigen Wert zwi-
schen –180 und 180 annehmen.

Wenn eine Webseite versucht, eine Ortsbestimmung vorzunehmen, wird im Browser eine ent-
sprechende Meldung angezeigt (Abb. 14.4), wenn der Browser nicht unverantwortlich unsicher
eingerichtet ist.

[Link] Ein praktisches Beispiel


Das soll die Basis für das erste Beispiel sein ([Link]):


<script src="lib/js/geo_1.js"></script></head>
<body><h1>Geolocation</h1><div id="karte"></div></body></html>
14.2 Geolocation 359

Betrachten Sie das einfache Skript geo_1.js:

function success(position) {
[Link]('karte').innerHTML = "Latitude: " +
[Link] + "<br />Longitude: " +
[Link];
}
function error(msg) {
[Link]('karte').innerHTML = msg;
}
function init() {
if ([Link]) {
[Link](success, error);
} else {
alert("Fehler bei der Geolokalisierung!");
}
}
[Link] = init;

Zuerst erzeugen wir ein Lokalisierungsobjekt mit den zwei Callbacks. Zur Absicherung
nutzen wir eine Browserweiche, die auf die Verfügbarkeit von [Link]
prüft. Im Callback für den Erfolgsfall verwenden wir coords, um Länge und Breite des
Besucherstandpunkts auszuwerten und anzuzeigen (Abb. 14.3). Beachten Sie, dass Sie
vor dem Lokalisieren in der Regel eine Warnung des Browsers sehen (Abb. 14.4) und
die Lokalisierung bestätigen müssen sowie, dass die Lokalisierung durchaus einige Se-
kunden dauern kann. Was letztendlich auch ein Beweis ist, dass die angesprochenen
Lokalisierungsdienste genutzt werden.

[Link] Kombination mit Kartendiensten


Nun ist die Angabe von einer Länge und Breite als Zahlen in der Praxis meist nicht aus-
reichend, aber es gibt zahlreiche Kartendienste wie Google Maps, Bing Maps, Map-
box, HERE Maps, Apple Maps, TomTom Maps oder OpenStreetMap. Damit sind in
der Regel auch zahlreiche Web APIs verbunden, die man in Webseiten verwenden kann.
Wenn Sie die Entwickler-Webseiten der Kartendienste besuchen werden Sie sehen, dass
Ihnen dort eine große Anzahl an APIs und Features für die verschiedensten Zwecke zu

Abb. 14.3 Mehr bedarf es zur


Geolokalisierung nicht – Länge
und Breite des Standpunkts
sind bestimmt
360 14 Erweiterte Techniken

Abb. 14.4 Erlaubnis zur Geolokalisierung einfordern

Verfügung stehen. Die Bibliotheken dienen im Kern dazu, dass Sie RIAs auf Basis von
CSS, JavaScript, HTML und letztendlich Ajax erstellen bzw. Ihre Webseiten um inter-
aktive, dynamische Funktionalitäten erweitern können. Unter anderem gibt es meist
JavaScript-Bibliotheken mit Satellitenbildern, Landkarten, Stadtplänen etc. und ver-
schiedenen Steuerelementen. Gekopplet mit CSS-Ressourcen sind diese Karten oft dy-
namisch. Man kann sie verschieben, vergrößern, verkleinern, etc. Solche Karten werden
in einem Bereich der Webseite in einem Gitter mit mehreren kleineren Einzelbildern und
ergänzenden Zusatzinformationen zusammengesetzt, die aufgrund von Benutzeraktionen
oder programmtechnischen Vorgaben vom Server gesendet werden und von Ihnen mit
JavaScript- und DHTML-Techniken in Ihre Seiten eingefügt werden können. Wir wol-
len jetzt mit einem Geodienst in einer Karte die Position eines Besuchers auswerten und
darüber seinen Ort in einer Karte anzeigen. Wie das genau geht, ist in den meisten Kar-
ten-APIs weitgehend ähnlich, aber im Detail natürlich spezifisch. Wir werden auf Details
nicht eingehen und es sei explizit auf die Dokumentationen der APIs verwiesen, aber ein
konkretes Beispiel mit dem API von OpenStreetMap sehen Sie hier. Das ist die HTML-
Basis ([Link]):


<link rel="stylesheet" href="[Link]
css" />
<style>#karte { height: 400px;width:400px; }</style></head>
<body><h1>Geolocation mit OpenStreetMap</h1><div id="karte"></div>
<script src="[Link]
<script src="lib/js/geo_2.js"></script></body></html>

Beachten Sie die Einbindungen der externen Ressourcen, die so von dem API gefordert
sind. Das Beispiel verwendet [Link], eine JavaScript-Bibliothek für interaktive
14.3 DHTML, Zeichnen und Animationen 361

Karten, und OpenStreetMap als Kartenanbieter. In unserer JavaScript-Datei geo_2.js


steht das:

var map = [Link]('karte').setView([0, 0], 13);


[Link]('[Link] {
  attribution: '&copy; <a href="[Link]
right">OpenStreetMap</a> contributors'
}).addTo(map);
function onLocationFound(e) {
  var radius = [Link] / 2;
  [Link]([Link]).addTo(map).bindPopup("Du bist innerhalb von " +
    radius + " Metern von diesem Punkt.").openPopup();
  [Link]([Link], radius).addTo(map);
}
function onLocationError(e) {
  alert([Link]);
}
[Link]('locationfound', onLocationFound);
[Link]('locationerror', onLocationError);
[Link]({setView: true, maxZoom: 16});

Die konkreten JavaScript-Anweisungen sind wie gesagt API-spezifisch, aber recht gut
nachvollziehbar. Wenn der Benutzer die Seite besucht, wird nach Erlaubnis gefragt,
die Position zu verwenden, denn im Hintergrund wird [Link] genutzt
(Abb. 14.4).
Dann wird die Position auf der Karte angezeigt, zusammen mit einem Marker und
einem Kreis, der die Genauigkeit der Positionierung darstellt (Abb. 14.5).

14.3 DHTML, Zeichnen und Animationen

Zum Abschluss des Kapitels widmen wir uns dem Thema DHTML (Dynamic HTML),
was wir schon vielfach berührt haben, sowie verschiedener grafischer Techniken all-
gemein. DHTML ist ein Begriff, der eng mit dem DOM-Konzept zusammenhängt, aber
weder eindeutig noch standardisiert oder geschützt ist. Die wichtigsten Protagonisten im
Internet verstehen unter dynamischen HTML die Veränderungen einer Webseite, nach-
dem die Seite bereits beim Client (also im Browser) angelangt ist. Die konkrete Technik,
wie die Veränderung dabei realisiert wird, ist irrelevant, obgleich DHTML fast immer als
Verbindung von HTML, JavaScript und Style Sheets bezeichnet wird. Natürlich brau-
chen nicht zwingend alle drei Techniken beteiligt zu sein. Auch Animationen kann man
im Grunde zu DHTML zählen, wenn man für die Animation die gleichen Techniken
(HTML, CSS und JavaScript) verwendet. Wenn man überhaupt eine Differenzierung
362 14 Erweiterte Techniken

Abb. 14.5 Eine interaktive Karte mit der Position des Besuchers

zwischen DHTML und Animationen vornehmen will, dann geht das über den Zeit-
faktor. Denn Animationen laufen über einen längeren Zeitraum ab und erzielen ihre Wir-
kung durch diese zeitorientierten Veränderungen. Allgemein zählt die dynamische Ver-
änderung einer Webseite zu den eindrucksvollsten Anwendungen von JavaScript, aber
rein von der Programmierung zu den einfachsten. Die einzige Krux war die hohe In-
kompatibilität verschiedener Browsermodelle, aber diese Probleme nehmen in modernen
Browsern immer mehr ab.

14.3.1 DHTML und Animation mit reinem JavaScript oder CSS

Viele Effekte, die DHTML zuzuordnen sind, lassen sich rein mit JavaScript durch-
führen, zum Beispiel der Austausch und die Größenveränderung von Bildern. Wir haben
schon mehrfach gesehen, dass Sie einfach die klassischen HTML-Attribute des <img>-
Tags manipulieren und mit geeigneten Eventhandlern – etwa für die Reaktion auf das
Überstreichen mit dem Mauszeiger – koppeln müssen. Auch Animationen lassen sich
rein durch die zeitgesteuerte Manipulation von Grafiken durchführen. Schlüssel ist der
14.3 DHTML, Zeichnen und Animationen 363

rekursive Einsatz von [Link]. Auch CSS selbst bietet einige dynamische
Effekte an, die man als DHTML oder gar Animation bezeichnen kann. Gerade in den
neuen Versionen von CSS mittels sogenannter Keyframes. Aber bereits zu Beginn der
Verwendung von CSS gab es den Hover-Effekt. In so einem Fall ergibt sich eine Än-
derung eines Bereichs in der Webseite, wenn dieser Bereich mit dem Mauszeiger über-
strichen wird. Ganz einfach kann man Hover-Effekte für Hyperlinks mit der CSS-
Pseudo-Klasse :hover erzeugen. Meist verwendet man diesen Effekt bei Hyperlinks über
a:hover, aber im Prinzip kann man ihn auch bei anderen Elementen verwenden. Der Ho-
ver-Effekt wirkt sich aber immer nur auf das Element aus, das gerade mit dem Maus-
zeiger überstrichen wird. Von daher ist er unflexibler als eine Programmierung mit Ja-
vaScript. Rein mit CSS kann man auch den Mauszeiger verändern, indem an eine Wert-
zuweisung für die Eigenschaft cursor vornimmt. Dennoch – richtig interessant, flexibel
und leistungsfähig sind grafische Effekte immer dann, wenn HTML, CSS und JavaScript
kombiniert werden.

14.3.2 Verbinden von JavaScript und Style Sheets

Im DOM gibt es seit Version 2.0 das style-Objekt, worüber alle Stilinformationen von
sämtlichen Elementen bzw. Elementknoten mit einer sichtbaren Repräsentation in der
Webseite abgefragt und meist auch geändert werden können. Unabhängig vom konkreten
Zugriff auf ein Element werden Sie in allen Fällen die gleichen Eigenschaften und Me-
thoden zum Umgang mit dem Element zur Verfügung haben und dementsprechend auch
auf style zugreifen können.

 Vorsicht Die Stileigenschaften des style-Objekts unterscheiden sich teilweise


in der Schreibweise ein wenig von der Schreibweise bei den CSS-Regeln. Dies
führt einerseits leicht zu Fehlern, aber andererseits lassen sich viele Namen der
Eigenschaften gut aus CSS herleiten. Im Allgemeinen gilt, dass der Bindestrich
bei CSS-Attributen bei den Eigenschaften des style-Objekts entfällt und statt-
dessen der nachfolgende Buchstabe groß zu schreiben ist (Groß- und Klein-
schreibung ist zu beachten – die Camel-Notation). Beispiele:

• Aus font-size bei CSS wird fontSize beim style-Objekt.


• Aus background-color bei CSS wird backgroundColor bei der Benennung der
identischen Eigenschaft des style-Objekts.
• Aus text-align bei CSS wird textAlign beim style-Objekt.

Man sollte auch verkürzte Angaben aus CSS wie background statt background-
color bzw. backgroundColor beim Umgang mit dem style-Objekt vermeiden.
Achten Sie beim Umgang mit den Werten von Eigenschaften des style-Objekts
darauf, dass viele Werte mit Einheiten (etwa px oder pt) angegeben werden
364 14 Erweiterte Techniken

müssen. Wenn man diese Regel einhält, werden in den meisten Fällen Eigen-
schaften des style-Objekts direkt und trivial aus CSS-Attributen herzuleiten
sein (und umgekehrt).

Sie können mit dem style-Objekt eine Webseite komplett statisch formatieren, als ob Sie
Regeln direkt mit CSS setzen. Etwa so:

Beispiel

[Link]("h2")[0].[Link] = "red";
[Link]("h2")[1].[Link] =
"blue";
[Link]("body")[0].[Link] = "center";
[Link]("div")[1].[Link] = "36px";

Diese Anweisungen müssen einfach nach dem Laden des DOM-Baums ausgeführt
werden, etwa mit [Link] = init;, wenn diese in einer entsprechenden Funktion
notiert sind. Aber man sollte vermeiden, dass Sie im allgemeinen Fall so Ihre Web-
seiten statisch formatieren! Warum das? Nun – Sie geben damit die Trennung von
Funktionalität und Layout auf! Es ist primäres Ziel einer modernen Webapplikation,
dass Struktur, Layout und Funktionalität streng getrennt werden, und das haben wir
eben nicht. Und warum sollten Sie mit JavaScript und DOM-Objekten formatieren,
wenn das genauso gut direkt mit CSS (am besten mit externen Dateien) geht? Aber es
gibt einen Grund. Logik! Solange Sie rein auf Basis von CSS-Selektoren beim Laden
der Webseite alle notwendigen Formatierungen vornehmen können, sollten Sie die For-
matierung per style meiden wie der Teufel das Weihwasser. Aber wenn Sie Logik be-
nötigen, die Ihnen die CSS-Selektoren nicht bieten, oder aber Sie dynamisch Formatie-
rungen ändern wollen, nachdem die Seite bereits geladen wurde, haben Sie mit style das
perfekte Hilfsmittel.

[Link] Verändern des Inhalts der Webseite


Eine wichtige Anwendung von DHTML ist dynamisches Verändern des Inhalts eines
Elements in der Webseite. Viele dynamischen Effekte in einer Webseite können über das
Einfügen von neuen Informationen in die bereits geladene Webseite realisiert werden,
die über Style Sheets dann bei Bedarf noch geeignet formatiert werden, sei es ein Popup,
ein Kontextmenü, ein aufklappbares Menü, eine Menüleiste, ein Tool-Tipp oder wie
immer die bekannten Effekte heißen. Wenn Sie allgemein den Inhalt eines Elements än-
dern wollen, selektieren Sie das Element auf eine der üblichen Arten, um einen Element-
knoten zu erhalten. Unabhängig von dem konkreten Zugriff auf ein Element werden Sie
in allen Fällen die gleichen Eigenschaften und Methoden zum Umgang mit dem Element
zur Verfügung haben. Für den Zugriff auf den Inhalt ist die Eigenschaft innerHTML
am wichtigsten, mit der wir in vielen Beispielen in dem Buch arbeiten. Bisher haben
wir darüber meist nur Texte ausgetauscht, aber Sie können damit auch hervorragend
14.3 DHTML, Zeichnen und Animationen 365

vollständige HTML-Fragmente in eine Seite einfügen und damit diverse bekannte


DHTML-Effekte realisieren.

[Link] Grafikaktionen zeitverzögert aufrufen


Die Methode setTimeout erlaubt die zeitverzögerte Ausführung einer Aktion und dazu
geben Sie als ersten Parameter eine Funktionsreferenz und als zweiten Parameter die zu
wartende Anzahl von Millisekunden an. Schauen wir uns als Beispiel noch eine kleine
Endlosanimation an, die rekursiv die Breite eines CSS-formatierten DIV-Bereichs sowie
asynchron dazu ein Bild vergrößert. Es entsteht beim DIV-Bereich der Eindruck eines
Fortschrittsbalkens und beim Bild der Eindruck, als bewege sich der Inhalt auf den Be-
trachter zu (Abb. 14.6).
Wir wollen hier mit einer externen JavaScript-Datei [Link] arbeiten und auch die
CSS-Formatierungen in der Datei [Link] sind bei diesem Beispiel von Bedeutung.
Hier ist zuerst die HTML-Datei ([Link]):


    <link rel="stylesheet" type="text/css" href="lib/css/[Link]"
/>
    <script src="lib/js/[Link]"></script></head>
  <body><h3>Animationen</h3><div id="ani1"></div> <hr />
    <img src="images/[Link]" alt="IT" id="ani2"/></body></html>

Abb. 14.6 Zwei Objekte werden unabhängig voneinander animiert


366 14 Erweiterte Techniken

In der referenzierten CSS-Datei [Link] werden vor allen Dingen der DIV-Bereich und
das Bild in der Breite auf 0 Pixel festgelegt. Dazu selektieren wir die Objekte über die
jeweilige Id:

body { background-color: lightgray; }


h3 { background-color: black; color: white; text-align: center; }
#ani1 { width: 0px; height: 10px; background-color: red; }
#ani2 { width: 0px; }

Die Festlegung der Breite über CSS ist wichtig, denn diese CSS-Werte wollen wir per
JavaScript verändern und dazu ist es notwendig, dass der Wert vorher explizit initialisiert
wurde. Die Animationen werden in der JavaScript-Datei [Link] definiert und auch
dort über JavaScript-Eventhandler aufgerufen:

var i = 0, j = 0;
function animation1() {
  i++;
  [Link]("ani1").[Link] = i + "px";
  if (i > 500) i = 0;
  setTimeout(animation1, 2);
}
function animation2() {
  j++;
  [Link]("ani2").[Link] = j + "px";
  if (j > 400) j = 0;
  setTimeout(animation2, 10);
}
function init() {
  animation1();
  animation2();
}
[Link] = init;

Wir animieren die beiden Objekte mit vollkommen analogen Funktionen, die aber be-
wusst nicht (!) synchronisiert werden. Das bedeutet, dass die Veränderung der Breite
beider Objekte explizit in unterschiedlichen Zeitintervallen erfolgt. Das Beispiel hat
zwei strukturell interessante Stellen. Mit [Link]("ani1").style.
width = i + "px"; und [Link]("ani2").[Link] = j + "px"; werden je-
weils die Breite des DIV-Containers und die Breite des Bildobjekts jeweils in Abhängig-
keit von einer globalen Zählvariable gesetzt. Dazu kommt das style-Objekt zum Einsatz,
um darüber die CSS-Eigenschaften zu manipulieren. Die zweite interessante Stelle be-
trifft den zeitverzögerten Selbstaufruf der Funktionen animation1 und animation2. Die
beiden Funktionen werden beim Laden der Webseite unmittelbar mit der Funktion init
14.3 DHTML, Zeichnen und Animationen 367

gestartet, die mit dem Eventhandler [Link] aufgerufen wird. Das bedeutet, dass
die beiden Animationen unmittelbar (und endlos – also bis zum Verlassen der Webseite)
ablaufen.
Schauen wir uns noch ein Beispiel an, in dem über die Manipulation von style auf
einfache Weise Animationen entstehen. Die Webseite [Link] ist ganz einfach auf-
gebaut und enthält im Wesentlichen zwei Überschriften, die animiert werden. Dazu
werden in der Überschrift der Ordnung h3 der Inhalt dynamisch ausgetauscht und die
Hintergrundfarbe der Webseite verändert:


    <script src="lib/js/[Link]"></script></head>
  <body><h1>Interessante Webseiten</h1><h2 id="dyn1">[Link]</h2>
    <h3 id="dyn2">[Link]</h3></body></html>

So sieht die JavaScript-Datei [Link] aus:

var i = 0;
function ani() {
  var seite = [Link]("body")[0];
  var u1 = [Link]("dyn1");
  var u2 = [Link]("dyn2");
  switch(i++) {
    case 0 : [Link] = "red";
      [Link] = "blue";
      [Link] = "42px";
      [Link] = "center";
      [Link] = "gray";
      [Link] = "yellow";
      [Link]="[Link]";
      break;
    case 1 : [Link] = "yellow";
      [Link] = "black";
      [Link] = "12px";
      [Link] = "right";
      [Link] = "red";
      [Link] = "white";
      [Link]="[Link]";
      break;
    case 2 :
      [Link] = "red";
      [Link] = "white";
      [Link] = "22px";
      [Link] = "left";
      [Link] = "lightgray";
368 14 Erweiterte Techniken

      [Link] = "blue";
      [Link]="[Link]";
      break;
    default : [Link] = "cyan";
      [Link] = "gray";
      [Link] = "cyan";
      [Link]="[Link]";
  }
  i %= 4;
  setTimeout(ani,'2500');
}
[Link]=ani;

Der Code ist etwas länger, weil viel einzelne CSS-Eigenschaften dynamisch gesetzt wer-
den. Das Beispiel ist als Endlosanimation konzipiert, denn beim Laden der Webseite
wird mit dem Eventhandler onload die Funktion ani aufgerufen, die eine zeitgesteuerte
Animation startet, die solange weiterläuft, bis die Webseite verlassen wird. Dabei grei-
fen wir für die Animation wieder auf einen rekursiven Aufruf zurück, der über window.
setTimeout verzögert wird. Die Webseite beinhaltet eine Überschrift der Ordnung 1. In
der Funktion ani werden style die Stilinformationen der Überschriften sowie der Inhalt
der Überschrift vom Typ h3 verändert. Zusätzlich wird die Hintergrundfarbe des Körpers
der Webseite geändert. Die Animation benutzt zum Austausch der Stilinformationen den
Modulo-Operator, um eine Zahl zwischen 0 und 4 zu berechnen, die in der folgenden
switch-case-Anweisung zur Auswahl der tatsächlichen Stilinformation verwendet wird.
Im Abstand von 2,5 Sekunden wechseln so die Schriftfarbe und Hintergrundfarbe der
Überschrift und zusätzlich die Hintergrundfarbe der Webseite.
Zum Abschluss soll noch eine Animation durch Manipulation von Grafiken folgen.
Das ist erst einmal die Basiswebseite ([Link]):


  <style>h1 { background-color: #EEE; }
    #p1 {position: absolute; top: 150px; left: 100px; }
    #b1 { width: 40px; height: 30px; }</style>
    <script src="lib/js/[Link]"></script></head>
  <body><h1>Eine Bildanimation</h1><button>Start</button>
    <button>Zurücksetzen</button><div id="p1"><img src="images/
[Link]"
id="b1" /></div><div id="ausgabe"></div></body></html>

Wir haben hier eine einfache Webseite mit einer Grafik und zwei Schaltflächen vorliegen,
die wie internem mit CSS formatiert wird. Für dieses Beispiel ist die CSS-Formatierung
wieder zum Teil von Bedeutung. Die Breite und Höhe von dem Bild werden mit Style
14.3 DHTML, Zeichnen und Animationen 369

Sheets festgelegt und der umgebende DIV-Bereich positioniert. Kommen wir zu Java-
Script-Datei [Link]:

var mB1 = new Image(), mB2 = new Image();


[Link] = "images/[Link]";
[Link] = "images/[Link]";
var b1, b2, a;
var counter = 0;
function ani() {
  counter++;
  [Link] = [Link];
  [Link] = (30 + counter * 3) + "px";
  [Link] = (40 + counter * 4) + "px";
  [Link] = "Breite: " + [Link] + ", Höhe: " + [Link].
height;
  if (counter < 150) [Link](ani, 1);
  else {
    [Link] = [Link];
    counter = 0;
  }
}
function retur() {
  [Link] = [Link];
  [Link] = 30 + "px";
  [Link] = 40 + "px";
  [Link] = "";
}
function init() {
  b1 = [Link]("b1");
  [Link] = 30 + "px";
  b2 = [Link]("b2");
  a = [Link]("ausgabe");
  with (document) {
    getElementsByTagName("button")[0].onclick = ani;
    getElementsByTagName("button")[1].onclick = retur;
  }
}
[Link] = init;

Wenn Besucher nach Laden der Seite auf die erste Schaltfläche klicken, werden die Gra-
fik vergrößert und die Breite und Höhe der Grafik jeweils ausgegeben (Abb. 14.7). Die
Animation erfolgt durch die Zeitverzögerung durch den Einsatz von [Link]
und den rekursiven Aufruf von ani. Beachten Sie aber die unscheinbare Stelle bei der
370 14 Erweiterte Techniken

Abb. 14.7 Die Animation läuft – beachten Sie die Pixel-Angaben der Größe

Ausgabe der Breite und Höhe. Sie sehen hier die Größenangaben mit der Einheit px.
Und dahinter steckt viel mehr als nur eine optische Feinheit. Am Ende der Animation
wird die Grafik ausgetauscht (Abb. 14.7).
In der Funktion ani wird bei jedem Aufruf wieder die Variable counter um den Wert
1 erhöht (counter +  + ;). Und damit wird die Breite und Höhe des Bildes verändert. Aber
dazu greifen wir hier auf das style-Objekt zu. Beachten Sie, dass die Breite und Höhe
von dem Bild jedoch mit Style Sheets festgelegt wurden. Beim Zuweisen der Breite und
Höhe auf diesem Weg müssen wir deshalb die Einheit "px" anhängen. Das ist ein ent-
scheidender Punkt, denn wenn Sie mit CSS einmal die Maße von einem Objekt in der
Webseite festgelegt haben, hat diese Festlegung eine höhere Priorität als die Festlegung
der Ausmaße in HTML mittels Attributen. Die Funktion zum Zurücksetzen der Anima-
tion stellt wieder den Originalzustand beim Laden der Webseite wieder her. Beachten Sie
auch hier die Notwendigkeit zur Angabe der Einheit (Abb. 14.8).

14.3.3 Zeichnen mit Canvas und SVG

Ein optisches Highlight von HTML5 ist, dass Sie mithilfe von Canvas-Objekten oder
SVG zeichnen können. Die Idee hinter solchen Bereichen zum Zeichnen ist auch keines-
falls neu, sondern in vielen Programmiersprachen schon seit zig Jahren etabliert. Die
14.3 DHTML, Zeichnen und Animationen 371

Abb. 14.8 Die Animation ist beendet und das Bild wurde ausgetauscht

Methoden, die etwa ein Canvas-Objekt bereitstellt, dürften daher für Java-Programmierer
kalter Kaffee und ohne große Erklärung anzuwenden sein. Sie werden auch zwischen
dem Zeichnen mit Canvas-Methoden und SVG eine hohe Ähnlichkeit kennenlernen.
Wahrscheinlich werden in Zukunft Web-Frameworks und Grafikprogramme geeignete
Methoden bereitstellen, um auf einer höheren Ebene diese Zeichenoperationen auszu-
führen. Wir wollen uns dennoch hier ganz nativ dem Canvas-Objekt und dem Umgang
mit SVG nähern.

Neue Zeichenoperationen mit CSS3 – insbesondere Keyframes – ermöglichen oft sehr ähnliche
Dinge.

[Link] Canvas
Betrachten wir zuerst den Umgang mit Canvas-Techniken. Die meisten Ausführungen
dazu werden sich auf SVG (und auch CSS3) übertragen lassen. Allerdings gibt es einige
Besonderheiten, die bereits damit anfangen, dass ein Canvas-Bild eine Pixelgrafik
ist, während SVG eine Vektorgrafik darstellt. In beiden Fällen wird aber nur die Be-
rechnungsvorschrift in den Client geladen und die Grafik dort erzeugt. Das spart gegen-
über der Übertragung einer vollständigen Grafik vom Server natürlich viel Daten und
damit wird auch die Performance einer Seite gewaltig verbessert. Nur muss der Client
die Ressourcen aufbringen, die Grafik dynamisch und schnell zu generieren.
372 14 Erweiterte Techniken

[Link].1 Das Canvas-Element
Als Erstes müssen Sie zum Zeichnen ein Canvas-Objekt zur Verfügung haben. HTML5
stellt dazu das Tag <canvas> zur Verfügung. Das ist im Grunde ein normales Block-
element in einer Webseite, in dem man aber über die Methoden der Objektrepräsentation
im DOM5-Baum zeichnen kann. Obwohl man die Breite und Höhe von Blockelementen
in modernen Webseiten mittels CSS festlegen soll, gibt man bei Canvas-Elementen der-
zeit die Breite und Höhe zudem mit HTML-Attributen an. Dies sorgt aktuell für einen
zuverlässigeren Aufbau in Browsern. Beispiel:

Beispiel

<canvas width="250" height="150"></canvas>

[Link].2 Der Grafikkontext
Ein Canvas-Objekt stellt sogenannte Grafikkontexte zur Verfügung. Das sind erst ein-
mal einfach die Bereiche, in denen man konkret zeichnen kann, allerdings im Sinn der
Objektorientierung mit implementierten Methoden. Der Bereich zum Zeichnen ist
nach der Initialisierung erst einmal leer. Im Konzept von HTML5 sind sowohl zwei-
dimensionale als auch dreidimensionale Bereiche vorgesehen, wobei zum Zeitpunkt
der Bucherstellung nur die zweidimensionalen Bereiche richtig unterstützt werden.
Zugriff auf den Grafikkontext erfolgt über ein Canvas-Objekt, das Sie wie immer als
node adressieren, und dessen Methode getContext. Als Parameter geben Sie die Art des
Grafikkontextes an (zweidimensional oder dreidimensional). Derzeit macht nur "2d"
Sinn. Etwa so:

Beispiel

var zb = [Link]('canvas')[0].getContext('2d');

In der Regel werden Sie mit einer Id auf das Canvas-Objekt zugreifen.

 Eine Eigenheit von Canvas-Bereichen ist, dass eine einmal dort gezeichnete Form
erhalten bleibt, wenn sie nicht verdeckt wird, auch wenn Sie eine neue Form in
dem Canvas-Bereich erzeugen. Wenn Sie immer einen leeren Bereich zum Zeich-
nen einer neuen Form benötigen, müssen Sie sich selbst darum kümmern, dass der
Canvas-Bereich beim Zeichnen der neuen Form leer ist.

[Link].3 Koordinatensystem und Gitter


Grundlage jeder grafischen Ausgabe in einem Canvas-Bereich (aber auch bei ande-
ren Techniken wie CSS3 oder SVG) ist ein Koordinatensystem, in dem mit zwei Wer-
ten ein Tupel spezifiziert werden kann, an dem eine bestimmte Ausgabe erfolgen soll,
und ein unsichtbares überlagertes Gitter (Grid), das den Bereich in Einheiten ska-
liert und das man als Grafikkontext selektiert. In dem in HTML5/DOM5 verwendeten
14.3 DHTML, Zeichnen und Animationen 373

Koordinatensystem lässt sich durch die Angabe von zwei Werten (sogenannte Vektoren,
die vom Ursprung des Koordinatensystems ausgehen) ein beliebiger Punkt in dem Ko-
ordinatensystem eindeutig festlegen. Dies sind ein x-Wert und ein y-Wert, wie man es
meist aus der Schule kennt. Die Maßeinheit ergibt sich aus dem Grid, wobei normaler-
weise eine Einheit im Grid mit einem Pixel im Canvas-Element korrespondiert. Das
hier verwendete Koordinatensystem unterscheidet sich aber ein wenig von dem im
Mathematikunterricht oft verwendeten System. Die obere linke Ecke des Canvas-Be-
reichs wird mit (0, 0) – also dem Koordinatensystemursprung – abgebildet (sonst ist es
meist die untere linke Ecke). Dabei ist der x-Wert die Anzahl der Bildschirmpixel von
links ausgehend, also in der Waagerechten, und y ist die Zahl der Pixel, von oben an-
gefangen, also in der Senkrechten. Das Koordinaten-Tupel (42, 10) beschreibt z. B. einen
Punkt, der 42 Pixel vom linken Rand und 10 Pixel vom oberen Rand des Canvas-Be-
reichs entfernt ist. Im Grunde sollten Sie das aber kennen, wenn Sie an die Positionie-
rung von Elementen mit CSS und den Eigenschaften top und left denken. In der Grund-
einstellung liegt der Ursprung des Grid auf dem Ursprung des Koordinatensystems.
Den Ursprung eines Grid können Sie allerdings innerhalb des Canvas-Elements mit ge-
eigneten Methoden verschieben, rotieren oder skalieren.
Jede Methode von einem Grafikkontext mit einer konkreten Ausgabe verwendet
irgendwelche Koordinatenangaben, denn es muss ja festgelegt werden, wo eine Ausgabe
beginnt, und oft noch, bis wohin die Ausgabe erfolgen soll.

[Link].4 Die Möglichkeiten zum Zeichnen über Canvas-Objekte


Ein Objekt vom Typ Canvas verfügt über den Grafikkontext über verschiedene Arten von
Methoden zum Zeichnen von Grafiken, unter anderem über die nun folgenden:

• Methoden zum Zeichnen von Linien und Polygonen,


• Textmethoden,
• Methoden zum Zeichnen von Rechtecken, Kreisen, Ellipsen etc.,
• Methoden zum Zeichnen von Bogensegmenten und Kurven,
• Methoden zum Zeichnen von Bildern.

Dazu kommen noch Methoden zur Angabe von Farben, Farbverläufen, Füllungen, Um-
randungen usw. Das 2D-API behandelt Formen, Text und Bilder im Grunde gleich und
unterstützt damit einen einheitlichen Mechanismus für Veränderungen (etwa Rotation,
Verzerrung, Verschiebung und Skalierung). Allerdings versteht man im Canvas-Konzept
nur Rechtecke als sogenannte Grafikprimitive. Das unterscheidet sich von den Grafik-
konzepten unter Java oder SVG, wo auch andere Formen als Grafikprimitive verstanden
werden. Diese anderen Formen müssen bei Canvas-Objekten aus sogenannten Pfaden
kombiniert werden, wobei das API glücklicherweise Methoden bereitstellt, mit denen
man einfach Formen erstellen kann. In jedem Fall kann man für jedes Grafikobjekt
spezifische Charakteristiken wie Höhe und Breite, Farbe, Füllmuster, Transparenz, Um-
randungen usw. angeben, aber auch Überblendungen und ähnlich Effekte.
374 14 Erweiterte Techniken

Wir werden hier im Buch nur punktuelle Schwerpunkte setzen, um die grundlegenden Vorgänge zu
besprechen und das Konzept als solches zu verstehen.

[Link].5 Rechtecke
Ein Rechteck ist im Canvas-Konzept eine herausgehobene Form, denn Sie wird als grafi-
sches Primitives verstanden. Betrachten wir kurz die genauen Details. Es gibt im API
drei Funktionen zum Zeichnen:

• fillRect(x, y, breite, höhe): gefülltes Rechteck,


• strokeRect(x, y, breite, höhe): ein leeres Rechteck (nur der Rahmen),
• clearRect(x, y, breite, höhe): Leeren eines Bereichs und transparent machen.

Die ersten beiden Parameter sind jeweils die X- und Y-Koordinate von der linken oberen
Ecke des Bereichs (relativ zum Ursprung) und die anderen beiden die Breite und Höhe
des Bereichs.

[Link].6 Farben und Farbverläufe


Bei Canvas können auch verschiedene Farben bereitgestellt und verwendet werden. Die
Farben können bei Texten, Füllungen, Farbverläufen (Gradienten), Hinter- und Vorder-
grund etc. als Eigenschaftswerte zugewiesen werden. In der Grundeinstellung wird
immer Schwarz verwendet. In vielen Fällen werden Farben bei Eigenschaften auf-
tauchen, die im Bezeichner fill (Füllung) oder stroke (Rahmen bzw. Außenlinie) auf-
weisen. Ganz wichtig sind fillStyle und strokeStyle. Die zugewiesene Farbe kann ein
String mit einem Farbnamen oder ein CSS-Farbwert sein. Bei CSS-Werten kann man
selbstverständlich auch die rgb- oder die rgba-Funktion verwenden. Darüber hinaus kann
man auch einen Farbverlauf (ein Gradientenobjekt) oder ein Patternobjekt zuweisen.
Beachten Sie, dass eine eingestellte Farbe (wie auch andere Eigenschaften des Grafik-
kontextes) so lange wirkt, solange keine neue Farbe dem Grafikkontext zugewiesen wird.

[Link].7 Ein paar Canvas-Beispiele


Damit Butter bei die Fische kommt, betrachten wir ein erstes Beispiel ([Link]):


    <script src="lib/js/[Link]"></script></head>
  <body><h1>Canvas</h1><canvas width="250" height="150">
      <img src="images/[Link]" alt="Alternative zu Canvas"/></canvas>
  </body></html>

In der Webseite sehen Sie ein Canvas-Element mit einem Alternativinhalt (hier ein Bild),
der angezeigt werden soll, wenn ein Browser dieses neue Canvas-Element nicht unter-
stützt. Hier ist die referenzierte JavaScript-Datei [Link]:
14.3 DHTML, Zeichnen und Animationen 375

function zeichnen() {
  var zb =
    [Link]('canvas')[0].getContext('2d');
  [Link] = "rgb(200,0,0)";
  [Link](15, 15, 155, 50);
  [Link] = "rgba(0, 20, 250, 0.1)";
  [Link](30, 40, 255, 250);
  for (var i = 3; i < 255; i++) {
    [Link] = 'rgba(' + i + ',' +
      (255 - i) + ',' + i + ',' + (1 / (i / 3)) + ')';
    [Link](i * 2, i, 25, 25 + i);
  }
  for (var i = 3; i < 255; i++) {
    [Link] = 'rgb(' + i + ',' + (255) + ',' + i + ')';
    [Link](200 + i * 2, i, 55 + i, 40);
  }
};
function init() {
    zeichnen();
}
[Link] = init;

In der Funktion zeichnen, die beim Laden der Webseite ausgeführt wird, selektieren wir
den Grafikkontext und speichern die Referenz darauf in der Variablen zb. In den folgen-
den Zeilen wird eine Methode dieses zweidimensionalen Grafikkontextes verwendet, die
bereits durch den Namen fillRect klar sein dürfte. Es werden hier gefüllte Rechtecke ge-
zeichnet (Abb. 14.9). Wir legen auch mit CSS-Standardfunktionen die Farbfüllung von
Objekten fest (einmal mit Transparenz). Hierzu verwenden wir fillStyle. In der folgen-
den Schleife werden die Werte für die CSS-Funktion rgba in Abhängigkeit vom Zähl-
index der Schleife berechnet (sowohl die Rot-, Grün- und Blauanteile als auch die Trans-
parenz). Darüber hinaus werden die Position und die Höhe des Rechtecks, das in jedem
Schleifendurchlauf gezeichnet wird, in Abhängigkeit von dem Zählindex verändert. Das
führt dazu, dass sich die Farbe in kleinen Schritten verändert sowie in der Transparenz
abschwächt und die Grafik zu einer neuen perspektivischen Form verschwimmt, die mit
einer Art Farbverlauf gefüllt ist (Abb. 14.9). Hier zeigt sich ganz deutlich, dass eine neue
Form im Grafikkontext nicht dazu führt, dass die vorhandenen Formen gelöscht werden.
Neue Formen werden einfach hinzugefügt. In der zweiten Schleife setzen wir die Eigen-
schaft strokeStyle in einer Schleife über die CSS-Funktion rgb. Damit wir einen Rahmen
sehen, verwenden wir statt fillRect für ein gefülltes Rechteck strokeRect, was nur einen
Rahmen zeichnet (Abb. 14.9).
Wir haben gerade so eine Art Farbverlauf über Schleifen demonstriert, aber bei Canvas-
Objekten muss man das gar nicht von Hand erledigten. Es gibt bereits implementierte
376 14 Erweiterte Techniken

Abb. 14.9 Farben und


Rechtecke

Farbverläufe (Gradienten) – das canvasGradient-Objekt. Die meisten visuellen Elemente


können Sie damit auch füllen. Ein Farbverlauf bedeutet eine gleichmäßige Veränderung
von einer Farbe in eine andere. Die Endpunkte der Farbveränderungen zwischen zwei
Farben werden Stopps genannt. Stopps können an beliebig vielen Punkten zwischen 0
und 1 eingefügt werden und erlauben so das Zusammensetzen von mehreren Farbüber-
gängen zu einem einzigen Farbverlauf. Später werden sie skaliert, um in den gewählten
Zielbereich für den Farbverlauf zu passen. Aufeinanderfolgende Stopps müssen immer
einen größeren offset-Wert haben als die Vorgänger. Es gibt nun lineare Farbverläufe,
die mit createLinearGradient(×1, y1, × 2, y2) erstellt werden und eine horizontale Farb-
verschiebung zwischen zwei Endpunkten (×1, y1) und (×2, y2) vornehmen. Die Ko-
ordinaten erlauben die Ausrichtung des Farbverlaufs in horizontaler und vertikaler sowie
kombinierter Richtung. Nun war schon von den Stopps die Rede. Dazu gibt es die Me-
thode addColorStop(position, farbe). Die Position ist ein besagter Wert zwischen 0.0
and 1.0 für eine relative Position der Farbe im Gradient und das zweite Argument ist die
Zielfarbe. Sie können zu einem Gradienten beliebig viele Stopps hinzufügen und damit
einen Farbverlauf über mehrere Farben erzeugen. Beispiel für einen linearen Farbverlauf
([Link]):

[Link] = function() {
  var zb = [Link]('canvas')[0].getCon-
text('2d');
  var lg1 = [Link](00, 00, 400, 175);
  [Link](0, 'white');
  [Link](1, 'black');
  [Link] = lg1;
  [Link](0, 0, 650, 195);
14.3 DHTML, Zeichnen und Animationen 377

  var lg2 = [Link](0, 0, 650, 0);


  [Link](0, '#00ABEB');
  [Link](0.3, '#fff');
  [Link](0.5, 'red');
  [Link](0.75, 'yellow');
  [Link](1, '#fff');
  [Link] = lg2;
  [Link](0, 200, 650, 450);
};

Mit [Link] erzeugen wir einen linearen Gradienten, der von der Ko-
ordinate 0,0 bis zur Koordinate 400,175 verläuft. Formen, die kleiner sind, werden die-
sen Gradienten beschneiden, wenn er zugewiesen wird. Sind die Formen jedoch größer,
werden die Bereiche außerhalb des Umfangs des Gradienten mit der Endfarbe deckend
gefüllt. Beachten Sie, dass wir keinen horizontalen Farbverlauf gewählt haben, sondern
durch den vierten Parameter einen Zielpunkt in Y-Richtung angeben, der tiefer als der
Y-Startpunkt liegt. Damit erhalten wir einen Farbverlauf nach rechts unten (Abb. 14.10).
Mit [Link] legen wir zwei Stopps fest, die man minimal braucht, um von
einer expliziten Startfarbe zu einer expliziten Endfarbe zu kommen (Vorgabefarbe wäre
immer Schwarz). In unserem Beispiel haben wir also einen Farbverlauf von Weiß nach
Schwarz. Der zweite lineare Gradient ist rein horizontal. Das erkennen Sie daran, dass
der Wert des zweiten und des vierten Parameters (die Y-Angaben) gleich sind. Dafür ver-
wenden wir dieses Mal mehrere Stopps, um über verschiedene Farben zu gehen.
Sie sollten sich bei Farbverläufen immer klarmachen, dass Sie nicht die Form selbst
damit füllen. Eine Form wird an einer gewissen Stelle gezeichnet und der Farbverlauf
scheint quasi durch. Wenn sich also ein Farbverlauf von einer gewissen Koordinatenangabe

Abb. 14.10 Lineare


Gradienten
378 14 Erweiterte Techniken

zu einer anderen erstreckt, wird eine Form nur den Teil von dem Farbverlauf anzeigen, in
dem sie diesen Bereich überdeckt.

 Ebenso gibt es im Canvas-Konzept kreisförmige (radiale) Gradienten.

14.3.4 Umgang mit Scalable Vector Graphics – SVG

Zu den wichtigen Neuerungen von HTML5 zählt definitiv die Unterstützung von SVG
(Scalable Vector Graphics – skalierbare Vektorgrafiken), wobei SVG schon lange vor
HTML5 verfügbar war. Sie werden viele Ähnlichkeiten zu Canvas bei der konkreten An-
wendung sehen. Aber es gibt auch gravierende Unterschiede in der technischen Grund-
ebene.

[Link] Grundlagen zu SVG
Bei SVG handelt es sich um die vom W3C schon recht lange empfohlene XML-­
Spezifikation zur Beschreibung zweidimensionaler Vektorgrafiken. Das beginnt bei
einfachen grafischen Formen wie Kreise oder Rechtecke und führt hin zu komple-
xeren Strukturen wie etwa Diagramme zur Darstellung von Messwerten, aber auch
die Beschreibung von abgerundeten Schaltflächen oder sonstigen per Formel zu be-
schreibenden Grafikobjekten. Auch Animationen werden von SVG unterstützt (mittels
einer anderen XML-Spezifikation namens SMIL – Synchronized Multimedia Integ-
ration Language). Ebenso kann der SVG-DOM nach der offiziellen Spezifikation mit-
hilfe eingebetteter Funktionen über ECMAScript und ähnliche Sprachen manipuliert
werden. Auch so kann man Animationen programmieren. Da SVG ein XML-basiertes
Dateiformat ist, sind Inhalte von SVG-Dateien leicht zugänglich. Sie können prinzipiell
auch direkt mit einem Texteditor bearbeitet werden. Die empfohlene Dateiendung von
SVG-Dateien ist .svg und der MIME-Typ ist image/svg + xml. Anzeigen können Sie es
entweder, wenn Sie es direkt in einem Browser laden (Abb. 14.11) oder als Teil einer
Webseite. Als XML-Dokument wird eine SVG-Datei in einer Baumstruktur aus ver-
schiedenen Elementen und diesen Elementen zugewiesenen Attributen aufgebaut. Jede
SVG-Datei beginnt damit mit der XML-Deklaration und der Dokumenttypdeklaration,

Abb. 14.11 Eine SVG-Grafik


14.3 DHTML, Zeichnen und Animationen 379

die den benutzten Namensraum beschreibt. Dazu wird bei Letzterem ein Verweis auf die
entsprechende DTD-Datei eingefügt.
Dem Prolog folgt natürlich das Root-Element, das im Fall von SVG <svg> lautet. Das
wäre eine schematische SVG-Datei:

Beispiel

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "[Link]
phics/SVG/1.1/DTD/[Link]">
<svg xmlns="[Link] xmlns:xlink="[Link]
[Link]/1999/xlink"
  xmlns:ev=[Link]
  version="1.1" baseProfile="full" width="800mm" height="600mm">

</svg>

Mit den Attributen width und height des SVG-Starttags <svg> wird die Größe der Grafik
definiert, die mit SVG beschrieben wird, wenn sie in eine Webseite integriert wird.

[Link].1 Das Koordinatensystem
SVG ist eine Vektorgrafik und erlaubt damit sowohl ganzzahlige als auch gebrochene
Koordinatenangaben, die wie üblich vom Koordinatenursprung in der linken oberen
Ecke des Zeichenbereichs ausgehen. Als Einheiten können in SVG sowohl relative (wie
em oder Prozent) als auch absolute Angaben (wie Meter oder Zoll) gewählt werden.
Grundsätzlich stehen in SVG alle von CSS bekannten Einheiten zur Verfügung.

[Link].2 Die Elemente
SVG unterstützt drei grundsätzlich unterschiedliche Typen von Elementen:

• Vektorgrafiken, aufgebaut aus grafischen Primitiven.


• Rastergrafiken, die extern eingebunden werden können.
• Text in einer bestimmten Schriftart, die dem Render-Programm zur Verfügung stehen
muss.

Wie bei fast allen grafischen Systemen bauen alle grafischen Elemente in SVG auf ein-
fachen Grundelementen (grafischen Primitiven) auf. Durch Kombination mehrerer grafi-
scher Primitive lassen sich komplexe Objekte erzeugen. Für alle grafischen Elemente
können Füllung, Art der Umrisslinie und Transparenz durch Attribute festgelegt wer-
den. In SVG sind im Wesentlichen alle grafischen Elemente verfügbar, die man auch
aus anderen Konzepten wie Java2D, JavaFX oder eben der Canvas-Technologie kennt,
etwa Rechtecke, Kreise, Ellipsen, Pfade, Polygone etc. Die Elemente werden durch
380 14 Erweiterte Techniken

weitgehend sprechende Elemente in der XML-Struktur beschrieben. Wer auch immer


mit der Programmierung von grafischen Elementen Erfahrung hat, wird die meisten Ele-
mente sofort erkennen und anwenden können.

• Das Element <path /> definiert eine Linie bestehend aus einer beliebigen Kombination
von Strecken, Ellipsenbögen, quadratischen und kubischen Bézierkurven sowie Neu-
positionierungen, die mithilfe von relativen oder absoluten Koordinaten beschrieben
werden. Der Pfad kann geschlossen, gefüllt und als Clipping-Pfad verwendet werden.
Der Pfad ist das mächtigste Grafikelement in SVG. Praktisch alles, was durch an-
dere Grafikelemente gezeichnet werden kann, kann durch ein Pfadelement äquivalent
dargestellt werden. Damit ist die Anwendung aber auch nicht ganz einfach und die
weiteren Grafikelemente vereinfachen die Anwendung.
• Das Element <circle /> definiert einen Kreis, der durch seinen Radius und durch die
Position des Mittelpunkts beschrieben wird.
• Das Element <ellipse /> definiert eine Ellipse, die durch ihre zwei Halbachsenradien
sowie, analog zum Kreis-Element, durch die Position ihres Mittelpunkts beschrieben
wird.
• Sehr einfach ist das Rechteck, das durch das Element <rect /> über die Position seiner
oberen linken Ecke, seiner Breite und seiner Höhe definiert wird. Optional können die
Ecken des Rechtecks abgerundet werden.
• Das Element <line /> definiert eine einfache gerade Linie (Strecke), die über die Ko-
ordinaten ihrer beiden Endpunkte beschrieben wird.
• Der Polygonzug, der über das Element <polyline /> definiert wird, gibt eine beliebige
Zahl von Punkten an, die als Polygonzug durch Strecken verbunden werden. Die
Punkte werden über Koordinatenpaare beschrieben.
• Das Element <polygon /> definiert ein Vieleck, das über seine Eckpunkte beschrieben
wird. Wie beim Polygonzug werden auch hier die Eckpunkte als Koordinatenpaare
angegeben und in der Reihenfolge ihrer Definition miteinander verbunden.
• Mit dem Element <text /> kann man in SVG Text in die Grafik einbetten. Das Text-
element kann über Attribute, wie zum Beispiel Schriftgröße und Schriftart, Lauf-
richtung und Wortabstand formatiert werden. Text kann zudem über Unterelemente
(<tspan />) strukturiert werden.
• Das Element (<image />) bindet eine Rastergrafik ein, die über eine Pfadangabe,
Breite und Höhe sowie die Position beschrieben wird.
• Man kann in SVG auch eine Gruppierung von Elementen vornehmen. Dazu dient das
Gruppenelement (<g />). Transformationen und Styling, die auf das Gruppenelement an-
gewendet werden, gelten für alle damit zusammengefassten untergeordneten Elemente.

[Link].3 Stilinformationen
Jedes Element in SVG kann durch Angabe von Styling-Eigenschaften in seiner optischen
Erscheinung angepasst werden. Das geschieht durch das style-Attribut oder Attribute zu
14.3 DHTML, Zeichnen und Animationen 381

einzelnen Eigenschaften wie fill oder stroke. Das sollte mit den Erfahrungen aus dem
Canvas-Konzept und DHTML sofort zu verstehen sein. Verändert werden können zum
Beispiel Füllung, Konturlinie und Transparenz der Elemente sowie bei Text die Eigen-
schaften der Schrift. Die Styling-Eigenschaften richten sich dabei in weiten Teilen nach
XSL und CSS. Über zusätzliche Elemente können nicht nur Farben, sondern auch Farb-
verläufe und Muster verwendet werden. Ein Beispiel für eine SVG-Datei mit Linien,
Rechtecken und Kreisen (Abb. 14.11) – [Link]:

<?xml version="1.0" encoding="UTF-8"?>


<svg xmlns="[Link] version="1.1" baseProfi-
le="full"
  width="700px" height="400px" viewBox="0 0 700 400">
  <!-- Eine Line --> <line x1="0" y1="200" x2="700" y2="200"
stroke="black" stroke-width="10px"/>
  <!-- Ein Rechteck --> <rect x="100" y="100" width="500"
height="200"
    fill="blue" stroke="black" stroke-width="10px"/>
  <!-- Eine weitere Line --> <line x1="180" y1="370" x2="500"
y2="50"
stroke="black" stroke-width="10px"/>
  <!-- Ein Kreis --> <circle cx="250" cy="275" r="80" stroke="red"
fill="green" stroke-width="5px"/>
</svg>

[Link].4 Transformationen
Jedes Element und jede Gruppe von Elementen kann in SVG durch eine Reihe affiner
Transformationen in Position, Orientierung und Form verändert werden (Abb. 14.12).
Die Transformation wird einem beliebigen Element als Attribut transform angehängt
([Link]):


  <rect x="100" y="100" width="500" height="200" fill="blue"
stroke="black" stroke-width="10px" transform="rotate(-10 525 15)"/>

[Link].5 Hyperlinks in SVG
SVG-Elemente können in ein HTML a-Tag eingesetzt werden. Sie machen damit also
Elemente in der SVG-Grafik sensitiv und verwenden die auch in HTML übliche Technik.
Hier sind zwei Elemente als Hyperlinks ausgezeichnet ([Link]):


<a href="[Link] cx="150" cy="75" r="50" stroke="red"
fill="green" stroke-width="5px"/></a>
382 14 Erweiterte Techniken

Abb. 14.12 Das Rechteck wurde rotiert

<a href="[Link] x="300" y="100" width="100"


height="20"
fill="blue" stroke="black" stroke-width="1px"/></a>

[Link].6 Integration von SVG in eine Webseite


SVG-Dateien kann man wie gesagt direkt in einem Browser anzeigen, aber SVG laubt
auch auf verschiedene Weise die nahtlose Integration in eine Webseite, wenn ein Brow-
ser die Technik grundsätzlich unterstützt. Im Grunde stehen die folgenden Möglichkeiten
zur Verfügung:

• Einbindung mit dem object-Tag,


• Verwendung im src-Attribut des img-Tags,
• SVG-Code direkt in die HTML-Datei schreiben,
• Mit CSS etwa als Hintergrundbild verwenden,
• In einem iframe einsetzen.

Für die Unterstützung von älteren Browsern ist die Verwendung des object-Tags ganz
sinnvoll, aber eigentlich auch nur noch für den Zweck.

Beispiel

<object data="[Link]" type="image/svg+xml"></object>

Die restlichen Arten der Einbindung sollten klar sein, weil sie sich in Nichts von der
Einbindung anderer Ressourcen unterscheiden. Allerdings kann die Wahl der Einbindung
14.3 DHTML, Zeichnen und Animationen 383

relevant werden, wenn Sie per JavaScript den Zugriff auf den DOM-Baum des SVG-Ob-
jekts vornehmen wollen. SVG direkt im HTML-Markup zu notieren, ist dazu eine sehr
komfortable Variante. Man nutzt das <svg>-Element direkt in einer Webseite und das ist
dann so etwas wie eine XML-Dateninsel. Bei den anderen Arten der Einbindung wird
ein Zugriff auf Bestandteile der SVG-Grafik aufwendiger.

[Link] Programmierung von SVG mit JavaScript


SVG kann mithilfe der DOM-Struktur dynamisch per JavaScript angepasst werden. Dabei
stehen alle bekannten Möglichkeiten zur Verfügung. Es können Elemente manipuliert, neu
erzeugt und eingefügt oder in Größe und Gestalt verändert werden. Die Programmierung
von SVG ist vollkommen intuitiv, wenn man DOM-Programmierung kennt. Wir ändern
bei unserem Beispiel einmal die Farbe eines circle-Elements beim Überstreichen mit dem
Mauszeiger. Hier ist zunächst die Webseite [Link] mit zwei eingebetteten SVG-Grafiken.


  <script src="lib/js/[Link]"></script></head><body><h1>SVG</h1>
  <img src="[Link]" width="200" id="svg1"/>
  <svg xmlns="[Link] version="1.1" baseProfi-
le="full"
width="700px" height="400px" viewBox="0 0 700 400">
  <!-- Eine Line -->
  <line x1="0" y1="200" x2="700" y2="200" stroke="black" stroke-
width="10px" />
  <!-- Ein Rechteck -->
  <rect x="100" y="100" width="300" height="100" fill="blue" stro-
ke="black" stroke-width="2px" transform="rotate(-15 525 25)" />
  <!-- Eine weitere Line -->
  <line x1="180" y1="370" x2="500" y2="50" stroke="black" stroke-
width="10px" />
  <!-- Ein Kreis -->
  <circle cx="150" cy="75" r="60" stroke="red" fill="green" stroke-
width="5px" />

Sie sehen, dass sowohl mit dem img-Tag als auch mit dem svg-Tag SVG-Grafiken in der
Webseite eingebunden sind. Der Zugriff aus JavaScript auf den circle-Tag innerhalb des
svg-Elements ist leicht ([Link]):

[Link] = function () {
  [Link]("circle")[0].onmouseover = function
() {
  [Link] = "cyan";
  };
384 14 Erweiterte Techniken

  [Link]("circle")[0].onmouseout = function ()
{
    [Link] = "green";
  };
  [Link]([Link]("svg1").nextElementSibling.
    getElementsByTagName("circle")[0]);
};

Sie sehen, dass der Zugriff auf den Kreis fast unverschämt einfach ist. Denn da wir XML
vorliegen haben, arbeiten wir mit einem node-Objekt und darauf können Sie mit den ge-
wohnten Methoden zugreifen, sowohl was die Selektion angeht als auch die dynamische
Gestaltung über das style-Objekt. Aber beachten Sie die Indizierung. Der Kreis in dem
svg-Element hat den Index 0, obwohl das img-Element in der Webseite vorher notiert ist.
Auf das Element, das über den img-Tag eingebunden ist, kann man zwar auch zugreifen,
wie die Ausgabe in der Konsole zeigt. Aber Sie erkennen sicher, dass das aufwendiger
ist, da die Bestandteile der SVG-Grafik nicht in den „normalen“ DOM der Webseite ge-
rendert werden (Abb. 14.13).

Abb. 14.13 Die Farbe des oberen Kreises wird dynamisch geändert, wenn sich der Mauszeiger in
dem Bereich befindet
Eine Frage der Qualität
15

Mittlerweile wurden alle zentralen Techniken von JavaScript behandelt. In diesem


abschließenden Kapitel soll ein Blick auf Softwarequalität geworfen werden und
wie man in JavaScript damit umgeht. Mit den Konventionen bei der Wahl von Be-
zeichnern wurde ja bereits sehr früh ein Teilaspekt thematisiert, aber es gibt wei-
tere Konventionen und Konzepte, die bei der Erstellung von „besserem“ Quellcode
helfen. Ebenso werfen wir einen kurzen Blick auf die Fehlersuche, denn Fehler-
freiheit ist unabdingbar, Qualitätssicherung, automatisiertes Testen sowie Möglich-
keiten zur Optimierung von JavaScripts und Webseiten in Form von Best Practices.

15.1 Was versteht man allgemein unter Softwarequalität?

Softwarequalität bezieht sich auf die Gesamtheit der Eigenschaften und Merkmale einer
Software, die ihre Fähigkeit bestimmen, bestimmte Anforderungen zu erfüllen und die
Bedürfnisse der Benutzer zufriedenstellend zu erfüllen. Hier sind einige wichtige As-
pekte der Softwarequalität im Zusammenhang mit dem Quellcode, wobei diese nicht
immer alle einzuhalten sind, sich teils sogar ausschließen können und auch nicht ver-
bindlich zu definieren sind. Aber die Mehrheit der IT-Experten stimmt diesen Punkten im
Wesentlichen zu:

• Zuverlässigkeit: Der Quellcode sollte fehlerfrei sein und die erwarteten Ergebnisse
liefern.
• Wartbarkeit: Der Quellcode sollte leicht zu verstehen, zu ändern und zu erweitern
sein. Man kann auch sagen, dass Codestrukturen „erwartbar“ sein sollen. Was ein
Entwickler geschrieben hat, sollten andere Entwickler sofort verstehen, weil sie es

© Der/die Autor(en), exklusiv lizenziert an Springer Fachmedien Wiesbaden GmbH, ein 385
Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
386 15 Eine Frage der Qualität

genauso machen würden. Das erleichtert die Fehlerbehebung, neue Funktionen hinzu-
zufügen und die Software im Laufe der Zeit anzupassen.
• Lesbarkeit: Der Quellcode sollte klar und verständlich sein. Auch dies trägt zu „er-
wartbarem“ Code bei. Dies umfasst die Verwendung aussagekräftiger Klassen-, Me-
thoden-, Funktions- oder Variablennamen samt Konventionen der Schreibweise, kon-
sistente Formatierung und gut strukturierten Code.
• Effizienz und Performance: Der Quellcode sollte ressourceneffizient sein und keine
unnötige Verwendung von Speicher oder Rechenleistung verursachen. Effizienter
Code führt zu schnelleren Ausführungszeiten und einer besseren Leistung der An-
wendung. Bei einigen Programmiersprachen kann diese Anforderung allerdings mit
Wartbarkeit und Lesbarkeit kollidieren. Zudem ist gerade Performance nicht immer
im Fokus.
• Skalierbarkeit: Quellcode sollte so gestaltet sein, dass er mit zunehmender Komplexi-
tät und Datenmenge der Anwendung umgehen kann. Bei JavaScript sind insbesondere
Module und das Kapseln von Details einer Struktur dazu geeignet. Dennoch ist bei
JavaScript die Skalierbarkeit gerade bei kleineren Aufgaben meist nebensächlich bis
irrelevant.
• Sicherheit: Quellcode sollte gegen Sicherheitslücken und Angriffe geschützt sein.
• Dokumentation: Quellcode sollte angemessen dokumentiert sein.

Insgesamt ist Softwarequalität ein multidimensionales Konzept, das verschiedene As-


pekte der Softwareentwicklung umfasst. Berücksichtigung dieser Aspekte beim Erstellen
von Quellcode trägt – bei Relevanz – dazu bei, eine hochwertige, robuste und benutzer-
freundliche Software zu entwickeln.

15.2 Allgemeine Konzepte für bessere Qualität

Es gibt ein paar Konzepte, die für Softwarequalität sorgen können. Teils passen Sie
sehr gut zum Erstellen von JavaScript, zum Teil macht JavaScript die Umsetzung eher
schwierig oder sie sind irrelevant.

15.2.1 KISS-Prinzip

Eine Regel für gute Software ist als KISS-Prinzip bekannt und dieses ist universell. KISS
steht für „Keep it simple, stupid“ (englisch für „Halte es einfach und dumm“) oder ähn-
liche Aussagen. Das ist kein Widerspruch zu einer intelligenten Programmierung – im
Gegenteil. Denn intelligent bedeutet in der Programmierung einfach und langweilig.
Damit ist die Erstellung des Codes erst einmal wenig fehlerträchtig, aber der Code ist
auch leichter wartbar, was ja als Kernfeatures von Softwarequalität schon thematisiert
wurde. „Einfach“ bedeutet, dass Sie nicht unnötig komplexe oder lange Strukturen
15.2 Allgemeine Konzepte für bessere Qualität 387

schreiben sollten. Lieber schreiben Sie z. B. zwei kurze Funktionen als eine lange Funk-
tion.
Als recht leicht merkbare Regel können Sie festhalten, dass Sie immer so program-
mieren sollten, dass sich jemand Fremdes mit analogem Wissenstand über Ihren Code
nicht wundern würde. Erwartbare Strukturen sind gut, unerwartete Strukturen schlecht.
Wenn Sie etwa an einer Stelle eine while-Schleife verwenden und in einer vergleich-
baren folgenden Situation eine for-Schleife, dann ist das unerwartet und schlecht, wenn
es keinen unvermeidbaren Grund gibt. Ein erfahrener Programmierer, der den Code
sieht, würde sich fragen, warum einmal die eine Schleife und das andere Mal die an-
dere Schleife verwendet wurde. Und dies vollkommen überflüssig, wenn es eben kei-
nen zwingenden Grund gibt. Oder wenn Sie einmal eine Konstante vollkommen groß-
schreiben und das andere Mal nur den ersten Buchstaben großschreiben, ist das un-
erwartet und damit schlecht (was auf Konventionen zurückkommt).

 Gerne wird das KISS-Prinzip auch so erklärt: „Wenn es mehrere gleichwertige Lö-
sungen für ein Problem gibt, wähle die einfachste Variante!“

15.2.2 Das DRY-Prinzip

DRY steht für „Don’t repeat yourself“ – Wiederholen Sie sich nicht. Nutzen Sie (ge-
testete) Strukturen (Module, Funktionen, Objekte, Methoden, etc.), statt sie selbst zu
schreiben. Und wenn Sie etwas selbst programmieren müssen, machen Sie dies Wieder-
verwendbar und nutzen es später bei Bedarf selbst.

15.2.3 Weitere Konzepte

Neben dem KISS-Prinzip und dem DRY-Prinzip gibt es eine Vielzahl anderer Konzepte
und Prinzipien, die dazu beitragen können, Softwarequalität beim Programmieren zu
steigern. Diese Konzepte und Prinzipien sind wichtige Leitlinien für die Gestaltung und
Implementierung von hochwertigem und wartbarem Code. Allerdings sind diese oft für
JavaScript schwer umzusetzen oder führen an der Stelle zu weit. Hier sollen deshalb ei-
nige weitere Konzepte nur kurz genannt werden:

• Single Responsibility Principle (SRP): SRP besagt, dass eine Klasse oder ein Modul
nur eine einzige Verantwortung haben sollte. Durch die Aufteilung des Codes in klei-
nere, spezifischere Teile wird der Code einfacher zu verstehen, zu testen und zu warten.
• Open/Closed Principle (OCP): OCP besagt, dass Softwareeinheiten offen für Er-
weiterungen, aber geschlossen für Modifikationen sein sollten. Dies bedeutet, dass
der Code so entworfen sein sollte, dass neue Funktionen hinzugefügt werden können,
ohne bestehenden Code zu ändern.
388 15 Eine Frage der Qualität

• Separation of Concerns (SoC): Das SoC-Prinzip besagt, dass unterschiedliche As-


pekte des Systems in separaten Modulen oder Komponenten behandelt werden soll-
ten. Durch die Trennung von Anliegen wird der Code besser organisiert und die Wart-
barkeit verbessert.

15.3 Ergänzende Regeln für JavaScript-Kodierung

Rückblickend auf den Stoff des Buchs, sollen einige Regeln zu Syntaxstrukturen – ins-
besondere Schleifen – nachgetragen werden. Konventionen für Bezeichner sind ja schon
bekannt, aber auch für Zählvariablen von Schleifen gibt es Konventionen.

• Wenn man zählergesteuerte Schleifen verwendet, gibt es übliche Konventionen für die
Benennung der Zählvariable, die Sie einhalten sollten. Wenn man while oder do-while
verwendet, wählt man für den Bezeichner der Zählvariablen entweder i oder counter.
Die Zählvariable bei der for-Schleife heißt so gut wie immer i.
• Wenn man zählergesteuerte, verschachtelte Schleifen verwendet, heißt die äußere
Zählvariable i, die Zählvariable für erste innere Schleife j, dann folgt k usw.
• Nach Konvention sollte man Zählvariable immer initialisieren.
• Initialisierungswert von Zählvariablen ist der Wert 0 und sie werden um den Wert 1
erhöht. Bei Bedarf wird Logik in den Schleifenkörper statt in den Schleifenkopf ver-
lagert.

Der letzte Punkt soll mit drei Beispielen verdeutlicht werden, welche alle drei die Lö-
sung einer Aufgabe darstellen. Aufgabe ist, dass man mit einer Schleife alle Zahlen von
57 bis 41 (inklusive) herunterzählt und eine Schrittweite von 0.5 wählt ([Link]).

for (let i = 57; i >= 41; i -= 0.5) { [Link](i); }

In dieser Lösung initialisieren wir schleifenlokal eine Variable imit Startwert 57. Die for-
Schleife läuft, solange igrößer oder gleich dem Endwert ist 41, und in jedem Schleifen-
durchlauf wird i um die Schrittweite 0.5 reduziert. Die aktuelle Zahl wird in jedem
Durchlauf in der Konsole ausgegeben. Die Schleife endet, wenn i kleiner als der End-
wert wird. Das ist ein kompakter, einfacher Code und die Aufgabenstellung suggeriert
bewusst eine solche Herangehensweise als nahe liegende Lösung. Aber diese Lösung
bricht die Konventionen, die oben genannt werden. Denken Sie an eine, Ihnen fremde,
Schleife, die umfangreicher ist und wo der Schleifenkopf nicht gemeinsam mit dem In-
halt des Schleifenkörpers zu sehen ist. Oder wenn wir eine while-Schleife haben und die
Initialisierung der Zählvariablen erfolgt viele Quellcodezeilen oberhalb der eigentlichen
Schleife. Woher sollten Sie wissen, wie der Initialisierungswert ist?
15.3 Ergänzende Regeln für JavaScript-Kodierung 389

Vereinfacht auf unser Beispiel, würden Sie etwa nur wissen, dass Sie einen Teil einer
Schleife sehen und darin die Logik, die Sie nachfolgend sehen, wiederholend ausgeführt
wird.

Beispiel

[Link](i);

Was würden Sie erwarten, was da gemacht wird? Ich würde erwarten, dass alle Zahlen
von 0 bis zu einem Endwert mit der Schrittweite 1 hochgezählt und ausgegeben werden.
Ich hoffe es wird deutlich, wo das Problem bei dieser – zugegeben einfachen und kom-
pakten – Lösung ist. Das wäre eine Alternative, in der Logik aus dem Schleifenkopf in
den Schleifenkörper verlagert wird ([Link]):

"use strict";
let zahl = 57; // Startwert
for (let i = 0; zahl >= 41; i++) {
  [Link](zahl);
  zahl -= 0.5; // den Wert um 0.5 verringern
}

Das Ergebnis ist identisch, wobei hier aufgrund des reinen Schleifenkörpers immer noch
nicht ganz klar ist, welchen Wert zahl beim Start der Schleife hat, wenn man deren Ini-
tialisierung nicht sehen kann. Ebenso haben wir im Schleifenkopf eine Abhängigkeit von
dem Wert von zahl und wenn dessen Initialisierung viele Zeilen im Quellcode entfernt
steht, ist auch das Abbruchverhalten der Schleife nicht wirklich sofort zu sehen. Aber
man kann auch so die Aufgaben lösen ([Link]):

"use strict";
let zahl; // Startwert
for (let i = 0; i <= (57 - 41) * 2; i++) {
  zahl = 57 - (i * 0.5);
  [Link](zahl);
}

Die gesamte Logik ist nun in den Schleifenrumpf verlagert und die Anzahl der
Schleifendurchläufe hängt unmittelbar an der Zählvariable i, die um den Wert 1 erhöht
wird. Beachten Sie, dass der Algorithmus zum Ermitteln des Endwerts ((57–41) * 2)
natürlich auch hartkodiert hier notiert werden kann. In dem Beispiellisting soll nur nach-
vollziehbar sein, wie sich der Wert ergibt. In der Tat ist die Logik im Schleifenrumpf
jedoch jetzt aufwendiger, aber in der Regel ist diese Lösung dennoch von der Software-
qualität besser, weil eben alles in dem Schleifenrumpf gekapselt ist.
390 15 Eine Frage der Qualität

15.4 Ein leidiges Thema – Fehler

Wenn es in einem Skript zu einem Fehler kommt, muss man diesen finden und damit
umgehen. Dabei sind insbesondere Webseiten ein ganz besonderes Umfeld, aber auch
JavaScript macht das Auffinden von Fehlern nicht immer leicht. Webseiten werden von
Browsern nach dem Prinzip der Fehlertoleranz interpretiert. Wenn Sie in HTML einen
Fehler machen, werden Webseiten dennoch im Browser zu sehen sein. Im schlimms-
ten Fall werden einige Informationen unterdrückt oder Teile einer Steueranweisung als
Klartext im Browser ausgegeben. In anderen Situationen werden Tags vergessen und
der Browser ergänzt sie nach gewissen Vorgaberegeln oder strukturiert die Webseite
im Hintergrund um. In den allermeisten Fällen merkt ein Anwender (und oft auch ein
Webseitenersteller) gar nichts von dem Fehler, weil der Browser die Fehler auf diese
Weise automatisch korrigiert und – zumindest optisch – für ein Resultat sorgt, wie es
der fehlerfreie Code wahrscheinlich auch bewirkt hätte. Nur bei JavaScript handelt es
sich um echte Programmierung und JavaScript kann deshalb nicht so fehlertolerant wie
eine Beschreibungssprache sein. Fehler in JavaScript können sowohl den Browser des
Anwenders abschießen als auch im schlimmsten Fall den ganzen Rechner negativ beein-
flussen. Im harmlosesten Fall läuft Ihr Skript nicht oder nicht korrekt (was viel schlim-
mer sein kann).
Die Suche nach Fehlern in einem JavaScript ist jedoch ein recht mühseliges Geschäft.
Denn als Teil einer Webseite haben Sie einzukalkulieren, dass der Browser durch das
Prinzip der Fehlertoleranz eine Basis, die Sie vermuten, im Hintergrund so verändert hat,
dass Sie von falschen Voraussetzungen ausgehen. Zudem ist JavaScript keine strenge
Sprache. Mit der losen Typisierung und laxen Syntaxregeln legte JavaScript von Anfang
an den Fokus auf den leichten Einstieg in die Sprache und die Möglichkeit zur schnellen
Erstellung von schlanken Skripten. Den Preis zahlt man bei der Wartung und der Suche
nach Fehlern. Glücklicherweise gibt es mittlerweile mächtige Tools zur Suche als auch
Vermeidung von Fehlern, die mit den Tools in anderen Sprachen wie Java oder C durch-
aus mithalten können.

15.4.1 Welche Fehler gibt es?

Wir müssen uns bei den möglichen Fehlern unter JavaScript (als auch den meisten ande-
ren Skript- und Programmiersprachen) mit verschiedenen Situationen auseinandersetzen:

1. Typografische Fehler beim Schreiben des Skripts.


2. Syntaktische Fehler beim Schreiben des Skripts.
3. Fehler zur Laufzeit, welche auf logische Fehler im Skriptaufbau zurückzuführen sind.
4. Fehler zur Laufzeit, welche auf äußere Umstände (Situationen, die erst zur Laufzeit
entstehen, die Umgebung des Programms oder den Anwender) zurückzuführen sind.
15.4 Ein leidiges Thema – Fehler 391

Die Beseitigung der Fehlerpunkte 1 bis 3 ist hauptsächlich das, was man unter De-
bugging versteht. Punkt 4 zählt teilweise ebenfalls dazu, was man Debuggen nennt,
wird jedoch bei Skripts weitgehend dem Interpreter überlassen oder mit Ausnahme-
behandlung behandelt.

[Link] Typografische Fehler
Wenn Sie sich beim Schreiben des Quellcodes vertippt oder ein Schlüsselwort, eine Va-
riable oder sonst etwas Entscheidendes falsch geschrieben haben, können im Wesent-
lichen zwei Fälle auftreten.

1. Sofern Sie ein Wort komplett falsch geschrieben haben, ist die Situation oft leicht in
den Griff zu bekommen. Wenn der Schreibfehler zu keinem vernünftigen Ausdruck
(Token) führt, wird der Ausdruck in der Regel einen syntaktischen Widerspruch
innerhalb des Skripts erzeugen und das System wird bei seiner Arbeit den Fehler ent-
decken. Danach reagiert der Interpreter im Fall von JavaScript mit Abbruch der Ver-
arbeitung von dem Skript und gibt meist eine Information zurück, wie Sie den Fehler
finden können. Falls Sie direkt an dieser Adressangabe den Fehler finden, haben Sie
Glück, denn leider ist diese Adressangabe nur die Stelle, wo sich ein Fehler auswirkt
und nicht unbedingt der Entstehungsort. Dies muss nicht immer identisch sein, aber
zumindest ist der Fehler schon einmal eingegrenzt und muss vorher im Quellcode
irgendwo gemacht worden sein.
2. Schlimmer ist, wenn Sie zufällig einen solchen Schreibfehler gemacht haben, sodass
der falsche Begriff wieder einen sinnvollen Token und keinen syntaktischen Wider-
spruch ergibt. Denn das wird ein viel ernsteres Problem. Der Parser oder Interpreter
würde kein Problem melden, wenn Sie etwa nur Token vertauschen und sich kein syn-
taktischer Widerspruch ergibt. Gerade JavaScript ist hier durch die lose Typisierung
im Zusammenhang mit Variablen extrem fehleranfällig. In so Fall haben Sie ohne
Hilfsmittel nur die Chance, dass Sie den Fehler im Quelltext durch aufmerksames
Lesen oder Analyse des Resultats finden (sehr schwer – lassen Sie sich nicht durch
dieses extrem einfache Exempel täuschen). Oder aber Sie greifen auf Testausgaben
oder gar einen Debugger zurück (Abschn. 1.4.3).

[Link] Syntaktische Fehler
Bei syntaktischen Fehlern in einem Skript (Fehler bei der Verwendung der Sprachsyntax)
wird der Interpreter in der Regel die Abarbeitung unterbrechen. Damit sind solche Feh-
ler meist sehr einfach zu finden. Viele syntaktische Fehler beruhen auf typografischen
Fehlern, aber beileibe nicht alle. Denn dieser Typ von Fehler beinhaltet auch die fal-
sche Verwendung von korrekten Ausdrücken oder Token (beispielsweise so etwas wie
for (i = = 0; i < 256;i +  + ) – ein Vergleich statt einer Wertzuweisung der Zählvariable).
Leider gilt auch hier, dass nicht unbedingt die Adressangabe des Fehlers in der Fehler-
anzeige eines Browsers der Entstehungsort des Fehlers sein muss.
392 15 Eine Frage der Qualität

[Link] Laufzeitfehler
Es gibt auch Programmierfehler, die sich erst zur Laufzeit des Skripts auswirken. Unter
Umständen sogar nur in einer ganz spezifischen Konstellation. Solche Fehler werden auf
logische Unzulänglichkeiten im Programmaufbau zurückzuführen sein. Hierbei haben
Sie ein syntaktisch vollkommen korrektes Skript geschrieben, das aber Fehler zu Lauf-
zeit zulässt. Dies geschieht zum Beispiel, wenn eine Variable einen falschen Wert be-
kommt.
Sie müssen zur Beseitigung der Fehlersituation die Stelle im Quelltext suchen, an der
diese Variable (Funktion, Methode usw.) verwendet wird. Dort müssen Sie dann ver-
suchen, den Aufbau und mögliche Fehler noch einmal durchzuspielen. Es gibt ebenso
Programmfehler zur Laufzeit, welche auf äußere Umstände (Umgebung des Programms
oder den Anwender) zurückzuführen sind. Es handelt sich also um Fehler, die mehr auf
konzeptioneller Seite zu suchen sind und weniger um technische Fehler.

15.4.2 Vorbeugen statt heilen

Fehler beim Programmieren können nie vollständig verhindert werden. Selbst Profis
unterlaufen permanent Fehler. Aber man kann die Anzahl der Fehler beim Kodieren re-
duzieren und gleichzeitig die Wartbarkeit von Projekten im Griff halten. Wartbarkeit und
Reduktion der Fehler beim Kodieren gehen direkt einher. Diese Maßnahmen, die oben
genannt wurden, können auch Einsteiger gleich von Anfang an berücksichtigen. Auch
bei einfachen Projekten und Quellcodes, um sich gleich einen guten Programmierstil an-
zugewöhnen.

15.4.3 Fehler suchen

Wenn nun im Skript trotz sorgfältiger Arbeit Fehler vorhanden sind, bleibt nur die
Fehlerbeseitigung. Dabei stellt sich zuerst das Problem, den Fehler überhaupt zu loka-
lisieren. Dies ist der größte und wichtigste Teil dessen, was Debugging genannt wird –
das Lokalisieren und Identifizieren eines Fehlers. Der meist einfachere Teil ist die dann
folgende Beseitigung des Fehlers. Sie können auch ohne Hilfsmittel erfolgreich auf die
Jagd nach Käfern gehen. Das funktioniert ganz gut, wenn man gewisse Konzepte zur
Suche verwendet. Die Fehlersuche ohne Debugger hat die wesentlichen Vorteile, dass
Sie außer dem Editor und dem Browser kein zusätzliches Programm benötigen und sich
vor allem nicht in dessen Bedienung einarbeiten müssen. Zudem lernen Sie eine Menge
über JavaScript.

[Link] Fehler ohne Debugger finden


Fehler in einem JavaScript können ohne einen Debugger auf verschiedene Weise lokali-
siert werden.
15.4 Ein leidiges Thema – Fehler 393

[Link].1 Die Fehlerkonsole


Wenn in einem JavaScript ein Fehler auftritt, sind Fehlermeldungen in der JavaScript-
bzw. Fehlerkonsole eines Browsers als auch der Konsole von [Link] sehr oft ziemlich
aussagekräftig. Darin wird sowohl die Art des Fehlers als auch die Stelle der Auswirkung
beschrieben.

Die Stelle, wo sich ein Fehler auswirkt, muss wie schon erwähnt nicht die Stelle sein, wo Sie einen
Fehler machen. Das gilt vor allen Dingen im Interpreter-Fall. Wenn Sie mit einem Auto aus einer
Kurve fliegen, unterscheiden sich auch die Stelle, an der Sie den Fehler machen und die Stelle, wo
er sich auswirkt (wenn das Auto dann wieder irgendwo aufschlägt). Sie werden in der Konsole
meist nur die Stelle erkennen, wo ein Fehler zum Tragen kommt. Sie können aber davon ausgehen,
dass Sie irgendwo vorher im Skript einen Fehler gemacht haben.

[Link].2 Kontrollausgaben
Mit Hilfe der Ausgaben in der Fehlerkonsole können Sie bereits Fehler recht genau lo-
kalisieren und auch klassifizieren. Der klassische Weg, um nicht offensichtliche Fehler
ohne einen Debugger zu finden, sind jedoch Kontrollausgaben, welche im endgültigen
Quellcode beseitigt werden. Gängige Praxis ist, vor einem vermuteten Fehler eine Bild-
schirmausgabe zu erzeugen. Sie können z. B. den Wert einer Variablen, in der man die
Fehlerursache vermutet, anzeigen. Oder man notiert einfach eine Meldung um zu sehen,
ob das Skript eine bestimmte Stelle erreicht. Für solche Kontrollausgaben verwendet
man in JavaScript als Teil einer Webseite oft ein alert-Fenster oder die Ausgabe in der
Konsole über [Link].

[Link].3 Teile des Quellcodes auskommentieren


Eine andere Technik zur Fehlerlokalisierung beruht darauf, eine größere Menge an An-
weisungen, in denen man die Wanze vermutet, auszukommentieren. Hat ein Skript einen
Fehler und läuft nach der Auskommentierung (natürlich ohne den auskommentierten
Part), wird sich der Fehler sehr wahrscheinlich in den auskommentierten Anweisungen
befinden. Nun verkleinert man Schritt für Schritt (sinnvoll) den auskommentierten Be-
reich und testet nach jeder Verkleinerung das Skript. Nach dem Schritt, nachdem das
Skript nicht mehr läuft, hat man die fehlerhafte Anweisung mit hoher Wahrscheinlichkeit
lokalisiert.

[Link].4 Wirklich ansehen, was der Browser verarbeitet


Wie oben angedeutet, ist das Prinzip der Fehlertoleranz Segen als auch Fluch des WWW.
Wenn Sie eine HTML-Seite erstellt haben und beim Zugriff auf diese per JavaScript Ele-
mente vom Browser nicht dort bereitgestellt werden, wo Sie diese notiert haben oder
sonstige „Verbesserungen“ durch einen Browser stattgefunden haben, laufen Sie u. U.
ins offene Messer. Aber mit dem Kontextmenü und dem „Untersuchen“-Befehl kommen
Sie an den Quellcode der Webseite, die der Browser wirklich verarbeitet. Das hilft sehr
oft weiter.
394 15 Eine Frage der Qualität

HTML ist wie ein Notenblatt und der Browser spielt die Noten. Das kann einmal sehr unterschied-
lich klingen, zum anderen interessiert Sie nicht das Notenblatt, sondern die erzeugte Musik.

[Link] Fehlersuche mit einem Debugger


Ein spezielles Programm bzw. Tool zum Auffinden von Fehlern wird Debugger genannt.
Genaugenommen analysiert man damit den Ablauf von einem Skript oder Programm,
was auch bei fehlerfreien Programmen sehr interessant sein kann, um diese zu verstehen.
Debugger gibt es für nahezu alle Programmiersprachen und für viele Skriptsprachen.
Auch für JavaScript gibt es mittlerweile sehr gute Debugger. Sowohl in allen modernen
Browsern als auch als Teil von Entwicklungstools wie Visual Studio Code, um damit
zum Beispiel mit [Link] zu debuggen. Die konkreten Anwendungen sind zu umfang-
reich und Tool-spezifisch als dass wir diese hier besprechen können. Wir behandeln die
grundsätzliche Vorgehensweise mit einem Debugger.

[Link].1 Die grundsätzliche Arbeit mit einem Debugger


Alle Debugger funktionieren in etwa ähnlich.

• Sie können beispielsweise ein Skript schrittweise verfolgen (so genanntes Steppen.
• Man kann ein Skript gezielt anhalten (mit Haltepunkten beziehungsweise Breakpo-
ints).
• Man kann einzelne Variablen und den Zustand des Programms oder Skripts aus-
werten.
• In leistungsfähigen Tools können Sie auch während das Skript oder Programm unter-
brochen ist einen Wert in einem Ausdruck manuell ändern.

Innerhalb eines Debuggers können Sie in der Regel durch verschiedene Schaltflächen
bzw. Menübefehle den Ablauf des Skripts verfolgen und kontrollieren. Als Werkzeuge
bietet das Programm unter anderem eine Übersicht der aktuell geöffneten Dokumente
und eine Möglichkeit Befehle einzugeben.

15.4.4 Fehler beheben

Ist ein Fehler lokalisiert, müssen Sie den Fehler beseitigen. Anhand der Fehlermeldung
in der JavaScript-Konsole oder aber auch Rückmeldungen von einem Debugger kön-
nen Sie in etwa sehen, was für ein Fehler vorliegt und was zu tun ist. JavaScript selbst
beschreibt mehrere Fehlertypen, die oft auch in der Fehlerkonsole eines Browsers oder
einem Debugger unmittelbar oder umschrieben zu sehen sind. Wichtig sind die folgen-
den (Tab. 15.1):
15.5 Erweiterte Techniken 395

Tab. 15.1  Fehlerarten
Fehlermeldung Bedeutung
ConversionError Ein Fehler beim Konvertieren eines Objektes
RangeError Überschreitung eines erlaubten Bereiches
ReferenceError Ein Referenzfehler
RegExpError Fehler bei einem regulären Ausdruck
SyntaxError Ein allgemeiner Fehler in der JavaScript-Syntax
TypeError Ein Fehler im Datentyp
URIError Ein Fehler beim Zugriff auf eine Datei

15.4.5 Fehler zur Laufzeit abfangen

Das Ausnahmekonzept in JavaScript haben wir wegen dessen herausragender Bedeutung


ja schon behandelt. Davon losgelöst können Sie beim Auftreten eines Laufzeitfehlers in
einem Browser einen bestimmten Code ausführen. Dann planen Sie also gezielt einen
solchen Fehler ein und haben sich bereits vernünftige Gegenmaßnahme überlegt, die
dann sinnvoll sein können. Wenn Sie das machen wollen, können Sie statt einer Aus-
nahmebehandlung den Eventhandler onerror verwenden. Dieser wird vom Browser beim
Auftreten eines Laufzeitfehlers ausgelöst. Sie können als Reaktion beispielsweise be-
nutzerfreundliche Fehlermeldungen anzeigen, eine aufgetretene Fehlersituation korrigie-
ren (wobei man das besser mit Ausnahmebehandlung macht), die Fehlermeldung mittels
eines versteckten Webformulars an den Administrator des Webangebots verschicken oder
sonst eine sinnvolle Maßnahme ergreifen. Prinzipiell registrieren Sie einen JavaScript-
Eventhandler bei einer Webseite am besten beim DOM-Objekt window. Beispiel:

Beispiel

[Link]=function(){
... // Fehlergegenmaßnahme
}. ◄

15.5 Erweiterte Techniken

Zum Abschluss sollen für erfahrene Programmierer und/oder ambitionierte Leser noch
Tools, Techniken und Frameworks vorgestellt werden, die Fehlersuche und Fehlerver-
meidung effektiv unterstützen können und vor allen Dingen Qualität und Wartbarkeit von
Quellcodes verbessern. Allerdings ist der Umgang mit den Tools teils nicht ganz trivial.
Gegebenenfalls können Sie später bei Bedarf darauf zurückkommen. Wobei ich betonen
396 15 Eine Frage der Qualität

möchte, dass dieser Abschnitt auch für Einsteiger viele Tipps, Tricks und Techniken ent-
hält, damit gleich ein sauberer Programmierstil mit JavaScript angewöhnt wird.

15.5.1 Automatische Dokumentation des Quelltextes und


Dokumentationskommentare

Dokumentation von Quelltext ist sowohl für die Fehlervermeidung (gerade von logi-
schen Fehlern) wichtig, denn man denkt bei der Dokumentation noch einmal über den
Quellcode nach. Vor allen Dingen aber wird die Wartbarkeit verbessert, wenn man selbst
oder ein anderer Programmierer später noch einmal den Quellcode anfassen muss. Es
gibt nun in JavaScript eine Spezialversion von einem mehrzeiligen Kommentar, der zu
automatischen Dokumentationszwecken verwendet werden kann. Dabei wird der mehr-
zeilige Kommentar einfach mit /** begonnen (also zwei Sternchen). Aus Sicht von Ja-
vaScript ist das dann nur ein gewöhnlicher mehrzeiliger Kommentar. Aber es gibt Doku-
mentationstools, die dies als Token verstehen und nachfolgenden Kommentar verarbeiten
können, um eine Dokumentation zu erstellen. Beachten Sie, dass diese Kommentare
immer unmittelbar vor der Struktur zu notieren sind, die damit kommentiert werden sol-
len. Mit geeigneten Tools wie jsdoc bzw. jsdoc3 ([Link] oder
jGrouse Doc ([Link] geht das Erzeugen von Kommenta-
ren aus mittlerweile auch JavaScript automatisiert.
Im Grunde arbeiten die Tools alle ähnlich. Ein Programm, das mit diesen speziellen
Kommentaren umgehen kann, erzeugt auf der Basis von diesen speziellen Kommentaren
innerhalb einer JavaScript-Quelldatei oder eines entsprechenden JavaScript-Bereichs in
einer Webseite eine Anzahl HTML-Dateien als API-Dokumentation der dokumentierten
Strukturen. Vereinfacht ausgedrückt schreibt so ein Tool die innerhalb der Datei in spe-
ziellen Kommentaren eingeschlossenen Texte in eine HTML-Datei. Darin können sich
beliebige Informationen befinden. Innerhalb des Kommentarcontainers /** … */ befindet
sich wie gesagt der Kommentartext, der neben reinem Text spezielle Steuerbefehle be-
inhalten kann, die automatisiert zur Erstellung bestimmter Standardelemente in der
Dokumentation verwendet werden. Die meisten Tools verwenden weitgehend analoge
Token. Sie werden dort innerhalb des Kommentarcontainers mit dem speziellen Zeichen
@ eingeleitet und sollten immer in einer einzelnen Zeile innerhalb des Containers ste-
hen. Für Details muss man sich die Dokumentation eines konkreten Tools ansehen, denn
die tatsächlich von einem konkreten Tool genau unterstützen.

15.5.2 Automatische Kontrolle und Qualitätssicherung mit JSLint

Ein mächtiges Tool oder genauer ein Skript zur automatischen Kontrolle von JavaScript-
Code nennt sich JSLint ([Link]
15.5 Erweiterte Techniken 397

 Linting hat sich in der Programmierung als Fachbegriff für allgemeine Kontrolle der
Softwarequalität etabliert.

JSLint soll unabhängig von der internen Arbeitsweise eines speziellen Browsers oder
einer Engine Programmierfehler und auch stilistische und qualitative Mängel in Ja-
vaScripts anzeigen und damit die Qualität des Codes erhöhen. Es geht also neben der
Suche nach echten Fehlern auch um eine explizite Prüfung, ob die strengen Kodierungs-
regeln von JavaScript wirklich vollständig eingehalten werden, obwohl es möglicher
Weise gar keine sichtbaren Fehlereffekte in Testsytemen, einer Konsole noch ggf. in
einem Debugger. Um so einen Qualitätssicherungstest auszuführen, können Sie auf der
Webseite des Projekts einfach den Skript-Code in ein Formular einfügen und prüfen las-
sen. Soweit ist der Umgang mit dem Tool vollkommen trivial. Aber die Interpretation
der Meldungen ist es ganz und gar nicht und zum Nutzen von diesem Skript gibt es sehr
kontroverse Ansichten. Natürlich werden echte Fehler gefunden und das ist wirklich eine
große Hilfe. Das Tool findet in der Tat auch viele Fehler, zeigt jedoch auch viele Stellen
im Code an, die zwar Regeln brechen, von Browsern zum Teil aber automatisch korri-
giert werden. Des Weiteren muss man bei JavaScript auch oft bestimmte Dinge program-
mieren, die JSLint als nicht konform mit dem Standard bewertet und als Fehler anzeigt.
In gewisser Weise hat JSLint das gleiche Problem wie der Validator vom W3C (https://
[Link]/) – im Code befindet sich oft nach einem theoretischen Standard ein
Fehler, aber in der Praxis geht allen, und wenn man den Fehler beseitigt, wird der theo-
retische Standard erfüllt und in der Praxis gibt es Probleme. Dazu erschlägt die Vielzahl
an Anmerkungen und viele der monierten Fehlerstellen sind nicht wirklich relevant bis
kleinlich. Etwa das Fehlen eines Leerzeichens vor einer Klammer. Ebenso werden etwa
oft Bezeichner als fehlerhaft angemerkt, da sie angeblich verwendet werden, bevor sie
deklariert wurden. Das passiert auf jeden Fall dann, wenn man nicht alle Skripts einer
Seite gemeinsam prüft und in anderen Skriptdateien die Deklaration erfolgt ist. Mit einer
recht umfangreichen Konfiguration können Sie allerdings das Prüfungsverhalten von die-
sem Skript steuern.
Ich möchte folgende – zugegeben sehr subjektiv geprägte – Empfehlung aussprechen:
nutzen Sie JSLint, aber korrigieren Sie – vor allen Dingen bei bereits funktionieren-
den Skripts – nicht blind jeden angeblichen Fehler. Überlegen Sie bei jeder einzelnen
Stelle, ob eine Korrektur wirklich notwendig ist. Wobei genau hier das Hauptproblem
von JSLint angesiedelt ist – man braucht Erfahrung und Sicherheit im Umgang mit Ja-
vaScript, um ein so professionelles Tool zu überstimmen. Ich rate dazu nur dann einen
angeblichen „Fehler“ zu korrigieren, wenn Sie genau verstanden haben, was da falsch
sein soll. Im Zweifelsfall lassen Sie es besser, wenn Sie ein bereits funktionierendes
Skript testen. Dieses kann oft zwar qualitativ verbessert, aber auch leicht unbrauchbar
gemacht werden. Aber selbst wenn Sie bei hunderten von angeblichen Fehlerstellen in
einem funktionierenden Skript nur ein oder zwei echte Probleme verbessern, hat sich der
Einsatz gelohnt.
398 15 Eine Frage der Qualität

15.5.3 xUnit-Testing

Dieser Abschnitt ist vor allen Dingen für Leser mit fundiertem Background oder Ambi-
tionen hinsichtlich professioneller Programmierung interessant. Die Technik ist zudem
hauptsächlich bei einer besonderen Vorgehensweise der Projektarbeit bzw. Program-
mierung von Bedeutung. Aber es ist zumindest interessant zu wissen, dass es auch unter
JavaScript einige Projekte gibt, die xUnit als auch testgetriebene Entwicklung (TDD –
Test-Driven Development) unterstützen. Mit TDD wird eine Methode bezeichnet, bei der
konsequent im Rahmen des Entwicklungszyklus Softwaretests von zu testenden Kom-
ponenten einer Applikation (programmtechnische Einheiten oder Units) erstellt wer-
den. Die Tests selbst sollten dabei weitgehend automatisiert ablaufen und bereits vor
(!) der eigentlichen Kodierung des Quellcodes einer Unit erstellt worden sein. Mit die-
sem Test- und gleichzeitig Entwicklungsverfahren sollen sowohl Probleme klassischer
manueller Tests oder auch Abschlusstests des Gesamtsystems als auch Probleme schon
bei der Entwicklung selbst kompensiert werden. Allgemein werden bei solch einer test-
getriebenen Entwicklung Tests im Kleinen (Unit-Tests) und Tests im Großen (System-
tests, Akzeptanztests) unterschieden. Aber man kann unabhängig von strenger TDD auch
rein den automatisierten Testaspekt in den Fokus stellen. Im JavaScript-Umfeld gibt
es mit dem JsUnit-Framework ([Link] oder das zu
jQuery zählende QUnit ([Link] und ähnlichen Systemen mittlerweile sol-
che Testmöglichkeit.

[Link] Wie arbeitet man grundsätzlich bei testgetriebener


Entwicklung?
Die detaillierte Behandlung von TDD und xUnit sprengt unseren Rahmen, aber Sie
sollen hier zumindest einen kurzen Einblick erhalten. Zumal das reine Testen mit ge-
eigneten Frameworks einfach ist. Zuerst wollen wir etwas genauer den Begriff des Unit-
Tests behandeln, damit Sie sich überhaupt etwas darunter vorstellen können. Das Unit-
Testing hat sich ursprünglich überwiegend im Java-Umfeld entwickelt und wurde unter
dem Begriff JUnit bekannt. Darunter versteht man im engeren Sinn ein Framework zum
Testen von Java-Programmen bzw. deren Bestandteilen (Units). Mittlerweile existieren
solche Test-Frameworks für viele Programmiersprachen, sodass man von einem uni-
versellen Test-Framework sprechen und es unter dem Begriff xUnit zusammenfassen
kann.
Ein typischer Zyklus bei der testgetriebenen Entwicklung ist immer gleich. Unit-Tests
selbst und mit ihnen getestete Einheiten werden bei einer konsequenten TDD stets paral-
lel entwickelt. Die eigentliche Programmierung der Tests sowie Units erfolgt in kleinen
und sich immer wieder wiederholenden Schritten, die aus drei bzw. vier (je nach Zähl-
weise) Hauptteilen bestehen:
15.5 Erweiterte Techniken 399

1. Zuerst schreibt man die Tests, die das erwünschte fehlerfreie Verhalten für eine be-
stimmte Funktionalität prüfen. Zu dem Zeitpunkt werden diese Tests von dem be-
stehenden Programmcode normalerweise noch nicht erfüllt bzw. es gibt diesen
Programmcode sogar noch gar nicht.
2. Nächster Schritt ist das Ändern oder Erstellen des Programmcodes, der die Tests er-
füllen soll. Dabei sollte man diesen Code zu dem Zeitpunkt mit möglichst wenig Auf-
wand erstellen. Der Code braucht nicht optimiert zu sein. Ziel in dieser Phase ist nur,
dass der anschließende Testdurchlauf alle Tests besteht. Grundsätzlich ist die Philo-
sophie von TDD darauf ausgelegt, Tests zu bestehen, nicht eine konkrete Software zu
erstellen. Die Erstellung der Software ist quasi ein „Nebenprodukt“.
3. Wenn die Tests bestanden sind, erfolgt die (vorsichtige) Optimierung des Codes
ohne Änderung der Funktionalität. Dies bezeichnet man als Refactoring (deutsch
Refaktorisierung, Restrukturierung oder Umgestaltung). Diese manuelle oder auto-
matisierte Strukturverbesserung von Programmquelltexten soll die Lesbarkeit, Ver-
ständlichkeit, Wartbarkeit und Erweiterbarkeit (und unter Umständen auch die Quali-
tät, Stabilität und Performance des Programms) verbessern.
4. Mit einem abschließenden Testen wird überprüft, ob die Refaktorisierung keine (un-
erwünschte) Veränderung des Programmverhaltens bewirkt hat.

Diese Schritte werden beim Vorantreiben der Applikation für jeden neuen Bestandteil der
Applikation so lange wiederholt, bis die gewünschte Funktionalität der ganzen Applika-
tion erreicht oder ein bekanntes Fehlverhalten bereinigt ist.

Es gibt keine Vorgaben, wie Tests bei einer TDD auszusehen haben. Es liegt in der Verantwortung
des Entwicklers sich alle potenziellen Fehlverhalten einer Einheit im Vorfeld zu überlegen und
sinnvolle Tests auszudenken. Die Konzeption von Tests wird bei konsequenter Anwendung der
TDD so lange vorangetrieben, bis keine sinnvollen weiteren Tests mehr einfallen. Sie sehen damit
jetzt schon, dass der Erfolg einer TDD stark von der Erfahrung eines Programmierers abhängt.

Eine letztendlich als fehlerfrei getestete und entwickelte Unit wird bis auf Weiteres als
fertig angesehen und kann der Testfunktionalitäten entledigt oder aus dem Testszenario
extrahiert werden.

[Link] Ergebnis und Aufbau eines xUnit-Tests


Verlassen wir die strenge TDD und deren Abläufe und betrachten isoliert xUnit-Tests.
Wenn Sie sich entschieden haben, einen xUnit-Test durchzuführen, wird das Ergebnis
eines Tests immer äußerst primitiv, aber aussagekräftig sein. Ein xUnit-Test kann grund-
sätzlich nur zwei Resultate liefern. Entweder ist der Test erfolgreich (das wird in grafi-
schen Systemen meist durch die Farbe Grün symbolisiert) oder nicht erfolgreich (dafür
steht bei einer visuellen Kennzeichnung fast immer die Farbe Rot). Um einen Test auszu-
führen zu können, stellen xUnit-Frameworks Testrunner (Test suite) zur Verfügung und
eine passende Syntax mit sogenannten assert-Methoden. Das bedeutet Annahmen. Man
trifft zu einer zu testenden Funktion oder einem Algorithmus eine gewisse Annahme und
400 15 Eine Frage der Qualität

die kann sich als korrekt oder falsch erweisen. Das Misslingen des Tests kann auf einem
echten Fehler (Error) oder einem falschen Ergebnis des Tests (Failure) beruhen. Beide
Situationen lösen in einem xUnit-Framework eine Ausnahme im Sinne des Exception-
Handlings aus. Ein falsches Ergebnis, das in der Konzeption einer Unit einkalkuliert
werden kann, löst eine abfangbare Ausnahme aus, während alle anderen Fehler vom Fra-
mework als echte Fehler geliefert werden. Dabei liegt es an der konkreten Umsetzung,
wie ein xUnit-Framework reagiert.

[Link] Konkretes xUnit-Testing
Schauen wir uns ein paar praktische Beispiele an, welche das Konzept klarmachen soll-
ten. Dazu verwenden wir QUnit, wobei alle xUnit-Frameworks ähnlich arbeiten. [Link]
soll die JavaScript-Umgebung darstellen, wobei es auch Testrunner gibt, die über einen
Browser genutzt werden. Aber die Verwendung von [Link] hat Vorteile, ist schlanker
und schneller. Zuerst einmal muss aber QUnit installiert werden, wenn es noch nicht vor-
handen ist. Etwa mit npm, was wir im Buch ja konsequent nutzen:

Beispiel

npm install --save-dev qunit

Nun erstellen wir eine einfache Funktion, die wir testen wollen. Das macht man z. B. in
einem Modul ([Link]), das in einem eigenen Verzeichnis xunit1 gespeichert werden soll.

function add (a, b) {


  return a + b;
}
[Link] = add;

Nun brauchen wir eine Datei für den Testrunner. In einem Unterverzeichnis test erstellen
wir dazu eine weitere Datei – ebenfalls mit dem Namen [Link], wobei der Name der
Datei bei der nachfolgend gezeigten Art des Testaufrufs keine Rolle spielt.

const add = require('../[Link]');


[Link]('Adiere zwei Zahlen', assert => {
  [Link](add(1, 2), 3);
});

Wir importieren zuerst das Modul mit der zu testenden Funktion aus dem Elternverzeich-
nis. Der folgende Aufruf von [Link] definiert eine Testsuite für die Funktion add mit
einem einzigen Testfall, der das Ergebnis der Addition zweier Zahlen überprüft. Es wird
geschaut, ob die Addition von 1 und 3 den Wert 3 ergibt. Detaillierte Informationen zum
15.5 Erweiterte Techniken 401

Organisieren von Tests und zum Erstellen anderer Aussagen finden Sie in der API-Doku-
mentation von QUnit.
Wir können jetzt den Test über einen Testrunner im Web als auch die QUnit-CLI aus-
führen. Letzteres werden wir machen. Es wird in der Dokumentation von QUnit emp-
fohlen, den qunit-Befehl über ein npm-Skript auszuführen, das das qunit-Programm
automatisch in Ihrem lokalen Ordner node_modules findet, in dem npm die von Ihnen
heruntergeladenen Abhängigkeiten speichert. Dazu benötigen wir noch eine package.
json-Datei in unserem Projektordner wie folgt:

{ "scripts": { "test": "qunit" }}

Mit der Anweisung npm test wird der QUnit-Test dann in einem Terminal ausgeführt
(Abb. 15.1).
Nun ist ein Kennzeichen von TDD, dass sehr viele Tests automatisiert durchgeführt
werden. Das kann durchaus viele Millionen Testdurchläufe umfassen. Aber auch ohne
strenge TDD ist es meist sinnvoll, sehr viele Testdurchläufe auszuführen. In einem Ja-
vaScript-Testframework können Sie Tests in einer Schleife ausführen, um sie mehrmals
zu wiederholen oder auch in manchen xUnit-Frameworks spezielle Methoden zum Para-
metrisieren von Tests oder zum Wiederholen von Tests nutzen. Ein einfacher Ansatz er-
zeugt viele Tests in einer Schleife. Etwa so:

Beispiel

// Anzahl der Wiederholungen


const anzahlDerWiederholungen = 1000000;
// Schleife zum Wiederholen des Tests
for (let i = 0; i < anzahlDerWiederholungen; i++) {
    [Link]("Mein Test – Wiederholung " + (i+1), function(as-
sert) {

Abb. 15.1 Ein Test hat


keinen Fehler entdeckt
402 15 Eine Frage der Qualität

        let ergebnis = 1 + 1; // Ihre Testlogik hier


        [Link](ergebnis, 2, "Die Addition sollte 2 er-
geben.");
    });
}

Eine andere Möglichkeit besteht darin, die Schleife zu verwenden, um den Testcode
innerhalb eines einzelnen QUnit-Tests zu wiederholen. Etwa so, was meist performanter
als der erste Ansatz ist:

Beispiel

[Link]("Mein Test mit Wiederholung", function(assert) {


    // Anzahl der Wiederholungen
    const anzahlDerWiederholungen = 1000000;
    for (let i = 0; i < anzahlDerWiederholungen; i++) {
        // Ihre Testlogik hier
        let ergebnis = 1 + 1;
        [Link](ergebnis, 2, "Die Addition sollte 2 er-
geben.");
    }
});

Nun sind die getesteten Behauptungen bisher trivial und deshalb werden alle Tests
erfolgreich sein. Deshalb soll jetzt zwei neue Funktionen getestet werden, die nicht
so trivial die Tests bestehen werden. Genaugenommen soll eine Funktion sogar häufi-
ger „versagen“. Betrachten wir diese Funktion, die wir in zwei Schritten testen wollen
([Link]):

funktion lotto1() {
  var zahlen=[];
  for(var i=0;i< 6; i++){
    zahlen[i] = 1 + [Link]([Link]()*49);
  }
  return zahlen;
}

Diese Funktion soll die Ziehung der Lottozahlen simulieren und sechs zufällige Zahlen
zwischen 1 und 49 (jeweils inklusive) liefern. Zwei Dinge sind zu testen:

1. Sind alle Zahlen zwischen 1 und 49?


2. Sind alle Zahlen nach Ende der Ziehung verschieden?
15.5 Erweiterte Techniken 403

Zuerst testen wir in dem Verzeichnis xunit2 den Algorithmus, ob alle Zahlen zwischen
1 und 49 liegen. Das wäre der Testaufbau nur für den ersten Test, bei dem 100.000 Test
durchgeführt werden und der Test in einem eigenen Verzeichnis gefahren wird:

[Link]("Randomtest: 1 - 49", assert => {


  [Link](100000); // Erwartet eine 100.000 Assertions
  let result;
  for (let i = 0; i < 100000; i++) {
    result = 1 + [Link]([Link]() * 49);
    //[Link](i,result);
    [Link](result >= 1 && result <= 49);
  }
});

Der Aufruf erfolgt am besten wieder mit npm test. Wenn Sie mögen, können Sie die Aus-
gabe aktivieren, um die einzelnen Tests zu sehen. Wenn Sie die getesteten Grenzen ver-
ändern und etwa schauen, ob der Algorithmus nur Zahlen kleiner als 43 liefert, werden
Ausnahmen geworfen und der Test war nicht erfolgreich (Abb. 15.2). Aber unser Grund-
algorithmus ist ok.
Betrachten wir diese Abwandlung im Verzeichnis xunit3:

for (let i = 0; i < 5000; i++) {


  [Link]("Randomtest: 1 - 49", assert => {
    let result;
    result = 1 + [Link]([Link]() * 49);
    //[Link](result);
    [Link](result >= 1 && result <= 42);
  });
}

Hier wird bei jedem Schleifendurchlauf ein neuer Test erstellt. Der Testdurchlauf dau-
ert dann erheblich länger und kann bei zu vielen so erzeugten Testobjekten sogar das
System einfrieren. Allerdings erhalten Sie so eine Aussage, in wie vielen Fällen der Test
einen Fehler ergeben hat (Abb. 15.3).
Das kann interessant sein, wenn man mit einer gewissen Fehlerhäufigkeit leben kann.
In der Regel wird eine vollständige Fehlerfreiheit natürlich das Ziel sein, aber so etwas
ist nicht immer mit einem vertretbaren Aufwand zu garantieren1. Denken Sie an Farbein-
stellungen oder das Laden bestimmter Schriftarten. Wenn das mit einer geringen Fehler-
wahrscheinlichkeit schiefgeht, kann man dennoch die Software u. U. einführen.

1 Ich habe die provokante These gelesen, dass es nur zwei Situationen gibt, in denen man unter
allen Umständen 100 % Fehlerfreiheit garantieren muss. Bei Software in Flugzeugen und Software
zum Steuern von Atomkraftwerken.
404 15 Eine Frage der Qualität

Abb. 15.2 Der Test hat Fehler in der Annahme entdeckt

Abb. 15.3 Eine quantitative


Auswertung, wie viele
Testfälle Fehler geliefert haben

Zurück zum getesteten Algorithmus. In der Originalversion ist der Algorithmus kor-
rekt und kann als getestet verwendet werden. Kommen wir nun dazu, dass wir die ge-
samte Funktion testen wollen, ob alle gezogenen Zahlen verschieden sind. Das ist ziem-
lich offensichtlich nicht der Fall und das muss ein Test auch entdecken. Denn unser
Algorithmus simuliert ein Ziehen mit Zurücklegen.

const QUnit = require('qunit');


const assert = [Link];
function lotto1() {
  var zahlen = [];
  for (var i = 0; i < 6; i++) {
    zahlen[i] = 1 + [Link]([Link]() * 49);
  }
  return zahlen;
}
[Link]("Test Lottozahlen", function(assert) {
  [Link](100000); // Erwartet eine 100.000 Assertions
15.5 Erweiterte Techniken 405

  let result, unique;


  for (let i = 0; i < 100000; i++) {
    result = lotto1();
    unique = new Set(result).size === [Link];
    [Link](unique);
  }
});

Sie werden an den Testergebnissen sehen, dass immer wieder Zahlen in dem Array
mehrfach auftauchen und damit kann dieser Algorithmus die geforderte Aufgabe nicht
zuverlässig lösen. Aber die nachfolgende Version ist eine Verbesserung, die ebenfalls ge-
testet werden soll (xunit5):

const QUnit = require('qunit');


const assert = [Link];
function lotto2() {
  let zahlen = [];
  let temp;
  let vorhanden = false;
  for (let i = 0; i < 6; i++) {
    temp = 1 + [Link]([Link]() * 49);
    for (let j = 0; j < [Link]; j++) {
      if (zahlen[j] == temp) {
        i--;
        vorhanden = true;
//        [Link]("Vorhanden: " + temp);
        break;
      }
    }
    if (!vorhanden) {
      zahlen[i] = temp;
    }
    vorhanden = false;
  }
  return zahlen;
}
[Link]("Test Lottozahlen", function (assert) {
  [Link](100000); // Erwartet eine 100.000 Assertions
  let result, unique;
  for (let i = 0; i < 100000; i++) {
    result = lotto2();
    unique = new Set(result).size === [Link];
    [Link](unique);
  }
});
406 15 Eine Frage der Qualität

Bei diesem Test wird kein einziger Fall vorkommen, dass Zahlen mehrfach in dem Array
auftauchen. Mit den beiden erfolgreichen Tests kann man also zuverlässig schließen,
dass die programmierte Funktion in der zweiten Variante die geforderte Aufgabe erfüllt.

 Wenn Sie eine fortgeschrittene Steuerung über die Wiederholung von Tests
benötigen, könnten Sie einen Testrunner wie Mocha verwenden, der eine um-
fangreichere Funktionalität für die Testausführung bietet und beispielsweise
Hooks für Wiederholungen oder Konfigurationen für parallele Ausführung von
Tests ermöglicht.

15.5.4 Cross-Engine-Probleme vermeiden

Bei der Suche nach Fehlern sollte man Cross-Engine-Probleme in JavaScript nicht ganz
vergessen, obwohl sie mittlerweile stark abnehmen. Dies sind Probleme, die auftreten
können, wenn JavaScript-Code in verschiedenen JavaScript-Engines oder Browser-
umgebungen ausgeführt wird. Jede JavaScript-Engine implementiert den ECMAScript-
Standard auf ihre eigene Weise, was zu Inkompatibilitäten und unerwartetem Verhalten
führen kann, wenn derselbe Code in verschiedenen Umgebungen ausgeführt wird. Hier
sind einige häufige Cross-Engine-Probleme und wie sie sich manifestieren können:

• Jede JavaScript-Engine kann den ECMAScript-Standard unterschiedlich inter-


pretieren oder zusätzliche Funktionen und APIs bereitstellen, die nicht in anderen En-
gines vorhanden sind. Dies kann dazu führen, dass Code in einer Engine einwandfrei
funktioniert, in einer anderen jedoch Fehler verursacht.
• Browser bieten spezifische Funktionen und APIs an, die nicht standardisiert sind und
daher nicht in allen Browsern verfügbar sind. Wenn eine Webseite oder Anwendung
diese nicht standardisierten Funktionen verwendet, kann sie in bestimmten Browsern
einwandfrei funktionieren, während sie in anderen Browsern nicht unterstützt wird.
• JavaScript-Engines variieren in ihrer Leistung und Effizienz bei der Ausführung von
Code. Ein Code, der in einer Engine schnell ausgeführt wird, kann in einer anderen
Engine langsam sein, was zu unerwarteten Leistungsproblemen führt.
• JavaScript kann mit CSS und HTML interagieren, um das Aussehen und Verhalten
von Webseiten zu ändern. Unterschiede in der CSS- oder HTML-Rendering-Engine
können dazu führen, dass dasselbe JavaScript in verschiedenen Browsern unterschied-
lich funktioniert oder das gewünschte Layout nicht korrekt gerendert wird.

Um Cross-Engine-Probleme zu minimieren, gibt es folgende Best Practices:

• Verwenden von Feature-Detection statt Browser-Detection. Das bedeutet, statt spezi-


fische Browser zu erkennen und anzupassen, sollte manr Feature-Detection ver-
wenden, um zu überprüfen, ob eine bestimmte Funktion oder API in der aktuellen
Umgebung unterstützt wird.
15.5 Erweiterte Techniken 407

• Verwenden Sie Polyfills und Transpilern. Polyfills sind JavaScript-Bibliotheken, die


fehlende Funktionen in älteren Browsern nachbilden, während Transpiler wie Babel
moderne JavaScript-Syntax in eine ältere Version übersetzen können, die von älteren
Browsern unterstützt wird.
• Umfassende Tests in verschiedenen Browsern sind letztendlich immer notwendig.
Automatisierte Testframeworks wie Selenium können dabei helfen, den Testprozess
zu vereinfachen.

15.5.5 Optimierung von JavaScripts

Der fundamentale Grundsatz in der Programmierung ist, dass Funktion vor Optimierung
geht. Ein Programm/Skript muss erst einmal fehlerfrei seine Aufgabe erledigen. Aber
dann kann man sich gegebenenfalls darum kümmern, dass es optimiert wird. Das be-
deutet, dass man die Performance, den Ressourcenbedarf, die Wartbarkeit oder noch di-
verse andere Dinge verbessert. Gerade im Web gibt es da eine Menge zu beachten. Es
gibt diverse Möglichkeiten, wie Sie eine Webseite im Allgemeinen verbessern können.
Das beginnt ganz banal bei der Reduzierung der Anzahl der Zeichen im Quellcode, be-
ginnend bei Leerzeichen und anderen Whitespace-Zeichen in der gesamten Webseite
(auch der reinen HTML-Bereiche), und führt über eine kompaktere Verwendung von
HTML-Tags bis hin zu der Kompression von Style Sheets und Skripten. Aber auch
andere Maßnahmen wie die Auslagerung von Style Sheets und Skripten in externe
Dateien und/oder Module, das zeitversetzte Laden von zusätzlichen Ressourcen, die
optimale Wahl der Größe von Bildern (in Hinsicht auf HTML5 der Einsatz von Vektor-
grafiken anstelle von Pixelgrafiken), das korrekte Schließen von Tags, das saubere Ver-
schachteln von Tags oder die passende Angabe von im Grunde optionalen Attributen
bei HTML-Tags bzw. – noch besser – deren Festlegung in Style Sheets sind klassische
Optimierungsschritte für eine Webseite im Allgemeinen.

 Um die Größe des Quelltextes bei HTML-, aber auch CSS und JavaScript-
Dokumenten zu reduzieren, gibt es sogenannte Kompressoren. Dazu gibt
es einige Anbieter, die das direkt über Webformulare ermöglichen und von
denen Sie hier zwei Vorschläge sehen ([Link]
pressor/ oder [Link] Der Einsatz von Kom-
pressoren geht meist mit zwei verschiedenen Versionen Ihrer Quelltexte ein-
her. Es gibt eine Version, mit der Sie programmieren und die Sie ausführlich
mit Zeilenumbrüchen, Kommentaren und Einrückungen versehen, sowie eine
Variante, die Sie ausliefern und vorher durch einen Kompressor jagen. Die Ver-
sion, die Sie dann veröffentlichen, ist eine minimierte Version, die um Kom-
mentare und überflüssige Leerzeichen sowie Zeilenumbrüche reduziert ist.
Funktional ist diese aber immer identisch mit der unkomprimierten Version.
408 15 Eine Frage der Qualität

Nun wollen wir uns hier auf die JavaScript-Seite konzentrieren und uns anschauen, wie
Sie die JavaScripts optimieren können, sowohl in Hinsicht auf die Größe als auch vor
allen Dingen auf die Geschwindigkeit der Verarbeitung im Browser. Hier gibt es für Sie
eine Reihe von Rezepten, die mehr oder weniger effektive Verbesserungen bewirken.
Dabei hängt es sowohl an der Art und Komplexität des Skripts als auch dem jeweiligen
Browser, ob ein Rezept überhaupt einen Vorteil bewirkt und wenn ja wie effektiv. Einige
Maßnahmen sind jedoch Browser übergreifend sinnvoll. Wir werden einige der populärs-
ten Rezepte besprechen und die wichtigsten bzw. effektivsten Maßnahmen ausdrücklich
kennzeichnen.

 Optimierungsmaßnahmen können schnell dazu führen, dass der Code so weit opti-
miert wird, bis er nicht mehr läuft. Leider passiert das oft. Ebenso können manche
Maßnahmen sich gegenseitig negativ beeinflussen und andere Maßnahmen sind
unter gewissen Konstellationen sogar grundsätzlich wenig sinnvoll. Optimieren
Sie nur so, dass die Funktionalität vollkommen gewährleistet ist und durch Op-
timierung nicht beeinträchtigt wird. Sichern Sie vor allen Dingen immer (!) den
funktionstüchtigen Stand, bevor Sie mit der Optimierung beginnen. Optimierung
ist ein Vorgang, der mit äußerster Vorsicht angegangen werden sollte und zwingend
eine Versionierung voraussetzt. Entweder machen Sie das manuell, indem Sie das
alte, funktionierende Projekt unter einem anderen Namen sichern, oder Sie ver-
wenden am besten Versionierungstechniken und -tools wie CVS, SVN oder Cloud-
Systeme wie Git.

Betrachten wir nun verschiedene Rezepte zur Optimierung von Skripten, die zum Teil
Dinge wiederholen, zum Teil aber auch ergänzen.

• Reduzierung der Skriptgröße.


• Externe JavaScript-Dateien und/oder Module einsetzen, Dateien zusammenfassen und
Kodierung kennzeichnen.
• Im strikten Modus ("use strict") arbeiten und die Option einmal ganz am Anfang des
Skripts notieren. Eine mehrfache Notation vermeiden.
• Auf überflüssige Erstellung von Objekten verzichten und insbesondere keine
Funktionsobjekte benutzen.
• Vermeiden von überflüssiger Ausnahmebehandlung.
• Die Funktion eval ist sowohl unsicher als auch ineffektiv. Verzichten Sie auf deren
Einsatz.
• Einer der größten Geschwindigkeitsfresser in JavaScript ist es, wenn Sie in einer
Funktion mit einer lokalen Variable arbeiten wollen und diese nicht als lokal kenn-
zeichnen.
• Bei setTimeout und setInterval Funktionsreferenzen oder anonyme Funktionen ver-
wenden.
• Den DOM der Webseite nicht permanent dynamisch verändern.
15.5 Erweiterte Techniken 409

• Signierte Skripte und Sicherheitseinstellungen der Browser einsetzen, wenn sinnvoll.


Damit kann man in diversen Browsern Skripten erweiterte Rechte einräumen, etwa
über die sogenannten signierten Skripten (Signed Script Policy).
• Die with-Anweisung vermeiden. Diese ist nicht performant.

Ein etwas ausführlichere Maßnahme betrifft Memory Leaks (Speicherlecks). Diese kön-
nen auftreten, wenn eine Anwendung Speicher reserviert, ihn aber nicht mehr freigibt,
obwohl er nicht mehr benötigt wird. Ungenutzten Speicherbereiche akkumulieren sich
im Laufe der Zeit und können dazu führen, dass die Anwendung mehr Speicher ver-
braucht als erforderlich ist. Dies kann zu Problemen wie einer verringerten Leistung,
erhöhtem Ressourcenverbrauch und im schlimmsten Fall zum Absturz der Anwendung
führen. In JavaScript können Memory Leaks aus verschiedenen Gründen auftreten:

• Wenn Variablen oder Objekte unbeabsichtigt im globalen Gültigkeitsbereich definiert


werden, bleiben sie im Speicher, selbst wenn sie nicht mehr benötigt werden. Dies
kann dazu führen, dass große Datenstrukturen oder Objekte unnötig im Speicher ver-
bleiben.
• Wenn zwei oder mehr Objekte sich gegenseitig referenzieren (zirkuläre Referenzen)
und keine von ihnen von außerhalb erreichbar ist, kann die Garbage Collection sie
nicht bereinigen, da sie immer noch als erreichbar markiert sind. Dies kann auftreten,
wenn beispielsweise ein Objekt ein anderes als Eigenschaft enthält und umgekehrt.
• Vergessene Event Listener und Callbacks bedeutet, dass Event Listener oder Call-
back-Funktionen nicht ordnungsgemäß entfernt wurden. Damit können sie weiterhin
im Speicher bleiben, auch wenn die entsprechenden DOM-Elemente oder Ereignisse
nicht mehr vorhanden sind. Dies kann insbesondere in Webanwendungen auftreten,
die viele dynamische Inhalte und Interaktionen haben.
• Nicht optimierte Schleifen können fatal sein. Das Erstellen von neuen Objekten inner-
halb von Schleifen, ohne die alten zu entfernen oder freizugeben, kann dazu führen,
dass unnötiger Speicher allokiert wird und die alten Objekte im Speicher verbleiben.

Um Memory Leaks in JavaScript zu vermeiden, ist es wichtig, bewusst mit Speicher-


ressourcen umzugehen und sicherzustellen, dass nicht mehr benötigte Objekte und Re-
ferenzen ordnungsgemäß freigegeben werden. Dies umfasst das Entfernen von Event
Listenern, das Löschen von nicht benötigten globalen Variablen, das Vermeiden von zir-
kulären Referenzen und die Optimierung von Schleifen, um Speicherleckquellen zu mi-
nimieren.
Stichwortverzeichnis

A Betriebssystem, 4
Abstraktion, 164 Bezeichner, 37, 40
addEventListener, 228 BigInt, 48, 58
Aggregationen, 165 Bildobjekte, 207
Ajax, 292, 300 Bit-Operatoren, 70
Promise, 325 Blockanweisungen, 44
alert-Methode, 17 Boolean, 41, 48, 196
Anweisung, 43 Botschaft, Objekt, 162
leere, 79 break, 41, 83, 91
Apache, 8 Breakpoint, 394
Apache Friends, 8 Browser, Daten speichern, 347
arguments, 125 Browserweiche, 33
Array, 49, 93, 194 Bubble-Phase, 224
Anzahl der Elemente, 98
assoziiertes, 100
Konstruktor, 95 C
mehrdimensionales, 97 Callback, 81, 136
nullindiziertes, 94 Callback-Hölle, 322
verschachteltes, 95 Call-by-Reference, 124
Array-Literal, 96 Call-by-Value, 124
Arrow Functions, 134 Camelnotation, 43
ASCII, 25 Canvas, 371
Assoziationen, 165 case, 41
Attribute, 14, 165 Casting, 46, 47
Ausdruck, 44 catch, 41, 284
regulärer, 207 Promise, 324
Ausnahmebehandlung, 281 Chakra, 6
Ausnahmen konsumieren, 285 ChakraCore, 6
class, 41, 181
Client-Server-System, 293
B Closures, 133
Befehlszeileninteraktion, 80 Code, unerreichbarer, 119
Beispiele Download, 2 Comet-Service, 337
Benutzereingabe, 19 CommonJS, 151, 152
Befehlszeile, 80 Compiler, 6

© Der/die Herausgeber bzw. der/die Autor(en), exklusiv lizenziert an Springer 411


Fachmedien Wiesbaden GmbH, ein Teil von Springer Nature 2025
R. Steyer, Programmieren mit JavaScript, [Link]
412 Stichwortverzeichnis

[Link], 14 E
console Eclipse, 5
Objekt für die Konsole, 14 ECMA, 24
const, 41, 57 ECMA International, 20
constructor, 179 ECMAScript, 24
continue, 41, 91 ECMAScript-Module s. ES6-Module
ConversionError, 395 Editor, 4
Cookies, 348 Eigenschaften, 14, 165
coords, 358 Elemente, statische, 165
CORS, 157 else, 41, 80
createServer, 299 encodeURI, 147
Cross-Domain-Zugriff, 305 encodeURIComponent, 147
Cross-Engine-Probleme, 406 Endlosschleife, 86, 91
Cross-Origin Resource Sharing s. CORS Engines, JavaScript, 5
Cross-Site-Scripting, 25 Entscheidungsanweisungen, 79
Entwicklertools, Browser, 5
Error, 284
D ES6-Module, 151, 155
Date, 196 escape, 147
Dateierweiterung für JavaScript, 14 Escape-Sequenzen, 60
Datenfeld s. Array Eulersche Konstante, 193
Datenkapselung, 134, 184 eval, 147
Datentypen, 45 EVA-Prinzip, 14
explizit, 46 event, 218
Datumsobjekte, 196 Event-Bubbling, 224
Debugger, 394 Eventhandler, 120, 129
debugger, 41 Eventhandling, 221
Debugging, 391, 392 Exceptionhandling, 281
decodeURI, 147 export, 41, 152, 155
decodeURIComponent, 147 extends, 41, 171
default, 41 Extensible Markup Language s. XML
Defaultparameter, Funktionen, 139
Deferred Object, 322
Definition s. Deklaration F
Deklaration, 31 Factory, 186
bedingte Funktionen, 138 Fakultätsberechnung, 142
delete, 41, 63, 110 Fall-Through-Anweisung, 83
Dialogfenster, 17 false, 41, 61
do, 41 Farbverlauf, 375
[Link], 348 Fehler, 390
document, 218 Laufzeitfehler, 392
Document Object Model s. DOM syntaktischer, 391
Dokumentationskommentare, 396 typografischer, 391
DOM, 17, 215 Fehlerkonsole, 393
do-while-Schleife, 87 Feld s. Array
DRY-Prinzip, 387 FIFO, 115
Duck-Typing, 46 FILO, 140
Dynamic HTML (DHTML), 361 finally, 41
Stichwortverzeichnis 413

First-Class Citizen, 50, 118, 134, 167 H


for, 41 Haltepunkt, 394
forEach, 104, 107 hasOwnProperty, 183
for-in, 104 Heap, 140
for-of, 104 history, 218, 261
for-Schleife, 88 Hoisting
Frames, 260, 271 Funktionen, 132
Frameworks, 186 Variablen, 55
Function, 41, 49, 118, 204 Hover-Effekt, 363
Funktion htdocs, Apache, 9
anonyme, 131 HTTP, 293
Aufruf, 120
Deklaration, 118
innere, 133 I
Parameter, 123 IDE, 4
Funktionsreferenzen, 119, 128 Identifier, 37
Parameter, 130, 131 Identität, 69
Funktionsunterschrift, 119 if, 41, 80
Funkzellen, 356 IFrames, 260
IIS, 8
Image, 207
G import, 41, 155
Garbage Collection (GC), 62 in, 41
Generalisieren, 165 Infinity, 51
Generatoren, 137 Information Hiding, 184
Geolocation, 355 Initialisieren, 53
GeoLocation Services, 357 Inline-Referenz, 26
GET, 295 innerHTML, 234
getContext, 372 innerText, 234
getCurrentPosition, 358 instanceof, 41, 169
getElementById, 229 Instanz, 162, 164
getElementsByClassName, 229 Instanzelemente, 164
getElementsByName, 229 Interaktivmodus, 11
getElementsByTagName, 229 Interpreter, 6
getOwnPropertyNames, 107 IP-Nummer [Link], 9
Getter, 184 isArray, 110
Git, 408 isFinite, 148
GitHub, 2 isNaN, 147
global, 50, 171 ISO-8859-1, 25
global object, 49 Iterationsanweisungen, 86
Global Positioning System (GPS), 356
Global Properties, 45
globalThis, 51 J
Google Maps, 359 Java, 1, 166
Gradienten, 376 JavaScript
Greenwich Mean Time (GMT), 199 asynchron, 291
Gültigkeitsbereich, 45, 55 Engines, 5
Global Properties, 45
414 Stichwortverzeichnis

Kommandozeile, 11 let, 41, 52, 55


Object Notation s. JSON LIFO, 115
JavaScriptCore, 5 Linting, 397
JavaScript-Generator, 137 Lisp, 166
JIT, 6 Listener, 228
jQuery, 398 Listings, Download, 2
JScript, 24 Literale, 37
JSLint, 396 LiveScript, 1, 24
JSON, 93, 99 localhost, 9
JsUnit, 398 Local Storage, 349
Just-In-Time-Compilation s. JIT [Link], 34
location, 218, 265
Logarithmus, 193
K Logo, 166
Kartendienste, 359 Longitude, 358
Keyframes, CSS, 363 Loopback-Adresse, 9
KISS-Prinzip, 386 loose typing, 45
Klammerschreibweise, Objektmember, 94
Klassen, 162, 164
abstrakte, 165 M
generische, 165 MAC-Adresse, 357
Klassenbaum, 163 Map, 112
Klassendefinition, 181 Mark-and-Sweep, 62
Klassenelemente, 165 Maschinencode, 6
Knoten, 217 Maskierung, 60
Knotentypen, DOM, 217 match, 211
Kommandozeile bei JavaScript, 11 Math, 192
Kommentare, 38 Maximalwert von ganzen Zahlen, 58
Konkatenationsoperator, 66 Member, 14
Konstanten, 57 Memory Leaks, 409
Konstruktor, Array, 95 Message s. Nachricht
Kontrollausgaben, 393 Metaklassen, 164
Kontrollstrukturen, 79 Methoden, 14, 118, 165
Konvention vor Konfiguration, 188 MIME-Typ, 27
Minimalwert von ganzen Zahlen, 58
Mitteilungsfenster, 17
L [Link], 152
Lambda-Ausdrücke, 134 Module, 7, 31, 81, 151
Lambda-Notation, 81 OOP, 186
Landing page, 265 Mosaic, 1
Latin-1, 25 Mozilla-Foundation, 24
Latitude, 358 Multipurpose Internet Mail Extensions s.
Laufzeitfehler, 392 MIME-Typ
Layer, Netscape, 220 Multithreading, 327
Lazy Evaluation, 70
[Link], 360
Leerzeichen, 37 N
length, 98 Nachricht, Objekt, 162
Lesezugriff, 184 Namen, 37
Stichwortverzeichnis 415

Namenskonventionen, 42 onopen, WebSocket, 339


Namensraum, gemeinsamer, 31 Open/Closed Principle (OCP), 387
NaN s. Not a Number OpenStreetMap, 359
[Link], 355 Operanden, 37, 63
navigator, 218 Operatoren, 37, 63
NCSA Mosaic, 1 arithmetische, 63
Netscape, 1, 24 logische, 70
Netscape Navigator, 1 Operatorenrangfolge, 78
new, 41, 95 Optimierung, 407
next, Generator, 138 Oracle, 1
[Link], 7 Override, 32
Benutzereingaben von der Konsole, 80
REPL, 12
node, 217, 219 P
node-fetch, 345 Paketmanager npm, 7
<noscript<, 33 Paradigma, 161
Not a Number, 40, 48 funktionales, 166
npm, 7 Paramenterfunktionen, 123
null, 41, 47 Parameterübergabe bei Funktionsreferenzen,
Null, 48 130, 131
Nullmeridian, 358 parseFloat, 148
Number.MAX_SAFE_INTEGER, 58 parseInt, 148
Number.MAX_VALUE, 59 Parser, 35
Number.MIN_SAFE_INTEGER, 58 Pattern, 208
Number.MIN_VALUE, 59 Pfeilfunktionen, 134
Number, 41, 48, 194 Pfeiloperator =<, 134
Plain Old JavaScript Object s. POJO
Plug-Ins, 220
O POJO, 186, 188
[Link](), 171 Polymorphie, 165
[Link], 190 POST, 295
[Link], 107 Prinzip der Fehlertoleranz, 390
[Link], 107 Priorität der Operatoren, 78
Object, 41, 49, 170, 189 private, 184
Objekt-Destrukturierung, ES6-Module, 156 [Link], 81
Objektfelder, 218 [Link], 81
Objektinitialisierer, 170 Programmierung, Funktionale, 49
Objekt Promises, 322
iterieren, 169 prompt-Methode, 19
Test auf Existenz, 169 protected, 184
Objekt-Literal, 99 prototype, 176
Objektorientierung, theoretische Kernkonzepte, Prototypen, 170
164 Pseudo-URL, 296
Objekttypen, 164 public, 184
onerror, 395 Punktnotation, 14, 118
onmessage JSON, 101
WebSocket, 340 Push-Service, 337
Web Worker, 329 Python, Dateierweiterung, 14
416 Stichwortverzeichnis

Q setTimeout, 262, 292


Quadratwurzel, 193 short, 40
Quellcode, 2, 6, 13 Short-Circuit-Auswertung, 70
Quelltext, 13 Sichtbarkeit, 55
Queue, 115 Signatur, Funktion, 119
QUnit, 398 Single page- Webapplikationen, 267
Single Responsibility Principle (SRP), 387
Smalltalk, 166
R Sniffer, 295
RangeError, 395 soap, 338
Rangordnung bei Operatoren, 78 Sockets, 336
Read Evaluate Print Loop s. REPL Softwarequalität, 164, 385
readline-Modul, 81 SOP, 30, 305
Reference Counting, 62 Sourcecode, 13
ReferenceError, 54, 55, 395 SPA, 348
RegExp, 207 Speicherlecks, 409
RegExpError, 395 Spezialisieren, 162, 165
Rekursion, 140 SpiderMonkey, 5
REPL, 12 Sprunganweisungen, 90
Request, 294 Stack, 115, 140
require, 152 static, 40
require is not defined, 152 Steppen, 394
require-Modul, 81 Steuerzeichen, Zeichenketten, 60
Response, 294 String, 41, 48
return, 41, 92, 119 Stringverkettungsoperator, 66
bedingtes, 119 style, 363
Rhino, 6 Subklasse, 162
Rich Internet Applications (RIA), 1 Suchmuster, 208
root, 217 Sun, 1
Klassenbaum, 163 super, 42, 183
Superklasse, 162
SVG, 378
S switch, 42
Same Origin Policy s. SOP switch-case, 83
Sandbox, 25, 305, 347 Symbol, 48
Scalable Vector Graphics s. SVG synchronized, 40
Schleife, zählergesteuert, 88 Synchronized Multimedia Integration Lan-
Schlüsselwörter, 37 guage (SMIL), 378
Schnittstellen, 165 SyntaxError, 395
Schreibzugriff, 184
scope, 45
screen, 218 T
Selbstaufrufe, 140 TCP/IP, 293
send, Websocket, 339 TDD, 398
Separation of Concerns (SoC), 388 Template-Literal-Interpolation, 66, 89
Sequenz, 79 terminate, Web Worker, 329
Session Storage, 349 Test-Driven Development s. TDD
Set, 113 Textindizes, Arrays, 99
Setter, 184 The epoch, 196
Stichwortverzeichnis 417

then, Promise, 323 Vorgabeparameter, Funktionen, 139


this, 42, 171, 179 VS Code, 5
Thread, 291
throw, 42, 92, 288
toFixed, 59 W
Token, 35 W3C, 20
transient, 40 WeakMap, 114
Trennzeichen, 37 WeakSet, 114
true, 42, 61 Web 2.0, 300
try, 42, 284 Webserver, 8, 293
TypeError, 395 [Link], 297
typeof, 42, 63 Wurzelverzeichnis, 9
TypeScript, 24 Webservices, 343
Typumwandlung, 46, 47 WebSockets, 336
Web SQL Database, 349
Web Worker, 327
U Weiterleitung, automatische, 33, 265
Überschreiben, 32 while, 42, 86
Übersetzung, zweistufige, 6 Wiederverwendbarkeit, 163
UDP, 293 window, 50, 218
undefined, 42, 47, 48, 53 with, 42, 169
unescape, 147 Worker, 328
Unicode, 25 ws-Protokoll, 338
Unterprogramm, 117 Wurzel, Klassenbaum, 163
URIError, 395 Wurzelknoten, 217
URL-Kodierung, 296 Wurzelverzeichnis, Apache, 9
use strict, 52

X
V XAMPP, 8
V8, 5 XHR, 304
Validator vom W3C, 397 XML, 17
var, 42, 52, 55 XMLHttpRequest, 218, 304
Variablen, 45, 52 xUnit, 398
lokale, 121
schleifenlokale, 88
Vektorgrafik, 378 Y
Verallgemeinerung, 162 yield, 42, 137
Vererbung, 165
Vergleichsoperatoren, 69
Verknüpfungsoperator, 63 Z
Versionierung, 408 Zeichenketten, 60
Vielgestaltigkeit, 165 Zeichensatz, 25
Visual Studio (VS), 5 Zeichenverkettungsoperator, 48
VS Code, 5 Zufallszahl, 193
void, 42 Zugriffsmodifizierer, 184
volatile, 40 Zuweisungsoperatoren, 77
Voraussetzungen, 3 Zwischencode, 6

Das könnte Ihnen auch gefallen