290
JavaScript kurz & gut 4. Auflage O REILLY O’Reillys Taschenbibliothek David Flanagan Übersetzung von Thomas Demmig, Jørgen W. Lang & Lars Schulten

3868993886_Script

Embed Size (px)

DESCRIPTION

Java Script! Um guia.

Citation preview

Page 1: 3868993886_Script

JavaScriptkurz & gut

4. Auflage

O’REILLY

O’Reillys Taschenbibliothek

David Flanagan Übersetzung von Thomas Demmig,

Jørgen W. Lang & Lars Schulten

Page 2: 3868993886_Script
Page 3: 3868993886_Script

4. AUFLAGE

JavaScriptkurz & gut

David Flanagan

Deutsche Übersetzung vonTh. Demmig, J. Lang, L. Schulten

Beijing � Cambridge � Farnham � Köln � Sebastopol � Tokyo

Page 4: 3868993886_Script

Die Informationen in diesem Buch wurden mit größter Sorgfalt erarbeitet. Dennochkönnen Fehler nicht vollständig ausgeschlossen werden. Verlag, Autoren undÜbersetzer übernehmen keine juristische Verantwortung oder irgendeine Haftungfür eventuell verbliebene Fehler und deren Folgen.Alle Warennamen werden ohne Gewährleistung der freien Verwendbarkeit benutztund sind möglicherweise eingetragene Warenzeichen. Der Verlag richtet sich imWesentlichen nach den Schreibweisen der Hersteller. Das Werk einschließlich allerseiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlichder Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung undVerarbeitung in elektronischen Systemen.

Kommentare und Fragen können Sie gerne an uns richten:O’Reilly Verlag GmbH & Co. KGBalthasarstr. 8150670 KölnE-Mail: [email protected]

Copyright der deutschen Ausgabe:� 2012 O’Reilly Verlag GmbH & Co. KG1. Auflage 19982. Auflage 20033. Auflage 20074. Auflage 2012

Die Originalausgabe erschien 2012 unter dem Titel JavaScript Pocket Reference,Third Edition bei O’Reilly Media, Inc.

Die Darstellung eines Indischen Nashorns im Zusammenhang mit dem ThemaJavaScript ist ein Warenzeichen von O’Reilly Media, Inc.

Bibliografische Information Der Deutschen NationalbibliothekDie Deutsche Nationalbibliothek verzeichnet diese Publikation in derDeutschen Nationalbibliografie; detaillierte bibliografische Datensind im Internet über http://dnb.de abrufbar.

Übersetzung und deutsche Bearbeitung: Thomas Demmig, Mannheim, Jørgen W.Lang, Hatten & Lars Schulten, KölnLektorat: Imke Hirschmann, KölnKorrektorat: Friederike Daenecke, ZülpichProduktion: Karin Driesen, KölnUmschlaggestaltung: Karen Montgomery, Sebastopol & Michael Oreal, KölnSatz: Reemers Publishing Services GmbH, Krefeld, www.reemers.deBelichtung, Druck und buchbinderische Verarbeitung: fgb freiburger graphischebetriebe, www.fgb.de

ISBN: 978-3-86899-388-2

Dieses Buch ist auf 100% chlorfrei gebleichtem Papier gedruckt.

Page 5: 3868993886_Script

Inhalt

Vorwort ........................................................................ VII

1 Die lexikalische Struktur ............................................... 1Kommentare ............................................................... 1Bezeichner und reservierte Wörter ....................................... 2Optionale Semikola ........................................................ 3

2 Typen, Werte und Variablen ........................................... 5Zahlen ...................................................................... 6Text......................................................................... 9Boolesche Werte ........................................................... 12null und undefined ........................................................ 14Das globale Objekt......................................................... 15Typumwandlungen ........................................................ 16Variablendeklaration....................................................... 21

3 Ausdrücke und Operatoren............................................. 25Ausdrücke .................................................................. 26Operatoren ................................................................. 31Arithmetische Operatoren ................................................. 34Relationale Operatoren .................................................... 39Logische Ausdrücke ........................................................ 42Zuweisungsausdrücke...................................................... 45Auswertungsausdrücke .................................................... 46Verschiedene Operatoren.................................................. 48

4 Anweisungen ............................................................. 51Ausdrucksanweisungen .................................................... 53Zusammengesetzte und leere Anweisungen ............................. 54

| III

Page 6: 3868993886_Script

Deklarationsanweisungen .................................................. 55Bedingungen................................................................ 57Schleifen .................................................................... 62Sprünge ..................................................................... 66Verschiedene Anweisungen ................................................ 73

5 Objekte ..................................................................... 77Objekte erstellen............................................................ 78Eigenschaften ............................................................... 82Objektattribute.............................................................. 93

6 Arrays 97Arrays erstellen ............................................................. 98Array-Elemente und -Länge................................................ 99Arrays durchlaufen.......................................................... 101Mehrdimensionale Arrays .................................................. 102Array-Methoden ............................................................ 102ECMAScript 5-Array-Methoden............................................. 107Der Array-Typ ............................................................... 111Array-artige Objekte ........................................................ 112Strings als Arrays ........................................................... 113

7 Funktionen ................................................................ 115Funktionen definieren ...................................................... 116Funktionen aufrufen ........................................................ 119Funktionsargumente und -parameter ..................................... 126Funktionen als Namensräume ............................................. 129Closures ..................................................................... 130Funktionseigenschaften, -methoden und -konstruktoren ................ 135

8 Klassen ..................................................................... 139Klassen und Prototypen .................................................... 140Klassen und Konstruktoren................................................. 142Java-artige Klassen in JavaScript .......................................... 147Unveränderliche Klassen ................................................... 150Unterklassen ................................................................ 151Klassen erweitern........................................................... 153

IV | Inhalt

Page 7: 3868993886_Script

9 Reguläre Ausdrücke ..................................................... 155Suchmuster mit regulären Ausdrücken definieren ....................... 155Mustervergleiche mit regulären Ausdrücken ............................. 164

10 Clientseitiges JavaScript ................................................ 169JavaScript in HTML einbetten ............................................. 169Event-gesteuerte Programmierung ....................................... 171Das Window-Objekt ....................................................... 171

11 Dokumente skripten .................................................... 185Übersicht über das DOM................................................... 185Dokument-Elemente auswählen .......................................... 188Dokumentenstruktur und -durchlauf ..................................... 195Attribute.................................................................... 197Element-Inhalt ............................................................. 199Knoten erstellen, einfügen und löschen .................................. 201Element Style .............................................................. 204Geometrie und Scrolling................................................... 208

12 Events 213Event-Typen................................................................ 215Event-Handler registrieren ................................................ 223Aufruf eines Event-Handlers .............................................. 227

13 Netzwerkverbindungen................................................. 233XMLHttpRequest verwenden .............................................. 233HTTP per <script>: JSONP ................................................ 241Server-Sent Events......................................................... 245WebSockets ................................................................ 246

14 Clientseitiger Speicher .................................................. 249localStorage und sessionStorage .......................................... 250Cookies ..................................................................... 256

Index ............................................................................ 263

Inhalt | V

Page 8: 3868993886_Script
Page 9: 3868993886_Script

Vorwort

JavaScript ist die Programmiersprache des Web. Die überwiegendeMehrheit moderner Websites setzt JavaScript ein. Außerdem ent-halten alle modernen Browser – auf Desktop-Rechnern, Spielekon-solen, Tablets und Smartphones – einen JavaScript-Interpreter.Damit ist JavaScipt die am weitesten verbreitete Programmierspra-che der Geschichte. JavaScript gehört zu den drei Technologien, diealle Web-Entwickler lernen müssen: HTML, um den Inhalt vonWebseiten zu definieren, CSS, um festzulegen, wie die Inhaltedargestellt werden sollen, und JavaScript, um das Verhalten derElemente einer Webseite zu steuern. Mit der Entwicklung von Node(http://nodejs.org) wird JavaScript außerdem auf Webservern immerwichtiger.

Dieses Buch ist ein Auszug aus JavaScript – Das umfassendeReferenzwerk, 6. Auflage, in dem JavaScript noch wesentlich aus-führlicher behandelt wird. Da das ursprüngliche Buch recht groß istund etwas einschüchternd wirken kann, hoffe ich, dass dieseskürzere und kompaktere Buch für einige Leser nützlicher ist. DieseKurzreferenz folgt grundsätzlich der Struktur seines großen Bru-ders: Die Kapitel 1 bis 9 beschäftigen sich mit dem Kern vonJavaScript. Hier geht es um grundsätzliche Dinge wie die Syntaxder Sprache, Typen, Werte, Variablen, Operatoren, Anweisungen.Danach beschäftigen wir uns mit JavaScript-Objekten, Arrays,Funktionen und Klassen. Diese Kapitel befassen sich mit der Spra-che selbst. Sie sind nicht nur für die Verwendung von JavasScript inWebbrowsern, sondern auch beim Einsatz von Node auf der Ser-verseite relevant.

| VII

Page 10: 3868993886_Script

Damit man eine Sprache nutzen kann, muss sie entweder einePlattform oder eine Standardbibliothek mit Funktionen besitzen,um zum Beispiel die Ein- und Ausgabe zu ermöglichen. Der Sprach-kern von JavaScript legt eine minimale API für den Umgang mitText, Arrays, Datumswerten und regulären Ausdrücken fest, besitztaber keine Funktionalität für die Ein- und Ausgabe. Diese (undfortgeschrittenere Features wie Netzwerkzugriffe, Speicher undGrafik) liegen ganz in der Verantwortung der »Host-Umgebung«,in die JavaScript eingebettet ist. Am häufigsten kommt als Host einWebbrowser zum Einsatz. Die Kapitel 1 bis 9 behandeln die in dieSprache integrierte Minimal-API. In den Kapiteln 10 bis 14 geht esum die Host-Umgebung des Webbrowsers. Außerdem wird hiererklärt, wie Sie »clientseitiges JavaScript« für die Erstellung dyna-mischer Webseiten und -applikationen verwenden können.

Die Anzahl der von Webbrowsern implementierten JavaScript-APIsist in den letzten Jahren regelrecht explodiert. Daher ist es leidernicht möglich, sie in einem Buch dieser Größe zu behandeln. In denKapiteln 10 bis 14 finden Sie Informationen zu den wichtigstenElementen von clientseitigem JavaScript: Fenster, Dokumente, Ele-mente, Stile, Events, Netzwerke und Speicherung. Mit diesemGrundlagenwissen ist es einfach, die Verwendung weiterer client-seitiger APIs zu lernen. Weitere Informationen hierzu finden Sieebenfalls im Buch JavaScript – Das umfassende Referenzwerk,6. Auflage (oder auch in Canvas – kurz & gut und jQuery PocketReference, die ebenfalls Auszüge aus JavaScript – Das umfassendeReferenzwerk, 6. Auflage sind).

Auch wenn die Programmierumgebung Node immer wichtigerwird, bietet diese Kurzreferenz leider nicht den nötigen Platz, umserverseitiges JavaScript zu behandeln. Sie finden weitere Informa-tionen unter http://nodejs.org. Auch zu diesem Thema möchte ichIhnen das Buch JavaScript – Das umfassende Referenzwerk,6. Auflage empfehlen. Alternativ finden Sie auch online eine Reihehervorragender JavaScript-Referenzen, wie beispielsweise das Mo-zilla Developer Network auf http://developer.mozilla.org/.

VIII | Vorwort

Page 11: 3868993886_Script

Typografische KonventionenIch habe die folgenden Formatierungskonventionen in diesem Buchverwendet:

KursivWird zum Hervorheben genutzt und um die erste Verwendungeines Begriffs zu kennzeichnen. Kursiv wird ebenfalls für E-Mail-Adressen, URLs und Dateinamen verwendet.

NichtproportionalschriftWird in allen JavaScript-Codeblöcken- und HTML-Listings ver-wendet sowie allgemein für alles, was man beim Programmierenwörtlich eingeben würde.

Nichtproportionalschrift kursivWird für die Namen von Funktionsargumenten verwendet undallgemein als Platzhalter eingesetzt, um Elemente zu kennzeich-nen, die in Ihren Programmen durch tatsächliche Werte ersetztwerden sollten.

Nutzung der CodebeispieleDie Beispiele, die wir hier zeigen, dürfen Sie generell in Ihren Pro-grammen und Dokumentationen verwenden. Sie brauchen unsnicht um Genehmigung zu bitten, sofern Sie nicht große Teile desCodes reproduzieren. Wenn Sie zum Beispiel ein Programm schrei-ben, das mehrere Codeabschnitte aus diesem Buch wiederverwen-det, brauchen Sie nicht unser Einverständnis.

Eine Quellenangabe ist zwar nicht notwendig, aber dennoch will-kommen. Bitte verwenden Sie in diesem Fall die Form: »AusJavaScript – kurz & gut, 4. Auflage von David Flanagan (O’Reilly).Copyright 2012 David Flanagan, 978-3-86899-388-2.« Wenn Sienicht wissen, ob Ihre Benutzung des Codes von dieser Genehmi-gung abgedeckt ist, nehmen Sie bitte unter der Adresse [email protected] Kontakt mit uns auf.

Vorwort | IX

Page 12: 3868993886_Script

DanksagungenVielen Dank an meinen Lektor Simon St. Laurent für den Vor-schlag, JavaScript – Das umfassende Referenzwerk, 6. Auflage aufdiese etwas besser handhabbare Größe zu verdichten. Ich dankeaußerdem den O’Reilly-Mitarbeitern aus der Produktion, die esimmer wieder schaffen, meine Bücher richtig gut aussehen zulassen.

X | Vorwort

Page 13: 3868993886_Script

KAPITEL 1

Die lexikalische Struktur

JavaScript-Programme werden im Unicode-Zeichensatz geschrie-ben. Unicode ist eine Obermenge von ASCII und Latin-1 und un-terstützt praktisch alle geschriebenen Sprachen, die aktuell aufdiesem Planeten genutzt werden.

JavaScript ist eine Sprache, die Groß-/Kleinschreibung unterstützt.Das heißt, dass Schlüsselwörter der Sprache, die Namen von Varia-blen und Funktionen und andere Bezeichner immer unter konsis-tenter Verwendung von Groß- und Kleinbuchstaben geschriebenwerden müssen. Das Schlüsselwort while muss hingegen immer»while« geschrieben werden, nicht »While« oder »WHILE«. Glei-chermaßen sind online, Online, OnLine und ONLINE vier verschiedeneVariablennamen.

KommentareJavaScript unterstützt zwei Kommentarstile. Jeglicher Text, derzwischen der Zeichenfolge // und dem Ende einer Zeile steht, wirdals Kommentar betrachtet und von JavaScript ignoriert. JeglicherText zwischen den Zeichenfolgen /* und */ wird ebenfalls alsKommentar betrachtet. Diese Kommentare können mehrere Zeilenlang sein, dürfen aber nicht geschachtelt werden. Die folgendenCodezeilen sind gültige JavaScript-Kommentare:

// Das ist ein einzeiliger Kommentar./* Das ist ebenfalls ein Kommentar */// Und hier ist ein weiterer./*

| 1

Page 14: 3868993886_Script

* Noch ein Kommentar,* der mehrere Zeilen lang ist.*/

Bezeichner und reservierte WörterEin Bezeichner ist einfach ein Name. In JavaScript werden Bezeich-ner genutzt, um Variablen und Funktionen zu benennen und umMarken für bestimmte Schleifen in JavaScript-Code anzugeben. EinJavaScript-Bezeichner muss mit einem Buchstaben, einem Unter-strich (_) oder einem Dollarzeichen ($) beginnen. NachfolgendeZeichen können Buchstaben, Ziffern, Unterstriche und Dollarzei-chen sein.

JavaScript reserviert einige Bezeichner als Schlüsselwörter der Spra-che. Sie dürfen diese Wörter in Ihren Programmen nicht als Be-zeichner nutzen:

break delete function return typeofcase do if switch varcatch else in this voidcontinue false instanceof throw whiledebugger finally new true withdefault for null try

JavaScript reserviert außerdem einige Wörter, die von der Spracheaktuell nicht genutzt werden, in zukünftigen Versionen aber genutztwerden könnten. ECMAScript 5 reserviert die folgenden Wörter:

class const enum export extends import super

Zusätzlich sind die folgenden Wörter, die in gewöhnlichem Java-Script-Code zulässig sind, im »strengen Modus« (strict mode) reser-viert:

implements let private public yieldinterface package protected static

Außerdem beschränkt der Strict-Modus die Verwendung der fol-genden Bezeichner. Sie sind nicht vollständig reserviert, dürfen abernicht als Namen für Variablen, Funktionen oder Parameter genutztwerden:

arguments eval

2 | Kapitel 1: Die lexikalische Struktur

Page 15: 3868993886_Script

ECMAScript 3 reservierte alle Schlüsselwörter der Programmier-sprache Java. Obgleich dies in ECMAScript 5 gelockert wurde,sollten Sie folgende Wörter in Ihrem Code als Bezeichner vermei-den, wenn Sie vorhaben, Ihren Code in einer ECMAScript 3-kon-formen JavaScript-Implementierung laufen zu lassen:

abstract double goto native staticboolean enum implements package superbyte export import private synchronizedchar extends int protected throwsclass final interface public transientconst float long short volatile

Optionale SemikolaWie viele andere Programmiersprachen nutzt JavaScript das Semiko-lon (;) um Anweisungen voneinander abzugrenzen (siehe Kapitel 4).Das ist wichtig, um die Bedeutung Ihres Codes zu verdeutlichen:Ohne ein Trennzeichen könnte es scheinen, als sei das Ende dereinen Anweisung der Anfang der nächsten oder umgekehrt. InJavaScript können Sie das Semikolon zwischen zwei Anweisungengewöhnlich weglassen, wenn diese Anweisungen auf eigenständi-gen Zeilen stehen. (Außerdem können Sie das Semikolon am Endeeines Programms weglassen oder dann, wenn das nächste Token imProgramm eine schließende geschweifte Klammer } ist.) Viele Java-Script-Programmierer (und der Code in diesem Buch) nutzen Semi-kola, um explizit das Ende einer Anweisung zu markieren, auch anStellen, an denen sie nicht verwendet werden müssten. Ein andererStil ist, Semikola immer wegzulassen, wenn das möglich ist, und sienur in den wenigen Situationen zu verwenden, in denen sie erfor-derlich sind. Unabhängig davon, für welchen Stil Sie sich entschei-den, sollten Sie ein paar Details zu optionalen Semikola in Java-Script begriffen haben.

Betrachten Sie den folgenden Code. Da die beiden Anweisungen aufzwei separaten Zeilen stehen, könnte das erste Semikolon weggelas-sen werden:

a = 3;b = 4;

Optionale Semikola | 3

Page 16: 3868993886_Script

Schreibt man die Anweisungen jedoch folgendermaßen, ist das ersteSemikolon erforderlich:

a = 3; b = 4;

Beachten Sie, dass JavaScript nicht jeden Zeilenumbruch als einSemikolon betrachtet: Normalerweise betrachtet es einen Zeilen-umbruch nur dann als ein Semikolon, wenn sich der Code ohnedieses Semikolon nicht parsen ließe. Genauer gesagt: JavaScriptinterpretiert einen Zeilenumbruch als Semikolon, wenn der Um-bruch entweder auf eines der Schlüsselwörter return, break bzw.continue folgt, wenn er vor den Operatoren ++ oder -- steht oderwenn das folgende Nicht-Leerzeichen nicht als Fortsetzung dergegenwärtigen Anweisung interpretiert werden kann.

Diese Regeln zum Abschluss von Anweisungen können zu einigenüberraschenden Fällen führen. Folgender Code scheint aus zweieigenständigen Anweisungen zu bestehen, die durch einen Zeilen-umbruch getrennt werden:

var y = x + f(a+b).toString()

Aber die Klammern zu Anfang der zweiten Codezeile können als einFunktionsaufruf auf dem f-Element der ersten Zeile gelesen wer-den, und JavaScript interpretiert diesen Code folgendermaßen:

var y = x + f(a+b).toString();

4 | Kapitel 1: Die lexikalische Struktur

Page 17: 3868993886_Script

KAPITEL 2

Typen, Werte und Variablen

Computer-Programme basieren auf der Manipulation von Wertenwie der Zahl 3,14 oder dem Text »Hallo Welt«. Die Arten derWerte, die in einer Programmiersprache repräsentiert und manipu-liert werden können, bezeichnet man als Typen. Wenn ein Pro-gramm einen Wert zur späteren Verwendung aufbewahren muss,weist es den Wert einer Variablen zu (oder »speichert« ihn in einer).Eine Variable definiert einen symbolischen Namen für einen Wertund ermöglicht es, den Wert über diesen Namen zu referenzieren.

Die Typen von JavaScript können in zwei Kategorien eingeteiltwerden: elementare Typen und Objekttypen. Zu den elementarenTypen von JavaScript zählen Zahlen, Zeichenfolgen (die man alsStrings bezeichnet) und boolesche Wahrheitswerte. Diese werden inden ersten Abschnitten dieses Kapitels behandelt. (Die Kapitel 5, 6und 7 behandeln drei Typen von JavaScript-Objekten.)

JavaScript wandelt Werte großzügig von einem Typ in einen ande-ren Typ um. Übergeben Sie zum Beispiel einem Programm, daseinen String erwartet, eine Zahl, wandelt es diese Zahl automatischfür Sie in einen String um. Geben Sie an einer Stelle, an der einboolescher Wert erwartet wird, einen Wert an, der kein boolescherWert ist, führt JavaScript die erforderliche Umwandlung durch. DieRegeln für Wertumwandlungen werden im Abschnitt »Typ-umwandlungen« auf Seite 16 erläutert.

JavaScript-Variablen sind typlos: Sie können einer Variable, der Siezunächst einen Wert des einen Typs zugewiesen haben, späterproblemlos einen Wert eines anderen Typs zuweisen. Variablen

| 5

Page 18: 3868993886_Script

werden mit dem Schlüsselwort var deklariert. JavaScript arbeitetmit lexikalischer Geltung. Variablen, die nicht innerhalb einer Funk-tion deklariert werden, sind globale Variablen, die überall innerhalbeines JavaScript-Programms sichtbar sind. Variablen, die in einerFunktion deklariert werden, haben Funktionsgeltung und sind nurfür Code sichtbar, der innerhalb der Funktion erscheint. Der Ab-schnitt »Variablendeklaration« auf Seite 21 geht detaillierter aufVariablen ein.

ZahlenAnders als viele andere Sprachen macht JavaScript keinen Unter-schied zwischen Ganzzahlen (oder Integern) und Gleitkommazahlen.In JavaScript werden alle Zahlen als Gleitkommawerte dargestellt.JavaScript repräsentiert Zahlen in dem 64-Bit-Gleitkommaformat,das im IEEE-Standard 754 definiert wird. Das heißt, es könnenZahlen bis hinauf zu �1.7976931348623157 × 10308 und bis hinabzu �5 × 10–324 repräsentiert werden.

Das JavaScript-Zahlenformat ermöglicht es Ihnen, alle ganzen Zah-len zwischen –9007199254740992 (–253) und 9007199254740992(253) exakt festzuhalten, die Grenzen eingeschlossen. Nutzen Siegrößere ganzzahlige Werte, können Sie in den letzten Ziffern anGenauigkeit verlieren. Beachten Sie jedoch, dass in JavaScript be-stimmte Operationen (wie die Indizierung von Arrays und die inKapitel 3 beschriebenen Bit-Operationen) mit 32-Bit-Ganzzahlendurchgeführt werden.

Erscheint eine Zahl unmittelbar in einem JavaScript-Programm,bezeichnet man das als ein Zahlliteral. JavaScript unterstützt Zahl-literale in verschiedenen Formaten. Beachten Sie, dass jedem Zahl-literal ein Minuszeichen (-) vorangestellt werden kann, um die Zahlnegativ zu machen.

Eine ganze Zahl zur Basis 10 wird in einem JavaScript-Programm alsFolge von Ziffern geschrieben. Zum Beispiel:

01024

6 | Kapitel 2: Typen, Werte und Variablen

Page 19: 3868993886_Script

Neben Ganzzahlliteralen zur Basis 10 kennt JavaScript Hexadezi-malliterale (zur Basis 16). Ein Hexadezimalliteral beginnt mit denZeichen »0x« oder »0X«, auf die eine Reihe hexadezimaler Ziffernfolgt. Hexadezimale Ziffern sind die arabischen Ziffern 0 bis 9 unddie Buchstaben a (oder A) bis f (oder F), die die Werte von 10 bis 15repräsentieren. Hier sind einige Beispiele für hexadezimale Ganz-zahlliterale:

0xff // 15*16 + 15 = 255 (Basis 10)0xCAFE911

Gleitkommaliterale können einen Dezimaltrenner enthalten undnutzen die traditionelle Syntax für reelle Zahlen. Eine reelle Zahlwird mit einem ganzzahligen Teil, einem Punkt als Dezimaltrennerund dem Nachkommateil der Zahl dargestellt.

Gleitkommaliterale können auch in wissenschaftlicher Notationdargestellt werden. Bei dieser folgen auf eine reelle Zahl der Buch-stabe e (oder E), ein optionales Plus- oder Minuszeichen und einganzzahliger Exponent. Eine in dieser Notation dargestellte Zahlentspricht der reellen Zahl mal 10 hoch dem Exponenten.

Kompakter formuliert, sieht diese Syntax so aus:

[Ziffern][.Ziffern][(E|e)[(+|-)]Ziffern]

Zum Beispiel:

3.146.02e23 // 6.02 × 1023

1.4738223E-32 // 1.4738223 × 10–32

JavaScript-Programme arbeiten bei Zahlen mit den arithmetischenOperatoren, die die Sprache bietet. Zu diesen zählen + für dieAddition, - für die Subtraktion, * für die Multiplikation, / für dieDivision und % für die Modulodivision (den Rest nach der Division).Vollständige Angaben zu diesen und weiteren Operatoren findenSie in Kapitel 3.

Neben diesen elementaren arithmetischen Operatoren unterstütztJavaScript komplexere mathematische Operationen über die Funk-tionen und Konstanten, die als Eigenschaften des Math-Objektsdefiniert sind:

Zahlen | 7

Page 20: 3868993886_Script

Math.pow(2,53) // => 9007199254740992: 2 hoch 53.Math.round(.6) // => 1.0: auf die nachste ganze Zahl

// runden.Math.ceil(.6) // => 1.0: auf die nachste ganze Zahl

// aufrunden.Math.floor(.6) // => 0.0: auf die nachste ganze Zahl

// abrunden.Math.abs(-5) // => 5: Absolutwert.Math.max(x,y,z) // Das großte der Argumente ermitteln.Math.min(x,y,z) // Das kleinste der Argumente ermitteln.Math.random() // Pseudo-Zufallszahl, fur deren Wert

// 0 <= x < 1.0 gilt.Math.PI // p: Kreisumfang / Kreisdurchmesser.Math.E // e: Die Basis des naturlichen

// Logarithmus.Math.sqrt(3) // Die Quadratwurzel von 3.Math.pow(3,1/3) // Die Kubikwurzel von 3.Math.sin(0) // Trigonometrie: auch Math.cos,

// Math.atan usw.Math.log(10) // Naturlicher Logarithmus von 10.Math.log(100)/Math.LN10 // Logarithmus zur Basis 10 von 100.Math.log(512)/Math.LN2 // Logarithmus zur Basis 2 von 512.Math.exp(3) // Math.E hoch 3.

In JavaScript lösen arithmetische Operationen keine Fehler aus,wenn es zu Wertüberläufen, Wertunterläufen oder einer Divisiondurch null kommt. Ist das Ergebnis einer numerischen Operationgrößer als die größte darstellbare Zahl (Überlauf), ist das Ergebnis einspezieller Wert für unendlich, den JavaScript als Infinity ausgibt.Wird ein negativer Wert kleiner als die kleinste darstellbare negativeZahl, ist das Ergebnis minus unendlich und wird als -Infinityausgegeben. Unendliche Werte verhalten sich, wie man erwartenwürde: Additions-, Subtraktions-, Multiplikations- und Divisions-operationen auf einem unendlichen Wert liefern als Ergebnis wiedereinen unendlichen Wert (eventuell mit umgekehrtem Vorzeichen).

Die Division durch null ist in JavaScript kein Fehler: Sie lieferteinfach Infinity oder -Infinity. Es gibt allerdings eine Ausnahme:Null geteilt durch null hat keinen wohldefinierten Wert. Das Ergeb-nis dieser Operation ist ein spezieller »Keine-Zahl«-Wert, der als NaN(kurz für Not-a-number) ausgegeben wird. NaN resultiert ebenfalls,wenn Sie versuchen, unendlich durch unendlich zu teilen, dieQuadratwurzel einer negativen Zahl zu ermitteln, oder wenn Sie

8 | Kapitel 2: Typen, Werte und Variablen

Page 21: 3868993886_Script

arithmetische Operatoren auf nicht numerischen Operanden nut-zen, die sich nicht in Zahlen umwandeln lassen.

JavaScript definiert die globalen Variablen Infinity und NaN zurRepräsentation von plus unendlich und »Keine-Zahl«.

NaN hat in JavaScript eine ungewöhnliche Eigenschaft: Dieser Wertist keinem anderen Wert gleich, nicht einmal sich selbst. Das heißt,Sie können nicht x == NaN schreiben, um zu prüfen, ob der Werteiner Variablen x gleich NaN ist. Stattdessen sollten Sie x != xschreiben. Dieser Ausdruck ist nur dann wahr, wenn x gleich NaNist. Alternativ können Sie die Funktion isNaN() nutzen. Diese lieferttrue, wenn ihr Argument NaN oder ein nicht-numerischer Wert wieein String oder ein Objekt ist, der nicht in einen numerischen Wertumgewandelt werden kann. Die verwandte Funktion isFinite()liefert true, wenn ihr Argument eine Zahl ist, die nicht NaN, Infinityoder -Infinity ist.

Es gibt unendlich viele reelle Zahlen, von denen im Java-Script-Gleitkommaformat aber nur eine endliche Anzahl(18437736874454810627, um genau zu sein) exakt repräsentiertwerden kann. Das heißt, dass die Darstellungen der Gleitkom-mazahlen, mit denen Sie in JavaScript arbeiten, häufig nur Nähe-rungswerte für die eigentlichen Zahlen sind und es daher zuRundungsfehlern kommen kann.

TextEin String ist eine unveränderliche Folge von 16-Bit-Werten, dieüblicherweise Unicode-Zeichen darstellen – Strings sind der Java-Script-Typ zur Darstellung von Text. Die Länge eines Strings ist dieAnzahl an 16-Bit-Werten, die er enthält. In JavaScript sind dieIndizes für Strings (und Arrays) nullbasiert: Der erste 16-Bit-Wertbefindet sich an Position 0, der zweite an Position 1 und so weiter.Der leere String ist der String der Länge 0. JavaScript hat keinenspeziellen Typ zur Darstellung eines einzelnen String-Elements. ZurDarstellung eines einzelnen 16-Bit-Werts nutzen Sie einfach einenString der Länge 1.

Text | 9

Page 22: 3868993886_Script

StringliteraleIn ein JavaScript-Programm schließen Sie ein Stringliteral ein,indem Sie die Zeichen des Strings einfach in ein Paar einfacheroder doppelter Anführungszeichen setzen (' oder "). DoppelteAnführungszeichen können in Strings eingebettet werden, die voneinfachen Anführungszeichen eingefasst werden. Einfache Anfüh-rungszeichen können umgekehrt in Strings eingebettet werden, dievon doppelten Anführungszeichen eingefasst werden. Hier sindeinige Beispiele für Stringliterale:

"" // Der leere String: Er enthalt keine Zeichen.'name="formular"'"Hatten Sie nicht lieber ein O'Reilly-Buch?""Dieser String\nhat zwei Zeilen.""p = 3.14"

Das Backslash-Zeichen (\) hat in JavaScript-Strings eine spezielleBedeutung. Gemeinsam mit dem auf es folgenden Zeichen reprä-sentiert es ein Zeichen, das auf andere Weise nicht im String dar-gestellt werden kann. Beispielsweise ist \n eine Escape-Sequenz, dieein Zeilenumbruchzeichen vertritt.

Ein weiteres Beispiel ist die Escape-Sequenz \', die ein einfachesAnführungszeichen (oder einen Apostroph) repräsentiert. DieseEscape-Sequenz ist hilfreich, wenn Sie einen Apostroph in einStringliteral einschließen müssen, das von einfachen Anführungs-zeichen eingefasst ist. Escape-Sequenzen nutzen Sie, um die üblicheInterpretation eines Zeichens zu verhindern. Deswegen sagt manauch, dass der Backslash das nachfolgende Zeichen (wie hier daseinfache Anführungszeichen) maskiert. Er sorgt dafür, dass derApostroph nicht als vorzeitiger Abschluss des Strings verstandenwird:

'You\'re right, it can\'t be a quote'

Tabelle 2-1 führt die JavaScript-Escape-Sequenzen und die Zeichenauf, die sie jeweils repräsentieren. Zwei Escape-Sequenzen sindallgemein und können eingesetzt werden, um beliebige Zeichenanhand ihres Latin-1- oder Unicode-Zeichencodes in hexadezimalerForm anzugeben. Beispielsweise repräsentiert die Escape-Sequenz\xA9 das Copyright-Symbol, dessen Latin-1-Kodierung hexadezimal

10 | Kapitel 2: Typen, Werte und Variablen

Page 23: 3868993886_Script

A9 ist. Die vergleichbare Escape-Sequenz \u repräsentiert ein belie-biges Unicode-Zeichen, das mit vier hexadezimalen Ziffern angege-ben wird. \u03c0 steht beispielsweise für das Zeichen p.

Tabelle 2-1: JavaScript-Escape-Sequenzen

Sequenz Zeichen

\0 Das NUL-Zeichen (\u0000)

\b Backspace (\u0008)

\t Horizontaler Tabulator (\u0009)

\n Zeilenumbruch (\u000A)

\v Vertikaler Tabulator (\u000B)

\f Seitenvorschub (\u000C)

\r Wagenrücklauf (\u000D)

\" Doppeltes Anführungszeichen (\u0022)

\' Apostroph oder einfaches Anführungszeichen (\u0027)

\\ Backslash (\u005C)

\x XX Das Latin-1-Zeichen, das durch die hexadezimalen Ziffern XX angegeben wird.

\u XXXX Das Unicode-Zeichen, das durch die vier hexadezimalen Ziffern XXXX angege-ben wird.

Steht das \-Zeichen vor einem anderen Zeichen als den in Tabelle 2-1aufgeführten, wird der Backslash einfach ignoriert (obwohl zukünf-tige Versionen der Sprache natürlich neue Escape-Sequenzen definie-ren können). Beispielsweise ist \# das Gleiche wie #. ECMAScript 5erlaubt einen Backslash vor einem Zeilenumbruch, um ein String-literal auf mehrere Zeilen zu verteilen.

Eine der eingebauten Einrichtungen von JavaScript ist die Fähigkeit,Strings zu verketten. Wenn Sie den +-Operator mit Zahlen alsOperanden verwenden, addiert er diese. Verwenden Sie ihn mitStrings als Operanden, verbindet er diese, indem er den zweitenString an den ersten anhängt. Zum Beispiel:

msg = "Hallo " + "Welt"; // => "Hallo Welt"

Die Länge eines Strings – also die Anzahl von 16-Bit-Werten, die erenthält – ermitteln Sie mit der length-Eigenschaft des Strings. DieLänge eines Strings s ermitteln Sie also folgendermaßen:

s.length

Text | 11

Page 24: 3868993886_Script

Neben der length-Eigenschaft gibt es eine Reihe von Methoden, dieSie auf Strings aufrufen können (wie immer finden Sie alle Detailsim Referenzabschnitt):

var s = "Hallo Welt" // Definiert einen Text.s.charAt(0) // => "H": Das erste Zeichen.s.charAt(s.length-1) // => "t": Das letzte Zeichen.s.substring(1,4) // => "all": Das 2., 3. und 4. Zeichen.s.slice(1,4) // => "all": Das Gleiche.s.slice(-3) // => "elt": Die letzten drei Zeichen.s.indexOf("l") // => 2: Position des ersten

// Vorkommens des Buchstabens l.s.lastIndexOf("l") // => 9: Position des letzten

// Vorkommens des Buchstabens l.s.indexOf("l", 3) // => 3: Position des ersten

// "l" nach oder bei der 3. Position.s.split(", ") // => ["Hallo", "Welt"]:

// In Substrings aufgetrennt.s.replace("h", "H") // => "hallo Welt": Ersetzt Vorkommen.s.toUpperCase() // => "HALLO WELT": Alles in

// Großbuchstaben.

Denken Sie daran, dass Strings in JavaScript immer unveränderbarsind. Methoden wie replace() und toUpperCase() liefern neueStrings – den String, auf dem sie aufgerufen wurden, verändern sienicht.

In ECMAScript 5 können Strings wie schreibgeschützte Arrays be-handelt werden. Das heißt, Sie können auf die einzelnen Zeichen(16-Bit-Werte) eines Strings mit eckigen Klammern statt mit derMethode charAt() zugreifen:

s = "Hallo Welt";s[0] // => "H"s[s.length-1] // => "t"

Boolesche WerteEin boolescher Wert repräsentiert Wahrheit oder Falschheit, anoder aus, ja oder nein. Für diesen Typ gibt es folglich nur zweimögliche Werte. Die reservierten Wörter true und false werden zudiesen beiden Werten ausgewertet.

12 | Kapitel 2: Typen, Werte und Variablen

Page 25: 3868993886_Script

Boolesche Werte sind in der Regel die Ergebnisse von Vergleichen,die Sie in Ihren JavaScript-Programmen anstellen. Zum Beispiel:

a == 4

Dieser Code prüft, ob der Wert der Variablen a gleich der Zahl 4 ist.Ist das der Fall, ist das Ergebnis dieses Vergleichs der boolescheWert true. Ist a nicht gleich 4, ist das Ergebnis des Vergleichs false.

Boolesche Werte kommen häufig in den Kontrollstrukturen vonJavaScript zum Einsatz. Beispielsweise führt die if/else-Anweisungvon JavaScript eine von zwei Aktionen aus, wenn ein boolescherWert true ist, und die andere, wenn er false ist. Üblicherweisebinden Sie den Vergleich, der den booleschen Wert hervorbringt,direkt in die Anweisung ein, die ihn nutzt. Das sieht dann folgen-dermaßen aus:

if (a == 4)b = b + 1;

elsea = a + 1;

Dieser Code prüft, ob a gleich 4 ist. Ist das der Fall, addiert er 1 zu bhinzu; andernfalls addiert er 1 zu a hinzu.

Wie wir im Abschnitt »Typumwandlungen« auf Seite 16 erörternwerden, kann jeder JavaScript-Wert in einen booleschen Wertumgewandelt werden. Die folgenden Werte werden in false umge-wandelt und verhalten sich deswegen auch wie false:

undefinednull0-0NaN"" // Der leere String.

Alle anderen Werte, einschließlich aller Objekte (und Arrays) wer-den in true umgewandelt und verhalten sich wie true. false und diesechs Werte, die in false umgewandelt werden, werden gelegent-lich als falsy, alle anderen Werte als truthy bezeichnet. (Das führenwir hier nur für den Fall an, dass Sie in englischer Literatur einmalauf diese Bezeichnungen stoßen. Wir werden hier einfach vonfalschen und wahren Werten sprechen.) Erwartet JavaScript einen

Boolesche Werte | 13

Page 26: 3868993886_Script

booleschen Wert, verhält sich ein falscher Wert wie false, einwahrer Wert wie true.

Nehmen Sie zum Beispiel an, die Variable o enthielte entweder einObjekt oder den Wert null. Dass o nicht null ist, könnten Sie dannexplizit mit einer if-Anweisung wie der folgenden prüfen:

if (o !== null) ...

Der Ungleich-Operator !== vergleicht o mit null und wird entwederzu true oder zu false ausgewertet. Sie können den Vergleich aberauch gleich weglassen und sich auf den Umstand stützen, dass nullein falscher Wert ist, Objekte hingegen wahre Werte sind:

if (o) ...

Im ersten Fall wird der Inhalt des ifs nur ausgeführt, wenn o nichtnull ist. Der zweite Fall ist weniger exakt: Der Body des ifs wirdausgeführt, wenn o nicht false oder irgendein falscher Wert (wienull oder undefined) ist. Welche if-Anweisung für Ihr Programmangemessen ist, hängt vollkommen davon ab, welche Werte Sie füro erwarten. Wenn null anders behandelt werden soll als 0 und "",müssen Sie einen expliziten Vergleich einsetzen.

null und undefinednull ist ein Schlüsselwort, das zu einem speziellen Wert ausgewertetwird, der üblicherweise genutzt wird, um das Fehlen eines Wertesanzuzeigen. Wird der typeof-Operator auf null angewandt, erhältman als Ergebnis den String »object«. Das zeigt an, dass null alsspezieller Objektwert betrachtet werden kann, der »kein Objekt«bedeutet. In der Praxis wird null üblicherweise jedoch als einzigesExemplar seines eigenen Typs betrachtet und kann ebenso einge-setzt werden, um für Zahlen oder Strings »kein Wert« anzuzeigen.Die meisten Programmiersprachen besitzen ein Äquivalent zuJavaScripts null: Vielleicht kennen Sie es als null oder nil.

JavaScript kennt noch einen weiteren Wert, der das Fehlen einesWertes anzeigt. Der undefinierte Wert steht für ein grundlegenderesFehlen. Es ist der Wert, den Variablen haben, die noch nichtinitialisiert wurden, und der Wert, den Sie erhalten, wenn Sie den

14 | Kapitel 2: Typen, Werte und Variablen

Page 27: 3868993886_Script

Wert einer Objekteigenschaft oder eines Array-Elements abfragen,die bzw. das es nicht gibt. Der undefinierte Wert wird auch vonFunktionen geliefert, die keinen Rückgabewert haben, und er wirdals Wert für Funktionsparameter geliefert, für die kein Argumentübergeben wurde. undefined ist eine vordefinierte globale Variable(kein Sprachschlüsselwort wie null), die auf den undefinierten Wertinitialisiert ist. Wendet man den typeof-Operator auf den unde-finierten Wert an, liefert er »undefined«. Das zeigt an, dass dieserWert das einzige Exemplar eines speziellen Typs ist.

Trotz dieser Unterschiede können null und undefined beide dasFehlen eines Wertes anzeigen und häufig austauschbar verwendetwerden. Der Gleichheitsoperator == betrachtet sie als gleich. (NutzenSie den strengen Gleichheitsoperator ===, wenn Sie sie unterscheidenmüssen.) Beides sind falsche Werte und verhalten sich wie false,wenn ein boolescher Wert benötigt wird. Weder null noch undefinedhaben irgendwelche Eigenschaften oder Methoden. Wenn Sie ver-suchen, mit . oder [] auf eine Eigenschaft oder Methode eines dieserWerte zuzugreifen, führt das sogar zu einem TypeError.

Das globale ObjektDie vorangegangenen Abschnitte haben die elementaren Typen undWerte von JavaScript behandelt. Objekttypen – Objekte, Arraysund Funktionen – werden weiter unten im Buch in eigenständigenKapiteln behandelt. Es gibt allerdings ein sehr wichtiges Objekt, daswir jetzt behandeln müssen. Das globale Objekt ist ein gewöhnlichesJavaScript-Objekt, das einen sehr wichtigen Zweck erfüllt: DieEigenschaften dieses Objekts sind die global definierten Symbole,die einem JavaScript-Programm zur Verfügung stehen. Wenn derJavaScript-Interpreter startet (oder ein Webbrowser eine neue Seitelädt), erstellt er ein neues globales Objekt und gibt ihm einenanfänglichen Satz von Eigenschaften, die Folgendes definieren:

• globale Eigenschaften wie undefined, Infinity und NaN

• globale Funktionen wie isNaN(), parseInt() (siehe »Typ-umwandlungen« auf Seite 16) und eval() (siehe »Auswer-tungsausdrücke« auf Seite 46).

Das globale Objekt | 15

Page 28: 3868993886_Script

• Konstruktorfunktionen wie Date(), RegExp(), String(), Ob-ject() und Array()

• Globale Objekte wie Math und JSON (siehe »Eigenschaftenund Objekte serialisieren« auf Seite 87)

Die anfänglichen Eigenschaften des globalen Objekts sind keinereservierten Wörter, verdienen es aber, als solche behandelt zuwerden. Dieses Kapitel hat sich bereits mit einigen dieser globalenEigenschaften befasst. Mit den meisten anderen werden wir uns ananderen Stellen dieses Buchs noch befassen.

Auf der obersten Codeebene – JavaScript-Code, der nicht Teil einerFunktion ist – können Sie das JavaScript-Schlüsselwort this nutzen,um auf das globale Objekt zu verweisen:

var global = this; // /Auf das globale Objekt verweisen.

Bei clientseitigem JavaScript dient das Window-Objekt als dasglobale Objekt. Dieses globale Window-Objekt hat eine selbstrefe-renzielle window-Eigenschaft, um auf das globale Objekt zu verwei-sen. Das Window-Objekt definiert die globalen Eigenschaften desSprachkerns von JavaScript und darüber hinaus eine ganze Mengeandere globale Eigenschaften, die für Webbrowser und clientseiti-ges JavaScript eigentümlich sind (siehe Kapitel 10).

Nachdem es erstellt wurde, definiert das globale Objekt alle vor-definierten globalen Werte von JavaScript. Aber dieses spezielleObjekt hält auch von Programmen definierte globale Werte fest.Wenn Ihr Code eine globale Variable deklariert, ist diese Variableeine Eigenschaft des globalen Objekts.

TypumwandlungenJavaScript ist sehr flexibel in Bezug auf die Typen der Werte, die esverlangt. Bei booleschen Werten haben wir das bereits gesehen:Erwartet JavaScript einen booleschen Wert, können Sie einen Wertbeliebigen Typs angeben, der von JavaScript in die erforderlicheForm umgewandelt oder konvertiert wird. Einige Werte (wahreWerte) werden in true umgewandelt, andere (falsche Werte) infalse. Das Gleiche erfolgt auch bei anderen Werten: Braucht

16 | Kapitel 2: Typen, Werte und Variablen

Page 29: 3868993886_Script

JavaScript einen String, wandelt es den von Ihnen übergebenenWert in einen String um. Braucht JavaScript eine Zahl, versucht esden von Ihnen angegebenen Wert in eine Zahl umzuwandeln (oderwandelt ihn in NaN um, wenn er nicht in eine vernünftige Zahlumgewandelt werden kann). Ein paar Beispiele:

10 + " Objekte" // => "10 Objekte". 10 -> String"7" * "4" // => 28: Beide Strings -> Zahlenvar n = 1 - "x"; // => NaN: "x" kann nicht in eine Zahl

// umgewandelt werden.n + " objects" // => "NaN Objekte": NaN -> "NaN"

Tabelle 2-2 fasst zusammen, wie Werte in JavaScript von einem Typin einen anderen umgewandelt werden. Fettgedruckte Tabellenein-träge unterstreichen Umwandlungen, die für Sie vielleicht über-raschend sein könnten. Leere Zellen zeigen an, dass keine Umwand-lung erforderlich ist und auch keine durchgeführt wird.

Tabelle 2-2: JavaScript-Typumwandlungen

Wert Umgewandelt in:

String Zahl Boole-scherWert

Objekt

undefined "undefined" NaN false meldet einen TypeError

null "null" 0 false meldet einen TypeError

true "true" 1 Boolean(true)

false "false" 0 Boolean(false)

"" (leerer String) 0 false String("")

"1.2" (nichtleer, numerisch)

1.2 true String("1.2")

"eins"(nicht leer,nicht numerisch)

NaN true String("ein")

0 "0" false Number(0)

-0 "0" false Number(-0)

NaN "NaN" false Number(NaN)

Infinity "Infinity" true Number(Infinity)

-Infinity "-Infinity" true Number(-Infinity)

1 (endlich,nicht 0)

"1" true Number(1)

Typumwandlungen | 17

Page 30: 3868993886_Script

Tabelle 2-2: JavaScript-Typumwandlungen (Fortsetzung)

Wert Umgewandelt in:

String Zahl Boole-scherWert

Objekt

{} (ein beliebigesObjekt)

toString() toString()odervalueOf()

true

[] (leeres Array) "" 0 true

[9] (1 numeri-sches Element)

"9" 9 true

['a'] (alle an-deren Arrays)

nutzt die Me-thode join()

NaN true

function(){}(eine beliebigeFunktion)

function source NaN true

Weil JavaScript Werte so flexibel umwandeln kann, hat sein==-Gleichheitsoperator auch einen sehr flexiblen Begriff von Gleich-heit. Alle nachfolgenden Vergleiche sind beispielsweise wahr:

null == undefined // Diese beiden Werte werden als gleich// betrachtet.

"0" == 0 // Der String wird vor dem Vergleich in eine// Zahl umgewandelt.

0 == false // Der boolesche Wert wird vor dem// Vergleich in eine Zahl umgewandelt.

"0" == false // Beide Operanden werden vor dem// Vergleich in Zahlen umgewandelt.

Obgleich JavaScript viele Typumwandlungen automatisch durch-führt, werden Sie gelegentlich genötigt sein, Umwandlungen expli-zit durchzuführen, oder vielleicht ziehen Sie es einfach vor, dieUmwandlung explizit anzugeben, um den Code verständlicher zumachen.

Die einfachste Art, eine explizite Typumwandlung anzufordern, istdie Verwendung der Boolean()-, Number()-, String()- oder Ob-ject()-Funktionen:

Number("3") // => 3String(false) // => "false" Oder false.toString()

18 | Kapitel 2: Typen, Werte und Variablen

Page 31: 3868993886_Script

Boolean([]) // => trueObject(3) // => new Number(3)

Beachten Sie, dass alle Werte außer null und undefined eine to-String()-Methode haben, deren Ergebnis üblicherweise dem ent-spricht, was von der Funktion String() geliefert wird.

Bestimmte JavaScript-Operatoren führen implizite Typumwand-lungen durch und werden gelegentlich eingesetzt, um eine Typ-umwandlung zu erzwingen. Ist einer der Operanden des +-Opera-tors ein String, wird der andere in einen String umgewandelt. Derunäre +-Operator wandelt seinen Operanden in eine Zahl um. Undder unäre !-Operator wandelt seinen Operanden in einen boo-leschen Wert um und negiert diesen. Diese Umstände sind für diefolgenden Idiome für Typumwandlungen verantwortlich, auf dieSie gelegentlich in Code stoßen können:

x + "" // Das Gleiche wie String(x)+x // Das Gleiche wie Number(x). Auch x-0!!x // Das Gleiche wie Boolean(x).

// Beachten Sie das doppelte !.

Computer-Programme stehen häufig vor der Aufgabe, Zahlen for-matieren und parsen zu müssen, und JavaScript besitzt spezialisierteFunktionen und Methoden, die eine genauere Kontrolle von Zahl/String- und String/Zahl-Umwandlungen ermöglichen.

Die toString()-Methode, die von der Klasse Number definiert wird,akzeptiert ein optionales Argument, das eine Basis für die Umwand-lung angibt. Geben Sie dieses Argument nicht an, erfolgt die Um-wandlung zur Basis 10. Aber Sie können auch Zahlen zu anderenBasen (zwischen 2 und 36) umwandeln. Zum Beispiel:

var n = 17;binary_string = n.toString(2); // Wird zu "10001"

// ausgewertet.octal_string = "0" + n.toString(8); // Wird zu "021"

// ausgewertet.hex_string = "0x" + n.toString(16); // Wird zu "0x11"

// ausgewertet.

Wenn Sie mit Daten aus dem Finanzwesen oder der Wissenschaftarbeiten, müssen Sie bei der Umwandlung von Zahlen in Stringsgelegentlich genauer steuern können, wie viele Nachkommastellen

Typumwandlungen | 19

Page 32: 3868993886_Script

oder signifikante Ziffern das Ergebnis hat, oder wollen erreichen,dass die wissenschaftliche Notation verwendet wird. Die KlasseNumber definiert drei Methoden für derartige Zahl/String-Um-wandlungen:

var n = 123456.789;n.toFixed(2); // "123456.79"n.toExponential(3); // "1.235e+5"n.toPrecision(7); // "123456.8"

Wenn Sie der Umwandlungsfunktion Number() einen String überge-ben, versucht sie, den String als Ganzzahl- oder Gleitkommaliteralzu parsen. Diese Funktion funktioniert nur bei ganzen Zahlen zurBasis 10 und erlaubt nach dem Zahlliteral keine Zeichen, die nichtTeil des Literals sind. Die Funktionen parseInt() und parseFloat()(das sind globale Funktionen, nicht Methoden einer Klasse) sindflexibler. parseInt() parst nur ganze Zahlen, während parseFloat()ganze Zahlen und Gleitkommazahlen parst. Beginnt ein String mit»0x« oder »0X«, interpretiert parseInt() ihn als hexadezimale Zahl.parseInt() und parseFloat() überspringen Leerraum am String-anfang, parsen so viele numerische Zeichen wie möglich und igno-rieren alles, was darauf folgt. Wenn das erste Nicht-Whitespace-Zeichen kein zulässiger Bestandteil eines numerischen Literals ist,liefern beide NaN:

parseInt("3 blinde Mause") // => 3parseFloat(" 3.14 Meter") // => 3.14parseInt("-12.34") // => -12parseInt("0xFF") // => 255parseFloat("$72.47"); // => NaN

parseInt() akzeptiert ein optionales zweites Argument, über das dieBasis der zu parsenden Zahl angegeben werden kann. Für diesesArgument sind Werte zwischen 2 und 36 erlaubt. Zum Beispiel:

parseInt("11", 2); // => 3 (1*2 + 1)parseInt("077", 8); // => 63 (7*8 + 7)parseInt("ff", 16); // => 255 (15*16 + 15)

20 | Kapitel 2: Typen, Werte und Variablen

Page 33: 3868993886_Script

VariablendeklarationBevor Sie in einem JavaScript-Programm eine Variable nutzen,sollten Sie diese deklarieren. Variablen werden folgendermaßen mitdem Schlüsselwort var deklariert:

var i;var sum;

Sie können auch mehrere Variablen mit einem var-Schlüsselwortdeklarieren:

var i, sum;

Und Sie können die Variablendeklaration mit der Variableninitiali-sierung kombinieren:

var message = "Hallo";var i = 0, j = 0, k = 0;

Sie müssen in der var-Anweisung keinen Anfangswert für eine Va-riable angeben, aber wenn Sie es nicht tun, bleibt der Wert derVariablen undefined, bis Ihr Code einen Wert in ihr speichert.

Beachten Sie, dass die var-Anweisung auch als Teil der for- undfor/in-Schleifen erscheinen kann (die in Kapitel 4 eingeführt wer-den). Das ermöglicht Ihnen, die Variable auf kompakte Weise alsTeil der Schleifensyntax selbst zu deklarieren. Zum Beispiel:

for(var i = 0; i < 10; i++) console.log(i);for(var i = 0, j=10; i < 10; i++,j--) console.log(i*j);for(var p in o) console.log(p);

Wenn Sie mit statisch typisierten Programmiersprachen wie C oderJava vertraut sind, wird Ihnen wahrscheinlich aufgefallen sein, dassbei den Variablendeklarationen von JavaScript kein Typ angegebenwird. Eine JavaScript-Variable kann einen Wert eines beliebigenTyps enthalten. Beispielsweise ist es in JavaScript vollkommen legal,einer Variable erst eine Zahl und später dann einen String zuzuwei-sen:

var i = 10;i = "ten";

Es ist erlaubt und mit keinerlei Risiken verbunden, eine Variablemehrfach mit der var-Anweisung zu deklarieren. Hat die erneute

Variablendeklaration | 21

Page 34: 3868993886_Script

Deklaration einen Initialisierer, verhält sie sich wie eine gewöhnli-che Zuweisungsanweisung.

Versuchen Sie, den Wert einer nicht deklarierten Variablen zu lesen,meldet JavaScript einen Fehler. Im Strict-Modus von ECMAScript 5(siehe den Abschnitt »use strict« in Kapitel 4) ist es auch ein Fehler,wenn einer nicht deklarierten Variablen ein Wert zugewiesen wird.Aber in älteren Versionen und im Nicht-Strict-Modus erstellt Java-Script die Variable als Eigenschaft des globalen Objekts, wenn Sieeiner undeklarierten Variable einen Wert zuweisen, und verhält sichdabei ähnlich wie bei einer ordentlich deklarierten globalen Varia-blen. Das heißt, dass Sie damit durchkommen können, wenn SieIhre globalen Variablen nicht deklarieren. Es ist jedoch eineschlechte Angewohnheit und eine Fehlerquelle. Sie sollten IhreVariablen deswegen immer mit dem Schlüsselwort var deklarieren.

Die Geltung oder der Geltungsbereich einer Variablen ist der Bereichim Quellcode Ihres Programms, in dem sie definiert ist. Eine globaleVariable hat globale Geltung und ist überall in Ihrem JavaScript-Code definiert. Variablen, die in einer Funktion deklariert wurden,sind hingegen nur im Inhalt dieser Funktion definiert. Es sind lokaleVariablen mit lokaler Geltung. Funktionsparameter sind ebenfallslokale Variablen und nur im Rumpf der Funktion definiert.

Wenn Sie Code im globalen Geltungsbereich schreiben, können Siedamit durchkommen, wenn Sie die var-Anweisung nicht verwen-den. Aber wenn Sie lokale Variablen deklarieren, müssen Sie varimmer verwenden. Funktionsdefinitionen können geschachtelt wer-den. Jede Funktion hat ihren eigenen lokalen Geltungsbereich. Mankann also mehrere geschachtelte Schichten lokaler Geltungsberei-che haben.

In C-artigen Programmiersprachen hat jeder Codeblock in ge-schweiften Klammern seinen eigenen Geltungsbereich, und Varia-blen sind nur in dem Geltungsbereich sichtbar, in dem sie deklariertwurden. Das bezeichnet man als Blockgeltung, die es in JavaScriptaber nicht gibt. JavaScript nutzt stattdessen Funktionsgeltung: Varia-blen sind in der Funktion sichtbar, in der sie deklariert wurden,sowie in allen Funktionen, die in diese Funktion eingebettet sind.

22 | Kapitel 2: Typen, Werte und Variablen

Page 35: 3868993886_Script

Die Funktionsgeltung für Variablen in JavaScript bedeutet, dass allein einer Funktion deklarierten Variablen innerhalb der gesamtenFunktion definiert sind. Kurioserweise bedeutet das auch, dassVariablen sogar sichtbar sind, bevor sie deklariert wurden. DieseEinrichtung von JavaScript wird informell als Hoisting, »Heben«,bezeichnet: JavaScript-Code verhält sich, als stünden alle Varia-blendeklarationen in einer Funktion (aber nicht die entsprechendenZuweisungen) am Anfang der Funktion (als wären sie dorthinverschoben oder »gehoben« worden).

Variablendeklaration | 23

Page 36: 3868993886_Script
Page 37: 3868993886_Script

KAPITEL 3

Ausdrücke und Operatoren

Ein Ausdruck ist eine JavaScript-Äußerung, die der JavaScript-Inter-preter auswerten kann, um einen Wert hervorzubringen. Eine Kon-stante, die direkt in Ihr Programm eingebettet ist, ist eine sehreinfache Art von Ausdruck. Ein Variablenname ist ebenfalls eineinfacher Ausdruck, der zu dem Wert ausgewertet wird, der dieserVariable zugewiesen wurde. Komplexe Ausdrücke werden aus ein-facheren Ausdrücken aufgebaut. Ein Array-Zugriffsausdruck be-steht beispielsweise aus einem Ausdruck, der zu einem Array aus-gewertet wird, auf den eine öffnende eckige Klammer folgt, dannaus einem Ausdruck, der zu einer ganzen Zahl ausgewertet wird,und schließlich aus einer schließenden eckigen Klammer. Dieserneue, komplexere Ausdruck wird zu dem Wert ausgewertet, derunter dem entsprechenden Index im angegebenen Array gespeichertist. Ähnlich besteht ein Funktionsaufruf aus einem Ausdruck, derzu einem Funktionsobjekt ausgewertet wird, und null oder mehrzusätzlichen Ausdrücken in einer Klammer, die als die Argumentefür die Funktion verwendet werden.

Am häufigsten setzt man für den Aufbau komplexer Ausdrücke auseinfachen Ausdrücken Operatoren ein. Ein Operator kombiniert dieWerte seiner Operanden (üblicherweise zwei) auf bestimmte Weiseund wird zu einem neuen Wert ausgewertet. Der Multiplikations-operator * ist ein einfaches Beispiel. Der Ausdruck x * y wird zumProdukt der Werte der Ausdrücke x und y ausgewertet. Der Ein-fachheit halber sagen wir gelegentlich, dass ein Operator einen Wertzurückliefert, nicht »er wird zu einem Wert ausgewertet«.

| 25

Page 38: 3868993886_Script

AusdrückeDie einfachsten Ausdrücke, die auch als elementare Ausdrückebezeichnet werden, sind die, die eigenständig sind – d.h., siekönnen nicht in weitere elementare Ausdrücke zerlegt werden. Ele-mentare Ausdrücke sind in JavaScript konstante oder literale Werte,einige Schlüsselwörter der Sprache und Variablenreferenzen.

Literale sind konstante Werte, die unmittelbar in Ihr Programmeingebettet sind. Sie sehen beispielsweise so aus:

1.23 // Ein Zahlliteral"hello" // Ein Stringliteral/pattern/ // Ein Regex-Literal

Reservierte Wörter wie true, false, null und this gelten ebenfallsals elementare Ausdrücke.

Die dritte Art von elementarem Ausdruck schließlich sind einfacheVariablenreferenzen:

i // Wird zum Wert der Variablen i ausgewertet.sum // Wird zum Wert der Variablen sum ausgewertet.

Erscheint ein Bezeichner unabhängig in einem Programm, gehtJavaScript davon aus, dass es sich um eine Variable handelt. Gibtes keine Variable dieses Namens, wird der Ausdruck zum Wertundefined ausgewertet. Im Strict-Modus von ECMAScript 5 führtder Versuch, eine Variable auszuwerten, die es nicht gibt, statt-dessen jedoch zu einem ReferenceError.

InitialisiererObjekt- und Array-Initialisierer sind Ausdrücke, deren Wert ein neuerzeugtes Objekt oder Array ist. Diese Initialisierungsausdrückewerden gelegentlich auch als »Objektliterale« und »Array-Literale«bezeichnet. Im Unterschied zu echten Literalen sind diese jedochkeine elementaren Ausdrücke, weil sie mehrere Teilausdrücke ent-halten, die Eigenschafts- und Elementwerte angeben.

Ein Array-Initialisierer ist eine durch Kommata getrennte Liste vonAusdrücken in eckigen Klammern. Der Wert eines Array-Initialisie-rers ist ein neu erzeugtes Array. Die Elemente dieses neuen Arrays

26 | Kapitel 3: Ausdrücke und Operatoren

Page 39: 3868993886_Script

sind auf die Werte der durch Kommata getrennten Ausdrücke ini-tialisiert:

[] // Ein leeres Array[1+2,3+4] // Ein Array mit zwei Elementen. Das erste Element ist

// 3, das zweite 7.

Die Elementausdrücke in einem Array-Initialisierer können selbstauch wieder Array-Initialisierer sein, was bedeutet, dass diese Aus-drücke geschachtelte Arrays erstellen können:

var matrix = [[1,2,3], [4,5,6], [7,8,9]];

Nach dem letzten Ausdruck in einem Array-Initialisierer ist einnachstehendes Komma erlaubt.

Objektinitialisierungsausdrücke ähneln Array-Initialisierungsaus-drücken, stehen statt in eckigen Klammern aber in geschweiftenKlammern. Außerdem werden jedem Teilausdruck ein Eigen-schaftsname und ein Doppelpunkt vorangestellt:

var p = { x:2, y:1 }; // Ein Objekt mit zwei Eigenschaften.var q = {}; // Ein leeres Objekt ohne

// Eigenschaften.q.x = 2; q.y = 1; // Jetzt hat q die gleichen

// Eigenschaften wie p.

Objektliterale können ebenfalls geschachtelt werden. Zum Beispiel:

var rectangle = { upperLeft: { x: 2, y: 2 },lowerRight: { x: 4, y: 5 } };

Die Ausdrücke in Objekt- und Array-Initialisierern werden jedesMal ausgewertet, wenn der Objektinitialisierer ausgewertet wirdund müssen keine konstanten Werte haben: Es können beliebigeJavaScript-Ausdrücke sein. Außerdem können die Eigenschafts-namen in Objektliteralen Zeichenketten (mit impliziten Anfüh-rungszeichen) statt Bezeichner sein (was nützlich sein kann, wennman Eigenschaftsnamen angeben muss, die reservierte Wörter sindoder die aus anderem Grund keine zulässigen Bezeichner darstel-len):

var side = 1;var square = { "ul": { x: p.x, y: p.y },

'lr': { x: p.x + side, y: p.y + side}};

Ausdrücke | 27

Page 40: 3868993886_Script

Auf Eigenschaften zugreifenDer Eigenschaftszugriffsausdruck wird zum Wert einer Objekt-eigenschaft oder eines Array-Elements ausgewertet. JavaScript de-finiert zwei Syntaxformen für den Eigenschaftszugriff:

Ausdruck . BezeichnerAusdruck [ Ausdruck ]

Bei der ersten Art des Eigenschaftszugriffs wird ein Ausdruck ge-nutzt, auf den ein Punkt und dann ein Bezeichner folgen. DerAusdruck gibt das Objekt an und der Bezeichner den Namen dergewünschten Eigenschaft. Bei der zweiten Art des Eigenschafts-zugriffs folgt auf den ersten Ausdruck (das Objekt oder Array) einweiterer Ausdruck in eckigen Klammern. Dieser zweite Ausdruckgibt den Namen der gewünschten Eigenschaft oder den Index desgewünschten Array-Elements an. Hier sind einige konkrete Beispie-le:

var o = {x:1,y:{z:3}}; // Ein Beispielobjekt.var a = [o,4,[5,6]]; // Ein Array, das o enthalt.o.x // => 1: Eigenschaft x des Ausdrucks o.o.y.z // => 3: Eigenschaft z des Ausdrucks o.y.o["x"] // => 1: Eigenschaft x des Objekts o.a[1] // => 4: Das Element beim Index 1 des

// Ausdrucks a.a[2]["1"] // => 6: Das Element beim Index 1 des

// Ausdrucks a[2].a[0].x // => 1: Eigenschaft x des Ausdrucks a[0].

Die .Bezeichner-Syntax ist die einfachere der beiden Optionen fürden Eigenschaftszugriff. Beachten Sie jedoch, dass sie nur eingesetztwerden kann, wenn die Eigenschaft, auf die Sie zugreifen wollen,einen Namen hat, der ein zulässiger Bezeichnername ist, und wennSie den Namen des Bezeichners bereits beim Schreiben des Pro-gramms kennen. Wenn der Eigenschaftsname einem reserviertenWort entspricht, Leerzeichen oder Interpunktionszeichen enthältoder eine Zahl ist (bei Arrays), müssen Sie die Notation mit deneckigen Klammern verwenden. Eckige Klammern werden auchgenutzt, wenn der Eigenschaftsname nicht statisch, sondern selbstdas Ergebnis einer Berechnung ist.

28 | Kapitel 3: Ausdrücke und Operatoren

Page 41: 3868993886_Script

FunktionsdefinitionEin Funktionsdefinitionsausdruck definiert eine JavaScript-Funk-tion, und der Wert eines solchen Ausdrucks ist die neu definierteFunktion. In gewisser Weise ist ein Funktionsdefinitionsausdruckein »Funktionsliteral«, ähnlich wie ein Objektinitialisierer ein »Ob-jektliteral« ist. Ein Funktionsdefinitionsausdruck besteht üblicher-weise aus dem Schlüsselwort function, einer kommasepariertenListe weiterer Bezeichner (den Parameternamen) in Klammern undeinem Block JavaScript-Code (dem Funktionsrumpf) in geschweif-ten Klammern. Zum Beispiel:

// Diese Funktion liefert das Quadrat des ubergebenen Werts.var square = function(x) { return x * x; }

Funktionen können auch mit einer Funktionsanweisung statt einemFunktionsausdruck definiert werden. Vollständige Informationenzu Funktionsdefinitionen finden Sie in Kapitel 7.

AufrufAls Aufrufausdruck bezeichnet man JavaScripts Syntax für denAufruf (oder die Ausführung) einer Funktion oder Methode. Siebeginnt mit einem Funktionsausdruck, der die aufzurufende Funk-tion bezeichnet. Auf den Funktionsausdruck folgen eine öffnendeKlammer, eine kommagetrennte Liste mit null oder mehr Argumen-ten und eine schließende Klammer. Ein paar Beispiele:

f(0) // f ist der Funktionsausdruck;// 0 ist der Argumentausdruck.

Math.max(x,y,z) // Math.max ist die Funktion;// x, y und z sind die Argumente.

a.sort() // a.sort ist die Funktion; es gibt// keine Argumente.

Wird ein Aufrufausdruck ausgewertet, wird zuerst der Funktions-ausdruck ausgewertet. Anschließend werden die Argumentausdrü-cke ausgewertet, um eine Liste mit Argumentwerten zu erstellen. Istder Wert des Funktionsausdrucks keine Funktion, wird ein Type-Error ausgelöst. Anschließend werden die Argumentwerte der Reihenach den Parameternamen zugewiesen, die bei der Definition derFunktion angegeben wurden, bevor der Codeinhalt der Funktion

Ausdrücke | 29

Page 42: 3868993886_Script

ausgeführt wird. Nutzt die Funktion eine return-Anweisung, umeinen Wert zu liefern, wird dieser Wert zum Wert des Aufrufaus-drucks. Andernfalls ist der Wert des Aufrufausdrucks undefined.

Jeder Aufrufausdruck enthält ein Klammernpaar und einen Aus-druck vor diesen Klammern. Ist dieser Ausdruck ein Eigenschafts-zugriffsausdruck, bezeichnet man den Aufruf selbst als einen Me-thodenaufruf. Bei Methodenaufrufen wird das Objekt oder Array,das der Gegenstand des Eigenschaftsaufrufs ist, zum Wert des this-Parameters für die Ausführung des Codeinhalts der Funktion. Dasist das Fundament des objektorientierten Programmierparadigmas,bei dem Funktionen (die unter diesem Paradigma eben als »Metho-den« bezeichnet werden) auf dem Objekt operieren, dessen Teil siesind. Mehr dazu finden Sie in Kapitel 8.

ObjekterstellungEin Objekterstellungsausdruck erstellt ein neues Objekt und rufteine (als »Konstruktor« bezeichnete) Funktion auf, um die Eigen-schaften dieses Objekts zu initialisieren. Objekterstellungsausdrü-cke sind wie Aufrufausdrücke; ihnen wird allerdings das Schlüssel-wort new vorangestellt:

new Object()new Point(2,3)

Werden der Konstruktorfunktion in einem Objekterstellungsaus-druck keine Argumente übergeben, kann die leere Klammer wegge-lassen werden:

new Objectnew Date

Bei der Auswertung eines Objekterstellungsausdrucks erstellt Java-Script zunächst ein neues leeres Objekt wie das, das vom Objekt-initialisierer {} erstellt wird. Dann wird die angegebene Funktionmit den angegebenen Argumenten aufgerufen, und dabei wird dasneue Objekt als Wert des Schlüsselworts this übergeben. DieFunktion kann this dann nutzen, um die Eigenschaften des neuerstellten Objekts zu initialisieren. Funktionen, die zur Verwendungals Konstruktoren geschrieben werden, liefern üblicherweise keinenWert zurück. Der Wert des Objekterstellungsausdrucks ist dann

30 | Kapitel 3: Ausdrücke und Operatoren

Page 43: 3868993886_Script

das neu erstellte und initialisierte Objekt. Liefert ein Konstruktoreinen Objektwert zurück, wird dieser zum Wert des Objekterstel-lungsausdrucks, während das neu erstellte Objekt verworfen wird.

OperatorenOperatoren werden für JavaScripts arithmetische Ausdrücke, Ver-gleichsausdrücke, logische Ausdrücke, Zuweisungsausdrücke undanderes genutzt. Tabelle 3-1 bietet einen Überblick über die Opera-toren von JavaScript.

Tabelle 3-1: JavaScript-Operatoren

Operator Operation Typen

++ Prä- oder Post-Inkrement Lval→Zahl

-- Prä- oder Post-Dekrement Lval→Zahl

- Zahl negieren num→Zahl

+ in Zahl umwandeln Zahl→Zahl

~ Bits invertieren Int→Int

! booleschen Wert invertieren Bool→Bool

delete Eigenschaft löschen Lval→Bool

typeof Typ ermitteln bel→Str

void undefinierten Wert liefern bel→undef

*, /, % Multiplikation, Division, Rest Zahl,Zahl→Zahl

+, - Addieren, Subtrahieren Zahl,Zahl→Zahl

+ Strings verketten Str,Str→Str

<< nach links verschieben Int,Int→Int

>> mit Vorzeichenerweiterung nach rechts verschieben Int,Int→Int

>>> mit Nullauffüllung nach rechts verschieben Int,Int→Int

<, <=, >, >= in numerischer Folge vergleichen Zahl,Zahl→Bool

<, <=,>, >= in alphabetischer Folge vergleichen Str,Str→Bool

instanceof Objektklasse prüfen Obj,Func→Bool

in prüfen, ob es eine Eigenschaft gibt Str,Obj→Bool

== auf Gleichheit prüfen bel,bel→Bool

!= auf Ungleichheit prüfen bel,bel→Bool

=== auf Gleichheit im strengen Sinne prüfen bel,bel→Bool

!== auf Ungleichheit im strengen Sinne prüfen bel,bel→Bool

Operatoren | 31

Page 44: 3868993886_Script

Tabelle 3-1: JavaScript-Operatoren (Fortsetzung)

Operator Operation Typen

& bitweises UND berechnen Int,Int→Int

^ bitweises XODER berechnen Int,Int→Int

| bitweises ODER berechnen Int,Int→Int

&& logisches UND berechnen bel,bel→bel

|| logisches ODER berechnen bel,bel→bel

?: zweiten oder dritten Operanden wählen Bool,bel,bel→bel

= Zuweisung an eine Variable oder Eigenschaft Lval,bel→bel

*=, /=, %=, +=, Operation und Zuweisung Lval,bel→bel

-=, &=, ^=, |=,

<<=, >>=, >>>=

, ersten Operanden verwerfen, zweiten liefern bel,bel→bel

Die in Tabelle 3-1 aufgeführten Operatoren sind nach absteigendemVorrang angeordnet, und zwischen den verschiedenen Vorrang-stufen wurden Trennlinien eingefügt. Der Operatorvorrang steuert,in welcher Abfolge Operationen ausgeführt werden. Operatorenmit höherem Vorrang (die, die weiter oben in der Tabelle stehen)werden vor denen mit niedrigerem Vorrang (denen, die weiterunten in der Tabelle stehen) ausgeführt.

Betrachten Sie die folgenden Ausdrücke:

w = x + y*z;

Der Multiplikationsoperator * hat einen höheren Vorrang als derAdditionsoperator +, die Multiplikation wird also vor der Additionausgeführt. Darüber hinaus hat der Zuweisungsoperator = dengeringsten Vorrang, die Zuweisung wird also erst ausgeführt, nach-dem alle Operationen auf der rechten Seite ausgeführt wurden.

Der Operatorvorrang kann durch den expliziten Einsatz von Klam-mern überschrieben werden. Ändern Sie den Ausdruck folgender-maßen, wenn Sie erzwingen wollen, dass die Addition zuerst aus-geführt wird:

w = (x + y)*z;

32 | Kapitel 3: Ausdrücke und Operatoren

Page 45: 3868993886_Script

Beachten Sie, dass Eigenschaftszugriffs- und Aufrufausdrücke hö-heren Vorrang haben als alle in Tabelle 3-1 aufgeführten Operato-ren. Betrachten Sie folgenden Ausdruck:

typeof my.functions[x](y)

Obwohl typeof einer der Operatoren mit dem höchsten Vorrang ist,wird die typeof-Operation auf dem Ergebnis der beiden Eigen-schaftszugriffe und dem Funktionsaufruf ausgeführt.

Sollten Sie sich nicht sicher sein, welchen Vorrang die von Ihnenverwendeten Operatoren haben, ist es empfehlenswert, die Aus-wertung explizit durch die Einführung von Klammern zu steuern.Folgende Regeln sollten Sie sich auf alle Fälle merken: Multiplika-tion und Division werden vor Addition und Subtraktion ausgeführt;die Zuweisung hat sehr geringen Vorrang und wird fast immerzuletzt ausgeführt.

Einige Operatoren arbeiten mit Werten beliebiger Typen, aber diemeisten erwarten Operanden eines spezifischen Typs, und diemeisten Operatoren liefern einen Wert eines spezifischen Typs(oder werden zu einem solchen ausgewertet). Die Typen-Spalte inTabelle 3-1 gibt (vor dem Pfeil) die Typen der Operanden und (nachdem Pfeil) den Typ des Ergebnisses für die Operatoren an. DieAnzahl der Typen vor dem Pfeil ist ein Hinweis auf die Stelligkeiteines Operators. Unäre Operatoren besitzen einen Operanden, bi-näre Operatoren besitzen zwei, und der ternäre Operator ?: besitztgleich drei.

Das Verhalten einiger Operatoren kann vom Typ der verwendetenOperanden abhängig sein. Der auffälligste Fall ist der des +-Opera-tors, der numerische Operanden addiert, Stringoperanden hingegenverkettet. Gleichermaßen führen Vergleichsoperatoren wie < denVergleich in Abhängigkeit vom Operandentyp auf numerische oderalphabetische Weise durch. Bei den Beschreibungen der einzelnenOperatoren werden ihre Typabhängigkeiten erläutert und diedurchgeführten Umwandlungen angegeben.

Beachten Sie, dass die Zuweisungsoperatoren und einige wenigeandere der in Tabelle 3-1 aufgeführten Operatoren einen Operan-den des Typs Lval erwarten. Das ist eine Abkürzung für Lvalue

Operatoren | 33

Page 46: 3868993886_Script

(Linkswert). Dies ist ein geschichtlicher Begriff, der »einen Aus-druck, der auf der linken Seite einer Zuweisungsanweisung erschei-nen darf« bezeichnet. In JavaScript sind Variablen, Objekteigen-schaften und die Elemente von Arrays Lvalues.

Die Auswertung eines einfachen Ausdrucks wie 2 * 3 wirkt sich nieauf den Zustand Ihres Programms aus, d.h., dass keine der in derFolge von Ihrem Programm ausgeführten Berechnungen von dieserAuswertung beeinträchtigt wird. Einige Ausdrücke haben jedochSeiteneffekte – ihre Auswertung kann sich auf das Ergebnis spätererAuswertungen auswirken. Die Zuweisungsoperatoren sind das of-fensichtlichste Beispiel: Weisen Sie einer Variablen oder einer Ei-genschaft einen Wert zu, ändert das den Wert jedes Ausdrucks, derdiese Variable bzw. Eigenschaft nutzt. Die ++- und ---Inkrement-und Dekrement-Operatoren verhalten sich ebenfalls so, da sieimplizit eine Zuweisung durchführen. Auch der delete-Operatorhat Seiteneffekte: Wird eine Eigenschaft gelöscht, ist das ähnlich(aber nicht das Gleiche), als würde der Eigenschaft undefinedzugewiesen.

Arithmetische OperatorenDieser Abschnitt behandelt die Operatoren, die arithmetische oderanderere numerische Operationen auf ihren Operanden ausführen.

Multiplikation (*)Berechnet das Produkt seiner zwei Operanden.

Division (/)Der /-Operator teilt seinen ersten Operanden durch seinenzweiten Operanden. Wenn Sie an Programmiersprachen ge-wöhnt sind, die Ganz- und Gleitkommazahlen unterscheiden,erwarten Sie vielleicht ein ganzzahliges Ergebnis, wenn Sie eineganze Zahl durch eine andere ganze Zahl teilen. Aber da inJavaScript alle Zahlen Gleitkommazahlen sind, haben alle Divi-sionsoperationen Gleitkommazahlen zum Ergebnis: 5/2 wird zu2.5 ausgewertet, nicht zu 2. Die Division durch null liefert Plus-oder Minus-Unendlich, während 0/0 zu NaN ausgewertet wird:In keinem dieser Fälle wird ein Fehler gemeldet.

34 | Kapitel 3: Ausdrücke und Operatoren

Page 47: 3868993886_Script

Modulo (%)Der %-Operator führt eine Modulodivision durch, d.h., er liefertden Rest, der verbleibt, wenn man den ersten Operanden ganz-zahlig durch den zweiten Operanden teilt. Das Vorzeichen desErgebnisses entspricht dem Vorzeichen des ersten Operanden.Beispielsweise wird 5 % 2 zu 1 ausgewertet und -5 % 2 zu -1.Dieser Operator wird typischerweise mit ganzzahligen Operan-den verwendet, funktioniert aber auch mit Fließkommazahlen.Die Operation 6.5 % 2.1 ergibt beispielsweise 0.2.

Addition (+)Der binäre +-Operator addiert numerische Operanden oderverkettet Stringoperanden:

1 + 2 // => 3"Hallo" + " " + "Welt" // => "Hallo Welt""1" + "2" // => "12"1 + 2 + " blinde Mause"; // => "3 blinde Mause"1 + (2 + " blinde Mause"); // => "12 blinde Mause"

Wenn die Werte der Operanden beide Zahlen oder beideStrings sind, ist offensichtlich, was der +-Operator tut. In allenanderen Fällen sind hingegen Typumwandlungen erforderlich.Die Operation, die letztendlich ausgeführt wird, ist von denUmwandlungen abhängig, die vorgenommen werden. Die Um-wandlungsregeln für + bevorzugen die Stringverkettung: Isteiner der Operanden ein String oder ein Objekt, das in einenString umgewandelt werden kann, wird der andere Operand ineinen String umgewandelt und eine Stringverkettung durch-geführt. Eine Addition wird nur dann durchgeführt, wennkeiner der Operanden stringartig ist.

Subtraktion (-)Subtrahiert den Wert des Operanden auf der rechten Seite vomWert des links stehenden Operanden.

Zusätzlich zu den oben stehenden binären Operatoren werden inJavaScript auch einige unäre Operatoren für arithmetische Aufga-ben definiert. Unäre Operatoren modifizieren den Wert eines ein-zelnen Operanden, um einen neuen Wert zu erzeugen:

Arithmetische Operatoren | 35

Page 48: 3868993886_Script

Unäres Plus (+)Der unäre Plusoperator wandelt seinen Operanden in eine Zahl(oder NaN) um und liefert den umgewandelten Wert. Wird er aufeinem Operanden verwendet, der bereits eine Zahl ist, tut ernichts.

Unäres Minus (-)Wird - als unärer Operator verwendet, wird der Operand beiBedarf in eine Zahl umgewandelt, und dann wird das Vorzei-chen des Umwandlungsergebnisses geändert.

Inkrement (++)Der ++-Operator inkrementiert seinen einen Operanden (d.h.,fügt ihm 1 hinzu). Der Operand muss ein Lvalue sein (eineVariable, ein Array-Element oder eine Objekteigenschaft). DerOperator wandelt seinen Operanden bei Bedarf in eine Zahlum, fügt dieser 1 hinzu und weist den inkrementierten Wertwieder der Variablen, dem Element oder der Eigenschaft zu.

Der Rückgabewert des ++-Operators ist von seiner Stellung inBezug auf den Operanden abhängig. Steht er vor seinem Ope-randen (man spricht dann von einem Prä-Inkrement), inkre-mentiert er den Wert und wird dann zum inkrementierten Wertdes Operanden ausgewertet. Steht er hinter dem Operanden(dann spricht man von einem Post-Inkrement), inkrementiert erden Operanden, wird aber zum nicht inkrementierten Wert desOperanden ausgewertet. Erwägen Sie die Unterschiede zwi-schen diesen beiden Codezeilen:

var i = 1, j = ++i; // i und j sind beide 2.var i = 1, j = i++; // i ist 2, j ist 1.

Dieser Operator wird in seinen Erscheinungsformen als Prä-und Postinkrementoperator am häufigsten zur Steuerung einesZählers eingesetzt, der eine for-Schleife (siehe den Abschnitt»for« auf Seite 63) steuert.

Decrement (--)Der ---Operator erwartet einen Lvalue als Operanden. Er wan-delt den Wert seines Operanden in eine Zahl um, zieht 1 vonihm ab und weist den dekrementierten Wert wieder dem Ope-randen zu. Wie beim ++-Operator ist der Rückgabewert von der

36 | Kapitel 3: Ausdrücke und Operatoren

Page 49: 3868993886_Script

Stellung zum Operanden abhängig. Steht der Operator vor demOperanden, wird der Operand dekrementiert und der dekre-mentierte Wert geliefert; steht er hinter dem Operanden, wirdder Operand dekrementiert, aber der nicht dekrementierte Wertgeliefert.

Die Bit-Operatoren führen elementare Manipulationen der Bits inder binären Repräsentation von Zahlen durch. Diese Operatorenwerden in der JavaScript-Programmierung nicht besonders oft ein-gesetzt. Wenn Sie sich nicht mit der binären Darstellung dezimalerGanzzahlen auskennen, können Sie diesen Abschnitt wahrschein-lich überspringen. Die Bit-Operatoren erwarten ganzzahlige Ope-randen und verhalten sich, als würden diese Werte als 32-Bit-Ganz-zahlwerte und nicht als 64-Bit-Gleitkommawerte repräsentiert. Siewandeln ihre Operanden bei Bedarf in Zahlen um und zwingen dienumerischen Werte dann in ein 32-Bit-Ganzzahlformat, indem alleBruchteile und Bits nach dem 32. Bit fallen gelassen werden. DieVerschiebungsoperatoren verlangen auf der rechten Seite einenOperanden zwischen 0 und 31.

Bitweises UND (&)Der &-Operator führt eine Boolean-UND-Operation auf allenBits seiner ganzzahligen Operanden durch. Ein Bit im Ergebniswird nur dann gesetzt, wenn das entsprechende Bit in beidenOperanden gesetzt ist. Beispielsweise wird 0x1234 & 0x00FF zu0x0034 ausgewertet.

Bitweises ODER (|)Der |-Operator führt eine boolesche ODER-Operation auf allenBits seiner ganzzahligen Operanden durch. Ein Bit im Ergebnisist gesetzt, wenn das entsprechende Bit in einem oder in beidenOperanden gesetzt ist. Beispielsweise wird 0x1234 | 0x00FF zu0x12FF ausgewertet.

Bitweises XOR (^)Der ^-Operator führt eine exklusive boolesche ODER-Opera-tion oder XOR-Operation auf den einzelnen Bits seiner ganz-zahligen Operanden durch. Exklusiv ODER heißt, dass ein Bitentweder im einen Operanden oder im anderen Operandenwahr ist, aber nicht in beiden. Ein Bit im Ergebnis der Operation

Arithmetische Operatoren | 37

Page 50: 3868993886_Script

ist gesetzt, wenn das entsprechende Bit in einem (aber nichtbeiden) Operanden gesetzt ist. Beispielsweise wird 0xFF00 ^0xF0F0 zu 0x0FF0 ausgewertet.

Bitweises NICHT (~)Der ~-Operator ist ein unärer Operator, der vor einem ganz-zahligen Operanden erscheint. Er bewirkt, dass alle Bits imOperanden umgekehrt werden. Die Art, wie Ganzzahlen mitVorzeichen in JavaScript repräsentiert werden, bewirkt, dass dieAnwendung des ~-Operators auf einen Wert das gleiche Ergeb-nis hat, als würde sein Vorzeichen geändert und dann 1 vomZwischenergebnis abgezogen. Beispielsweise wird ~0x0F zu0xFFFFFFF0 oder –16 ausgewertet.

Verschiebung nach links (<<)Der <<-Operator verschiebt alle Bits in seinem ersten Operan-den um die Anzahl von Stellen nach links, die durch denzweiten Operanden angegeben werden. Beispielsweise wird inder Operation a << 1 das erste Bit (das Einer-Bit) von a zumzweiten Bit (das Zweier-Bit), das zweite Bit von a zum dritten Bitusw. Für das neue erste Bit wird eine Null eingesetzt, und derWert des 32. Bits geht verloren. Eine Verschiebung um einePosition nach links entspricht einer Multiplikation mit 2, eineVerschiebung von zwei Positionen einer Multiplikation mit 4und so weiter. Beispielsweise wird 7 << 2 zu 28 ausgewertet.

Verschiebung nach rechts mit Vorzeichen (>>)Der >>-Operator verschiebt alle Bits in seinem ersten Operan-den um die durch den zweiten Operanden angegebenen Stellennach rechts. (Der zweite Operand ist eine ganze Zahl zwischen 0und 31.) Bits, die nach rechts hinausgeschoben werden, gehenverloren. Wie die neuen Bits auf der linken Seite gefüllt werden,hängt vom Vorzeichen des ursprünglichen Operanden ab, da-mit das Vorzeichen im Ergebnis bewahrt bleibt. Ist der ersteOperand positiv, werden die neuen hohen Bits mit Nullenaufgefüllt. Ist der erste Operand negativ, werden die neuenhohen Bits mit Einsen aufgefüllt. Eine Verschiebung um einePosition nach rechts entspricht einer ganzzahligen Divisiondurch 2 (wobei der Rest verworfen wird), eine Verschiebung

38 | Kapitel 3: Ausdrücke und Operatoren

Page 51: 3868993886_Script

um zwei Stellen einer restlosen Division durch 4 und so weiter.Beispielsweise wird 7 >> 1 zu 3 ausgewertet und --7 >> 1 zu –4.

Verschiebung nach rechts mit Nullauffüllung (>>>)Der >>>-Operator entspricht dem >>-Operator, aber die vonlinks eingeschobenen Bits werden immer mit Null gefüllt –unabhängig davon, welches Vorzeichen der erste Operand hat.Beispielsweise wird -1 >> 4 zu -1 ausgewertet und -1 >>> 4 zu0x0FFFFFFF.

Relationale OperatorenJavaScripts relationale Operatoren prüfen Verhältnisse (wie»gleich«, »kleiner« oder »Eigenschaft von«) zwischen zwei Wertenund liefern true oder false, je nachdem, ob dieses Verhältnisbesteht oder nicht. Relationale Ausdrücke werden immer zu einembooleschen Wert ausgewertet, und dieser Wert wird häufig einge-setzt, um den Ablauf der Programmausführung in if-, while- undfor-Anweisungen zu steuern (siehe Kapitel 4).

JavaScript kennt =-, ==- und ===-Operatoren. Es ist wichtig, dassIhnen die Unterschiede zwischen dem Zuweisungs-, dem Gleich-heits- und dem Identitätsoperator klar sind, und achten Sie beimProgrammieren sorgfältig darauf, dass Sie tatsächlich den benötigenOperator verwenden! Obgleich es verführerisch sein mag, bei allendrei Operatoren jeweils »gleich« zu lesen, können Sie sich wahr-scheinlich Irritationen ersparen, wenn Sie für = »wird zugewiesen«,für == »ist gleich« und für === »ist identisch mit« lesen.

Strikte Gleichheit (ist identisch mit) (===)Der ===-Operator wird auch als Operator für strikte Gleichheit(gelegentlich auch als Identitätsoperator) bezeichnet. Er über-prüft, ob die zwei Operanden "identisch" sind. Hierbei kommteine strikte Definition von Gleichheit zum Einsatz, die keineÄnderungen am Typ der Operanden zulässt. Haben die Ope-randen einen unterschiedlichen Typ, sind sie im Sinne diesesOperators nicht gleich. Haben beide Operanden dagegen dengleichen elementaren Typ, gelten sie als gleich. Verweisen beideOperanden auf das gleiche Objekt, Array oder die gleiche Funk-

Relationale Operatoren | 39

Page 52: 3868993886_Script

tion, gelten sie als gleich. Beziehen sie sich auf verschiedeneObjekte, werden die Operanden nicht als gleich angesehen,selbst wenn die Objekte die gleichen Eigenschaften besitzen.Auf ähnliche Weise gelten zwei Arrays, die die gleichen Ele-mente in derselben Reihenfolge enthalten, nicht als gleich. Dereinzige Haken an diesem Operator ist sein Verhalten, bei »KeineZahl«-(NaN-)Werten. NaN-Werte sind niemals identisch mitetwas anderem, nicht einmal mit sich selbst.

Strikte Ungleichheit (ist nicht identisch mit) (!==)Der !== -Operator ist das genaue Gegenteil des ===-Operators:Er gibt false zurück, wenn zwei Werte identisch sind, ansons-ten true.

Gleichheit (==)Der ==-Operator (mit zwei Gleichheitszeichen statt drei) verhältsich wie der Identitätsoperator, ist aber weniger strikt. Habendie Werte der beiden Operanden unterschiedliche Typen, wirdversucht, sie entsprechend umzuwandeln. Danach wird einerneuter Vergleich vorgenommen. Dieser Operator sieht dieWerte null und undefined als gleich an – eine oftmals hilfreicheTyp-Umwandlung. Andere vom ==-Operator durchgeführteUmwandlungen sind jedoch noch überraschender. Sämtlicheunten stehenden Vergleiche haben das Ergebnis true:

1 == "1"true == 1"1" == truefalse == 0[] == 0

Ungleichheit (!=)Der Operator != ist das genaue Gegenteil des ==-Operators. Ergibt false zurück, wenn die beiden Werte gleich sind, ansons-ten ist das Ergebnis true.

Die Vergleichsoperatoren überprüfen die relative Reihenfolge (nu-merisch oder alphabetisch) ihrer zwei Operanden. Operanden, diekeine Zahlen oder Zeichenketten sind, werden bei Bedarf umge-wandelt. Zeichenketten werden von diesen Operatoren als Folgenvon 16-Bit-Integerwerten behandelt. Der Vergleich zweier Zeichen-ketten ist also einfach nur ein nummerischer Vergleich der Werte

40 | Kapitel 3: Ausdrücke und Operatoren

Page 53: 3868993886_Script

der beiden Strings. Beachten Sie insbesondere, dass StringvergleicheGroß-/Kleinschreibung berücksichtigen und dass alle ASCII-Groß-buchstaben »kleiner als« alle ASCII-Kleinbuchstaben sind. DieseRegel kann zu verwirrenden Ergebnissen führen, wenn Sie aufdieses Verhalten nicht eingestellt sind. Beispielsweise kommt fürden <-Operator der String »Zoo« vor dem String »aal«:

Kleiner als (<)Der <-Operator wird zu true ausgewertet, wenn sein ersterOperand kleiner ist als der zweite, andernfalls zu false.

Größer als (>)Der >-Operator wird zu true ausgewertet, wenn sein ersterOperand größer ist als der zweite, andernfalls zu false.

Kleiner oder gleich (<=)Der <=-Operator wird zu true ausgewertet, wenn sein ersterOperand kleiner oder gleich dem zweiten Operanden ist, an-dernfalls zu false.

Größer oder gleich (>=)Der >=-Operator wird zu true ausgewertet, wenn sein ersterOperand größer oder gleich dem zweiten Operanden ist, an-dernfalls zu false.

Die letzten beiden relationalen Operatoren sind in und instanceof:

Existenz von Eigenschaften (in)Der in-Operator erwartet auf seiner linken Seite einen Operan-den, der ein String ist oder in einen String umgewandelt werdenkann, und auf seiner rechten Seite einen Operanden, der einObjekt ist. Er wird zu true ausgewertet, wenn der Wert auf derlinken Seite der Name einer Eigenschaft des Objekts auf derrechten Seite ist. Zum Beispiel:

var p = { x:1, y:1 };"x" in p // => true: p hat eine Eigenschaft

// namens "x"."z" in p // => false: p hat keine Eigenschaft

// namens "z"."toString" in p // => true: p erbt toString()-Methode.

Relationale Operatoren | 41

Page 54: 3868993886_Script

var a = [7,8,9];"0" in a // => true: Array hat Element "0".1 in a // => true: Zahlen werden in Strings umgewandelt.

Objekttyp (instanceof)Der instanceof-Operator erwartet auf der linken Seite einenOperanden, der ein Objekt ist, und auf der rechten Seite einenOperanden, der eine Klasse von Objekten angibt. Der Operatorwird zu true ausgewertet, wenn das Objekt auf der linken Seiteeine Instanz der Klasse auf der rechten Seite ist, andernfalls zufalse. Kapitel 8 erläutert, dass in JavaScript Klassen von Ob-jekten durch die Konstruktorfunktion definiert werden, die sieinitialisiert. Der rechte Operand für den instanceof-Operatorsollte also eine Funktion sein. Hier sind einige Beispiele:

var d = new Date();d instanceof Date; // => trued instanceof Object; // => trued instanceof Number; // => falsevar a = [1, 2, 3];a instanceof Array; // => truea instanceof Object; // => true

Logische AusdrückeDie logischen Operatoren &&, || und ! führen boolesche Algebradurch und werden häufig gemeinsam mit den relationalen Opera-toren genutzt, um zwei relationale Ausdrücke zu einem komplexe-ren Ausdruck zu vereinen. Zum besseren Verständnis sollten Siebedenken, dass die Werte null, undefined, 0, "" und NaN immer als»falsch« angesehen werden, d.h., sie werden in einem logischenAusdruck als false angesehen. Alle anderen Objekte und Arrayswerden dagegen als »wahr« angesehen und evaluieren entsprechendals true.

Der &&-Operator kann auf drei verschiedenen Ebenen betrachtetwerden. Auf der einfachsten Stufe, wenn er mit booleschen Ope-randen verwendet wird, führt && einfach die boolesche UND-Ope-ration auf den beiden Werten durch. Der Operator liefert nur danntrue, wenn der erste und der zweite Operand true sind. Wenn eineroder beide Operanden false sind, liefert er false.

42 | Kapitel 3: Ausdrücke und Operatoren

Page 55: 3868993886_Script

&& wird häufig zum Verbinden zweier relationaler Ausdrücke ge-nutzt:

// nur 'wahr' wenn x und y beide den Wert 0 habenx == 0 && y == 0

Da relationale Ausdrücke immer zu true oder false ausgewertetwerden, liefert auch der Operator selbst true oder false, wenn erauf diese Weise verwendet wird. Relationale Operatoren habeneinen höheren Vorrang als && (und ||), Ausdrücke wie dieser kön-nen also problemlos ohne Klammern geschrieben werden.

Aber && verlangt nicht, dass seine Operanden boolesche Werte sind.In zweiter Hinsicht kann && also als boolesches UND für wahre undfalsche Werte verstanden werden. Sind beide Operanden wahreWerte, liefert der Operator einen wahren Wert. Andernfalls, d.h.,wenn mindestens einer der Operanden ein falscher Wert ist, lieferter einen falschen Wert. In JavaScript können alle Ausdrücke oderAnweisungen, die einen booleschen Wert erwarten, mit wahrenund falschen Werten umgehen. Deswegen verursacht der Umstand,dass && nicht immer zu true oder false evaluiert, in der Praxiskeinerlei Probleme.

Beachten Sie, dass die Beschreibung oben sagt, dass der Operator»einen wahren Wert« oder »einen falschen Wert« liefert, aber nichtangibt, was dieser Wert tatsächlich ist. Dazu müssen wir && indritter und letzter Hinsicht beschreiben. Der Operator beginntdamit, dass er seinen ersten Operanden, den Ausdruck zu seinerLinken, auswertet. Ist der Wert dieses Operanden ein falscher Wert,muss auch der Wert des gesamten Ausdrucks ein falscher Wert sein.&& liefert deswegen einfach den Wert auf der linken Seite und wertetden Ausdruck auf der rechten Seite nicht einmal aus.

Ist der Wert auf der linken Seite hingegen ein wahrer Wert, hängtder Wert des gesamten Ausdrucks vom Wert auf der rechten Seiteab. Ist der Wert auf der rechten Seite ein wahrer Wert, muss dergesamte Ausdruck ein wahrer Wert sein; ist er ein falscher Wert, soist auch der gesamte Ausdruck falsch. Wenn der Wert auf der linkenSeite ein wahrer Wert ist, wertet der &&-Operator also einfach denWert auf der rechten Seite aus und liefert ihn zurück:

Logische Ausdrücke | 43

Page 56: 3868993886_Script

var o = { x : 1 };var p = null;o && o.x // => 1: o ist wahr, es wird also der Wert

// von o.x geliefert.p && p.x // => null: p ist falsch und wird geliefert;

// p.x wird nicht ausgewertet.

Es ist wichtig, dass Sie begreifen, dass der Operand auf der rechtenSeite von && manchmal ausgewertet wird, manchmal nicht. Im Codeoben war die Variable p auf null gesetzt. Der Ausdruck p.x hätte,wäre er ausgewertet worden, zu einem TypeError geführt. Aber derCode nutzt && auf idiomatische Weise, damit p.x nur ausgewertetwird, wenn p wahr ist – nicht null oder undefined.

Der ||-Operator führt auf seinen beiden Operanden eine boolescheODER-Operation durch. Sind beide Operanden wahre Werte, lie-fert er einen wahren Wert; sind beide Operanden falsche Werte,liefert er einen falschen.

Obwohl der ||-Operator in der Regel meist einfach als boolescherODER-Operator zum Einsatz kommt, hat er wie && ein komplexeresVerhalten. Er beginnt damit, dass er seinen ersten Operanden, denOperanden auf seiner linken Seite, auswertet. Ist der Wert diesesOperanden ein wahrer Wert, liefert er diesen wahren Wert. An-dernfalls wertet er seinen zweiten Operanden, den Ausdruck aufseiner rechten Seite, aus, und liefert den Wert dieses Ausdrucks.

Wie beim &&-Operator sollten Sie auf der rechten Seite Operandenvermeiden, die Seiteneffekte haben, es sei denn, Sie haben explizitvor, den Umstand auszunutzen, dass der Ausdruck auf der rechtenSeite eventuell nicht ausgewertet wird.

Eine idiomatische Verwendungsweise dieses Operators ist sein Ein-satz zur Auswahl des ersten wahren Wertes in einer Menge vonAlternativen:

// max_width nutzen, wenn es definiert ist. Andernfalls// einen Wert im Objekt preferences nachschlagen.// Wenn es auch den nicht gibt,// auf eine Konstante zuruckgreifen.var max = max_width || preferences.max_width || 500;

Dieses Konstrukt wird häufig im Codeinhalt von Funktionen ge-nutzt, um Standardwerte für Parameter anzugeben:

44 | Kapitel 3: Ausdrücke und Operatoren

Page 57: 3868993886_Script

// Die Eigenschaften von o in p kopieren und p liefern.function copy(o, p) {

// Wurde fur p kein Objekt ubergeben,// ein neu erstelltes nutzen.p = p || {};// Inhalt der Funktion

}

Der !-Operator ist ein unärer Operator, der vor einem einzigenOperanden steht. Sein Zweck ist es, den booleschen Wert seinesOperanden zu negieren. Ist x ein wahrer Wert, wird !x zu falseausgewertet. Ist x ein falscher Wert, wird !x zu true ausgewertet. Da! immer true oder false liefert, können Sie einen beliebigen Wert xin den entsprechenden booleschen Wert umwandeln, indem Siediesen Operator zweimal anwenden: !!x.

Da ! ein unärer Operator ist, hat er hohen Vorrang und bindetstark. Wenn Sie den Wert eines Ausdrucks wie p && q invertierenwollen, müssen Sie deswegen Klammern nutzen: !(p && q).

ZuweisungsausdrückeMit dem =-Operator können Sie in JavaScript Variablen, Objekt-eigenschaften oder Array-Elementen einen Wert zuweisen. ZumBeispiel:

i = 0 // Setzt die Variable i auf 0.o.x = 1 // Setzt die Eigenschaft x von Objekt o auf 1.

Der =-Operator erwartet, dass der Operand auf seiner linken Seiteein Lvalue ist: eine Variable oder eine Objekteigenschaft (oder einArray-Element). Als rechtsseitigen Operanden erwartet er einenbeliebigen Wert eines beliebigen Typs. Der Wert eines Zuweisungs-ausdrucks ist der Wert seines rechtsseitigen Operanden. Als Seiten-effekt weist der =-Operator den Wert auf seiner rechten Seite derVariablen oder Eigenschaft auf seiner linken Seite zu, damit spätereReferenzen auf die Variable oder Eigenschaft zu diesem Wert aus-gewertet werden.

Der Zuweisungsoperator ist rechtsassoziativ. Das bedeutet, dassmehrere Zuweisungsoperatoren in einem Ausdruck von rechts

Zuweisungsausdrücke | 45

Page 58: 3868993886_Script

nach links ausgewertet werden. Sie können also Code wie diesenschreiben, um mehreren Variablen den gleichen Wert zuzuweisen:

i = j = k = 0; // Initialisiert 3 Variablen auf 0.

Neben dem normalen =-Zuweisungsoperator unterstützt JavaScripteine Reihe weiterer Zuweisungsoperatoren, die Kurzformen zurKombination einer Zuweisung mit einer anderen Operation dar-stellen. Beispielsweise führt der +=-Operator eine Addition und eineZuweisung aus. Der Ausdruck

total += sales_tax

ist gleichwertig zu diesem:

total = total + sales_tax

Wie man erwarten könnte, kann der +=-Operator mit Strings undZahlen umgehen. Bei numerischen Operanden führt er Additionund Zuweisung aus, bei Stringoperanden Verkettung und Zuwei-sung.

Ähnliche Operatoren sind unter anderem -=, *=, &= und so weiter.

AuswertungsausdrückeWie viele interpretierte Sprachen hat JavaScript die Fähigkeit,Strings mit JavaScript-Quellcode zu interpretieren und auszuwer-ten, um einen Wert hervorzubringen. In JavaScript erfolgt das mitder globalen Funktion eval():

eval("3+2") // => 5

Die dynamische Auswertung von Strings mit Quellcode ist einemächtige Spracheinrichtung, die in der Praxis fast nie notwendigist. Wenn Sie auf eval() zurückgreifen, sollten Sie sorgfältig über-legen, ob Sie es tatsächlich benötigen. Technisch gesehen ist eval()eigentlich eine Funktion. Wir behandeln sie trotzdem hier, weil siesich in vielerlei Hinsicht eher wir ein Operator verhält. Die dyna-mische Auswertung von Strings mit Quellcode ist eine mächtigeSpracheinrichtung, die in der Praxis fast nie notwendig ist. WennSie auf eval() zurückgreifen, sollten Sie sorgfältig überlegen, ob Siees tatsächlich benötigen.

46 | Kapitel 3: Ausdrücke und Operatoren

Page 59: 3868993886_Script

eval() erwartet ein Argument. Übergeben Sie einen anderen Wertals einen String, liefert es einfach diesen Wert zurück. ÜbergebenSie einen String, versucht es, diesen String als JavaScript-Code zuparsen, und löst einen SyntaxError aus, wenn dieser Versuch schei-tert. Kann es den String erfolgreich parsen, wertet es den Code ausund liefert den Wert des letzten Ausdrucks oder der letzten Anwei-sung im String oder undefined, wenn der letzte Ausdruck bzw. dieletzte Anweisung keinen Wert hatte. Löst der ausgewertete Stringeinen Fehler aus, wird dieser vom Aufruf an eval() weitergereicht.

Das Wesentliche bei eval() (wenn es auf diese Weise aufgerufenwird) ist, dass es die Variablenumgebung des Codes nutzt, der esaufruft. Das heißt, dass es die Werte von Variablen auf gleicheWeise nachschlägt und neue Variablen und Funktionen auf gleicheWeise definiert wie lokaler Code. Definiert eine Funktion einelokale Variable x und ruft dann eval("x") auf, erhält sie den Wertder lokalen Variablen. Ruft sie eval("x=1") auf, ändert sie den Wertder lokalen Variablen. Und wenn die Funktion eval("var y = 3;")aufruft, hat sie eine neue lokale Variable y definiert. Mit Code wiedem folgenden kann eine Funktion auf vergleichbare Weise aucheine lokale Funktion deklarieren:

eval("function f() { return x+1; }");

eval() besitzt eine sehr ungewöhnliche Einschränkung (die benö-tigt wird, damit der JavaScript-Interpreter effizient funktioniert): Esfunktioniert nur auf die oben beschriebene Weise, wenn es mitseinem tatsächlichen Namen »eval« aufgerufen wird. Da eval() imtechnischen Sinne eine Funktion ist, können wir es auch eineranderen Variablen zuweisen. Wird eval() über diese Variable auf-gerufen, verhält es sich anders: Wird es mit einem anderen Namenaufgerufen, wertet eval() den String aus, als würde es sich umglobalen Code handeln. Der ausgewertete Code kann neue globaleVariablen und Funktionen definieren und setzen, aber keine beste-henden Variablen aus dem lokalen Geltungsbereich der aufrufen-den Funktion verwenden oder verändern.

Vor dem IE9 verhielt sich der IE anders als andere Browser: Er führtkein globales Eval aus, wenn eval() unter einem anderen Namenaufgerufen wird. Der IE definiert eine globale Funktion namens

Auswertungsausdrücke | 47

Page 60: 3868993886_Script

execScript(), die ihr Stringargument ausführt, als wäre es ein Top-Level-Skript. Im Gegensatz zu eval() ist der Rückgabewert vonexecScript() immer null.

Der ECMAScript 5-Strict-Modus versieht das Verhalten von eval()mit weiteren Einschränkungen. Wird eval() aus Code im Strict-Modus aufgerufen oder beginnt der auszuwertende Code selbst mitder Direktive »use strict«, führt eval() ein lokales Eval mit einerprivaten Variablenumgebung aus. Das bedeutet, dass so ausgewer-teter Code im Strict-Modus lokale Variablen abfragen und setzen,aber keine neuen Variablen oder Funktionen im lokalen Geltungs-bereich definieren kann. Außerdem macht der Strict-Modus eval()noch operatorartiger, indem er »eval« praktisch zu einem reser-vierten Wort macht.

Verschiedene OperatorenJavaScript unterstützt eine Reihe von Operatoren zu anderen Zwe-cken, die in den folgenden Abschnitten beschrieben werden.

Der Bedingungsoperator (?:)Der Bedingungsoperator ist der einzige Ternäroperator (drei Ope-randen) in JavaScript. Dieser Operator wird gelegentlich in derForm ?: geschrieben, obwohl er im Code nicht ganz in dieserForm erscheint. Weil dieser Operator drei Operatoren hat, kommtder erste vor das ?, der zweite zwischen ? und : und der dritte nachdem :. Er wird folgendermaßen verwendet:

x > 0 ? x : -x // Der Absolutwert von x

Die Operanden des Bedingungsoperators können einen beliebigenTyp haben. Der erste Operand wird ausgewertet und als boolescherWert interpretiert. Ist der erste Operand ein wahrer Wert, wird derzweite Operand ausgewertet und sein Wert zurückgeliefert. Ist dererste Operand hingegen ein falscher Wert, wird der dritte Operandausgewertet und sein Wert zurückgeliefert. Es wird immer entwederder erste oder der zweite Operand ausgewertet, nie beide.

48 | Kapitel 3: Ausdrücke und Operatoren

Page 61: 3868993886_Script

Obgleich man mit der if-Anweisung (siehe den Abschnitt »if« aufSeite 58) Ähnliches erreichen kann, erweist sich der h?:-Operatorhäufig als eine praktische Kurzform. Hier ist eine typische Verwen-dung, in der geprüft wird, ob eine Eigenschaft definiert ist (undeinen sinnvollen, wahren Wert hat). Falls dem so ist, wird sieverwendet, andernfalls wird auf einen Vorgabewert ausgewichen:

greeting = "Hallo " + (user.name ? user.name : "Welt!");

Der typeof-Operatortypeof ist ein unärer Operator, der seinem Operanden vorangestelltwird, der einen beliebigen Typ haben kann. Sein Wert ist ein String,der den Typ des Operanden angibt. Die folgende Tabelle gibt denWert des typeof-Operators für alle JavaScript-Werte an:

x typeof x

undefined "undefined"

null "object"

true oder false "boolean"

eine beliebige Zahl oder NaN "number"

ein beliebiger String "string"

eine beliebige Funktion "function"

jedes native Objekt, das keine Funktion ist "object"

Sie könnten den typeof-Operator in einem Ausdruck wie diesemnutzen:

(typeof value == "string") ? "'" + value + "'" : value

Der delete-Operatordelete ist ein unärer Operator, der versucht, die Objekteigenschaftoder das Array-Element zu löschen, die bzw. das als sein Operandangegeben wird. (Sollten Sie C++-Programmierer sein, müssen Siebeachten, dass das delete-Schlüsselwort von JavaScript keinerleiÄhnlichkeit mit dem delete-Schlüsselwort von C++ hat.) Wie dieOperatoren für Zuweisung, Inkrement und Dekrement wird delete

Verschiedene Operatoren | 49

Page 62: 3868993886_Script

üblicherweise wegen seines Seiteneffekts, Eigenschaften zu löschen,eingesetzt, und nicht für den Wert, den es liefert. Einige Beispiele:

var o = {x:1, y:2}, a = [1,2,3];delete o.x; // Eine seiner Eigenschaften loschen."x" in o // => false: Die Eigenschaft gibt es nicht mehr.delete a[2]; // Sein letztes Element loschen.2 in a // => false: Array-Element 2 exisitiert nicht.

Der void-Operatorvoid ist ein unärer Operator, der vor seinem Operanden steht, dereinen beliebigen Typ haben kann. Dieser Operator ist ungewöhn-lich und wird nur sehr selten eingesetzt: Er wertet seinen Operan-den aus, verwirft aber dann den Wert und liefert undefined. Da derWert des Operanden verworfen wird, ist void nur sinnvoll, wennder Operand Seiteneffekte hat.

Der Kommaoperator (,)Der Kommaoperator ist ein binärer Operator, der mit Operandenbeliebigen Typs umgehen kann. Er wertet seinen linken Operandenaus, wertet seinen rechten Operanden aus und liefert dann denWert des rechten Operanden. Der Ausdruck auf der linken Seitewird immer ausgewertet, aber sein Wert wird verworfen. Der Ein-satz des Kommaoperators ist also nur sinnvoll, wenn der linksseitigeAusdruck Seiteneffekte hat. Der einzige Ort, an dem der Kom-maoperator häufig verwendet wird, ist eine for-Schleife (siehe denAbschnitt »for« auf Seite 63) mit mehreren Schleifenvariablen:

// Das erste Komma unten ist Teil der Syntax der// var-Anweisung, das zweite der Kommaoperator:// Quetschen wir 2 Ausdrucke (i++ and j--) in eine// Anweisung (die for-Schleife), die einen erwartet.for(var i=0,j=10; i < j; i++,j--)

console.log(i+j);

50 | Kapitel 3: Ausdrücke und Operatoren

Page 63: 3868993886_Script

KAPITEL 4

Anweisungen

Kapitel 3 beschrieb Ausdrücke als JavaScript-Äußerungen. Im Sinnedieser Analogie wären Anweisungen JavaScript-Sätze oder -Befehle.So wie man im Deutschen Sätze mit Punkten beendet und von-einander abgrenzt, werden JavaScript-Anweisungen mit Semikola(siehe den Abschnitt »Optionale Semikola« auf Seite 3) beendet.Ausdrücke werden ausgewertet, um einen Wert hervorzubringen;Anweisungen hingegen werden ausgeführt, um etwas geschehen zulassen.

Eine Möglichkeit, »etwas geschehen zu lassen«, besteht darin, einenAusdruck auszuwerten, der Seiteneffekte hat. Ausdrücke mit Seiten-effekten wie Zuweisungen oder Funktionsaufrufe können eigen-ständig als Anweisungen stehen und werden, wenn sie auf dieseWeise verwendet werden, als Ausdrucksanweisungen bezeichnet.Eine ähnliche Kategorie von Anweisungen sind die Deklarations-anweisungen, die neue Variablen deklarieren und neue Funktionendefinieren.

JavaScript-Programme sind nichts anderes als eine Folge auszufüh-render Anweisungen. Standardmäßig führt der JavaScript-Interpre-ter diese Anweisungen nacheinander in der Reihenfolge aus, in dersie im Programm erscheinen. Eine weitere Möglichkeit, »etwasgeschehen zu lassen«, besteht darin, die Standardabfolge der Aus-führung zu ändern. JavaScript bietet eine Reihe von Anweisungenoder Kontrollstrukturen, die genau das tun:

• Bedingungen sind Anweisungen wie if und switch, die denJavaScript-Interpreter auf Basis des Wertes eines Ausdrucks

| 51

Page 64: 3868993886_Script

veranlassen, bestimmte Anweisungen auszuführen oder an-dere zu überspringen.

• Schleifen sind Anweisungen wie while und for, die andereAnweisungen wiederholt ausführen.

• Sprünge sind Anweisungen wie break, return und throw, dieden Interpreter veranlassen, zu einem anderen Teil des Pro-gramms zu springen.

Die folgende Tabelle 4-1 enthält eine Zusammenfassung der Syntaxfür JavaScript-Anweisungen. Details zu den einzelnen Anweisungenfinden Sie in den folgenden Abschnitten.

Tabelle 4-1: Syntax der JavaScript-Anweisungen

Anweisung Syntax Zweck

break break [Marke]; Beendet die innerste Schleife, switch-Anweisung oder benannte umgebendeAnweisung.

case case Ausdruck: Bezeichnet eine Anweisung innerhalbeines switch-Blocks.

continue continue [Marke]; Beginnt die nächste Iteration der inners-ten Schleife oder benannten Schleife.

debugger debugger; Breakpoint für den Debugger

default default: Bezeichnet die Standard-Anweisunginnerhalb eines switch-Blocks.

do/while do Anweisung while(Ausdruck);

Eine Alternative zur while-Schleife

empty ; Nichts tun

for for(init; test; incr)Anweisung

Eine einfach zu benutzende Schleife

for/in for (var in Objekt)Anweisung

Iteriert über die Eigenschaften vonObjekt.

function functionname([param[,...]]){ body }

Deklariert die benannte Funktion Name.

if/else if (expr) Anweisung1[else Anweisung2]

Führe Anweisung1 oder Anweisung2aus.

label label: Anweisung Versehe die Anweisung mit demNamen Bezeichnung.

return return [Ausdruck]; Gibt den Wert einer Funktion zurück.

52 | Kapitel 4: Anweisungen

Page 65: 3868993886_Script

Tabelle 4-1: Syntax der JavaScript-Anweisungen (Fortsetzung)

Anweisung Syntax Zweck

switch switch (Ausdruck){ Anweisungen }

Mehrfachverzweigung zu case- oderdefault:-Bezeichnern

throw throw Ausdruck; Ausnahmefehler auslösen

try try {Anweisungen}

[catch { Anweisungen }]

[finally{ Anweisungen }]

Ausnahmen abfangen

use strict "use strict"; Einschränkungen des »Strict«-Modus aufSkript oder Funktion anwenden

var var name [ = expr][ ,... ];

Eine oder mehrere Variablen deklarierenund initialisieren

while while (Ausdruck)Anweisung

Ein einfache Schleife

with with (Objekt) Anweisung Erweitert eine Kette von Geltungsberei-chen (im »Strict«-Modus nicht erlaubt).

AusdrucksanweisungenDie einfachste Art von Anweisungen in JavaScript sind Ausdrückemit Seiteneffekten. Anweisungen dieser Art sahen Sie in Kapitel 3.Zuweisungsanweisungen sind eine wichtige Kategorie von Aus-drucksanweisungen. Zum Beispiel:

greeting = "Hallo " + name;i *= 3;

Die Inkrement- und Dekrementoperatoren, ++ und --, sind mit denZuweisungsanweisungen verwandt. Sie haben den Seiteneffekt,dass der Wert einer Variablen geändert wird, als wäre eine Zuwei-sung erfolgt:

counter++;

Der delete-Operator hat den wichtigen Seiteneffekt, dass eineObjekteigenschaft gelöscht wird. Er wird also fast immer als eigen-ständige Anweisung und nicht als Teil eines umfangreicheren Aus-drucks eingesetzt:

delete o.x;

Ausdrucksanweisungen | 53

Page 66: 3868993886_Script

Funktionsaufrufe sind eine weitere wichtige Kategorie von Aus-drucksanweisungen. Zum Beispiel:

alert(greeting);window.close();

Diese clientseitigen Funktionsaufrufe sind Ausdrücke, haben aberSeiteneffekte, die sich auf den Browser auswirken, und werden hierals Anweisungen genutzt.

Zusammengesetzte und leereAnweisungenEin Anweisungsblock kombiniert mehrere Anweisungen zu einereinzigen zusammengesetzten Anweisung. Ein Anweisungsblock isteinfach eine Folge von Anweisungen, die in geschweifte Klammerneingeschlossen ist. Die folgenden Zeilen verhalten sich also wie eineeinzige Anweisung und können überall eingesetzt werden, woJavaScript nur eine Anweisung erwartet:

{x = Math.PI;cx = Math.cos(x);console.log("cos(p) = " + cx);

}

Die Kombination von Anweisungen zu umfangreicheren Anwei-sungsblöcken ist in der JavaScript-Programmierung äußerst üblich.So wie Ausdrücke häufig Unterausdrücke enthalten, so enthaltenAnweisungen häufig Unteranweisungen. Formal gestattet die Java-Script-Syntax in der Regel nur eine einzige Unteranweisung. Bei-spielsweise erlaubt die Syntax der while-Schleife nur eine einzigeAnweisung als Schleifeninhalt. Anweisungsblöcke ermöglichen esIhnen, in dieser einen zulässigen Unteranweisung beliebig vieleAnweisungen unterzubringen.

Eine zusammengesetzte Anweisung ermöglicht es Ihnen, mehrereAnweisungen einzusetzen, wo die JavaScript-Syntax eine einzigeAnweisung erwartet. Die leere Anweisung ist genau das Gegenteil:Sie ermöglicht es Ihnen, keine Anweisung anzugeben, wo eine er-wartet wird.

54 | Kapitel 4: Anweisungen

Page 67: 3868993886_Script

Die leere Anweisung sieht so aus:

;

Der JavaScript-Interpreter unternimmt nichts, wenn er eine leereAnweisung ausführt. Die leere Anweisung kann gelegentlich prak-tisch sein, wenn Sie eine Schleife erstellen wollen, die keinen Inhalthat:

// Die Elemente von a bis 0 initialisierenfor(i = 0; i < a.length; a[i++] = 0) /* empty */;

DeklarationsanweisungenDie var- und function-Anweisungen sind Deklarationsanweisungen– sie deklarieren oder definieren Variablen und Funktionen. DieseAnweisungen definieren Bezeichner (Variablen- und Funktions-namen), die an anderen Stellen Ihres Programms genutzt werdenkönnen und denen Sie Werte zuweisen können. Deklarationsanwei-sungen selbst leisten nicht viel, aber indem Sie Variablen undFunktionen erstellen, definieren Sie in erheblichem Umfang dieBedeutung anderer Anweisungen in Ihrem Programm.

varDie var-Anweisung deklariert eine oder mehrere Variablen. So siehtdie Syntax aus:

var Name_1 [ = Wert_1] [ ,..., Name_n [= Wert_n]]

Auf das Schlüsselwort var folgt eine kommaseparierte Liste der zudeklarierenden Variablen. Jede Variable in der Liste kann optionaleinen Initialisierungsausdruck haben, der ihren anfänglichen Wertfestlegt. Zum Beispiel:

var i; // Eine einfache Variable.var j = 0; // Eine Variable, ein Wert.var p, q; // Zwei Variablen.var greeting = "hello" + name; // Ein komplexer

// Initialisierer.var x = 2, y = x*x; // Das zweite var nutzt

// das erste.

Deklarationsanweisungen | 55

Page 68: 3868993886_Script

var x = 2, // Mehrere Variablen ...f = function(x) { return x*x }, // jede auf einery = f(x); // eigenen Zeile.

Erscheint eine var-Anweisung im Codeinhalt einer Funktion, defi-niert sie eine lokale Variable, die nur in dieser Funktion Geltung hat.Wird var in Top-Level-Code verwendet, definiert es hingegen glo-bale Variablen, die im gesamten JavaScript-Programm sichtbar sind.

Wird für eine Variable in der var-Anweisung kein Initialisiererangegeben, ist der anfängliche Wert der Variablen undefined.

Beachten Sie, dass die var-Anweisung auch als Teil der for- undfor/in-Schleifen erscheinen kann:

for(var i = 0; i < 10; i++) console.log(i);for(var i = 0, j=10; i < 10; i++,j--) console.log(i*j);for(var i in o) console.log(i);

functionDas Schlüsselwort function wird genutzt, um Funktionen zu dekla-rieren. Wir sahen es im Funktionsdefinitionsausdruck im Abschnitt»Funktionsdefinition« auf Seite 29 aus Kapitel 3. Es kann aucheigenständig in Anweisungsform eingesetzt werden:

// Ausdruck wird einer Variablen zugewiesen.var f = function(x) { return x+1; }// Anweisung schließt Variablennamen ein.function f(x) { return x+1; }

Eine Funktionsdeklarationsanweisung hat die folgende Syntax:

function funcname([arg1 [, arg2 [..., argn]]]) {statements

}

funcname ist ein Bezeichner, der den Namen der deklarierten Funk-tion angibt. Auf den Funktionsnamen folgt in Klammern einekommaseparierte Liste mit Parameternamen. Diese Bezeichner kön-nen im Funktionsinhalt genutzt werden, um auf die Argumentwertezuzugreifen, die beim Aufruf der Funktion übergeben wurden.

Der Inhalt der Funktion besteht aus einer beliebigen Anzahl vonJavaScript-Anweisungen, die in geschweifte Klammern eingeschlos-

56 | Kapitel 4: Anweisungen

Page 69: 3868993886_Script

sen werden. Diese Anweisungen werden nicht ausgeführt, wenn dieFunktion definiert wird. Stattdessen werden sie mit dem neuenFunktionsobjekt verknüpft und zur Ausführung beim Aufruf derFunktion vorgemerkt.

Hier sind einige Beispiele für Funktionsdeklarationen:

function hypotenuse(x, y) {return Math.sqrt(x*x + y*y);

}

function factorial(n) { // Eine rekursive Funktion.if (n <= 1) return 1;return n * factorial(n - 1);

}

Funktionsdeklarationsanweisungen können in Top-Level-JavaScript-Code erscheinen oder eingebettet in anderen Funktionsdeklaratio-nen. Wenn sie in andere Funktionen eingebettet sind, dürfen Funk-tionsdeklarationen allerdings nur auf der obersten Ebene stehen. Dasheißt, Funktionsdefinitionen dürfen nicht in if-Anweisungen, while-Schleifen oder anderen Anweisungen erscheinen.

Funktionsdeklarationsanweisungen unterscheiden sich von Funk-tionsdefinitionsausdrücken darin, dass sie einen Funktionsnameneinschließen. Beide Formen erstellen ein neues Funktionsobjekt,aber die Funktionsdeklarationsanweisung deklariert zugleich denFunktionsnamen als Variable und weist ihr das Funktionsobjekt zu.Wie die Variablen, die mit var deklariert werden, werden Funk-tionsdefinitionsanweisungen implizit an den Anfang des Skriptsbzw. der Funktion »gehoben«, in dem bzw. der sie enthalten sind;alle Funktionen in einem Skript oder alle eingebetteten Funktionenin einer Funktion sind also deklariert, bevor irgendwelcher Codeausgeführt wird. Das bedeutet, dass Sie eine JavaScript-Funktionaufrufen können, bevor Sie sie deklarieren.

BedingungenBedingungsanweisungen führen andere Anweisungen aus oder über-springen andere Anweisungen auf Basis des Werts eines angegebenenAusdrucks. Diese Anweisungen sind die Entscheidungspunkte Ihres

Bedingungen | 57

Page 70: 3868993886_Script

Codes und werden gelegentlich auch als »Verzweigungen« bezeich-net. Wenn Sie sich vorstellen, der JavaScript-Interpreter folge einemPfad durch Ihren Code, dann sind Bedingungsanweisungen dieStellen im Code, an denen sich der Code in zwei oder mehr Pfadeverzweigt, unter denen der Interpreter einen auswählen muss.

ifDie if-Anweisung ist die grundlegende Kontrollanweisung, die esJavaScript ermöglicht, Anweisungen bedingt auszuführen. DieseAnweisung gibt es in zwei Formen. Die erste sieht so aus:

if (Ausdruck)Anweisung

In dieser Form wird zunächst der Ausdruck ausgewertet und dann,wenn der resultierende Wert ein wahrer Wert ist, die Anweisungausgeführt. Ergibt Ausdruck einen falschen Wert, wird Anweisungnicht ausgeführt:

if (username == null) // Wenn username null oder// undefined ist,

username = "Hans Mustermann"; // username definieren

Beachten Sie, dass die Klammern um den Ausdruck ein erforderli-cher Teil der Syntax der if-Anweisung sind.

Die zweite Form der if-Anweisung führt eine else-Klausel ein, dieausgeführt wird, wenn Ausdruck false ist. Ihre Syntax sieht so aus:

if (Ausdruck)Anweisung1

elseAnweisung2

Diese Form der Anweisung führt Anweisung_1 aus, wenn Ausdruckein wahrer Wert ist, und Anweisung_2, wenn Ausdruck einen falschenWert ergibt. Zum Beispiel:

if (n == 1) {console.log("Sie haben 1 neue Nachricht.");

}else {

console.log("Sie haben " + n + " neue Nachrichten.");}

58 | Kapitel 4: Anweisungen

Page 71: 3868993886_Script

else ifDie if/else-Anweisung wertet in Abhängigkeit von einem Aus-druck eine von zwei Codealternativen aus. Aber was ist, wenn Siemehr als zwei Alternativen haben? Eine Lösung für dieses Problembietet die else if-Anweisung. else if ist eigentlich keine JavaScript-Anweisung, sondern ein häufig eingesetztes Programmieridiom, dasresultiert, wenn mehrere if/else-Anweisungen kombiniert werden:

if (n == 1) {// Block 1 ausfuhren.

}else if (n == 2) {

// Block 2 ausfuhren.}else if (n == 3) {

// Block 3 ausfuhren.}else {

// Wenn alles andere scheitert, Block 4 ausfuhren.}

An diesem Code ist nichts Bemerkenswertes. Er ist einfach eineFolge von if-Anweisungen, bei denen jedes folgende if Teil derelse-Klausel der vorangehenden Anweisung ist. Das else if-Idiomist empfehlenswerter und besser lesbar als die syntaktisch äquiva-lente, vollständig geschachtelte Form dieser Anweisungen:

if (n == 1) {// Block 1 ausfuhren.

}else {

if (n == 2) {// Block 2 ausfuhren.

}else {

if (n == 3) {// Block 3 ausfuhren.

}else {

// Wenn alles andere scheitert, Block 4// ausfuhren.

}}

}

Bedingungen | 59

Page 72: 3868993886_Script

switchEine if-Anweisung bewirkt eine Verzweigung im Ablauf der Pro-grammauswertung, und das else if-Idiom ermöglicht mehrereVerzweigungen. Es ist jedoch nicht die beste Lösung, wenn alleZweige vom Wert des gleichen Ausdrucks abhängen. Ist das derFall, wäre es Verschwendung, den gleichen Ausdruck in if-Anwei-sungen wiederholt auszuwerten.

Die switch-Anweisung kümmert sich um eben diese Situation. Aufdas Schlüsselwort switch folgen ein Ausdruck in Klammern und einCodeblock in geschweiften Klammern:

switch(Ausdruck) {Anweisungen

}

Aber die vollständige Syntax einer switch-Anweisung ist kompli-zierter. Unterschiedliche Positionen im Codeblock werden mit demcase-Schlüsselwort versehen, auf das ein Ausdruck und ein Doppel-punkt folgen. case ist wie eine markierte Anweisung, nur dass diemarkierte Anweisung nicht mit einem Namen verknüpft wird,sondern mit einem Ausdruck. Wird eine switch-Anweisung aus-geführt, berechnet sie den Wert von Ausdruck und sucht dann nacheiner case-Marke, deren Ausdruck zum gleichen Wert ausgewertetwird (wobei die Gleichheit über den ===-Operator geprüft wird).Findet sie eine passende Marke, beginnt sie den Codeblock an dermit case markierten Anweisung auszuführen. Findet sie kein casemit einem passenden Wert, sucht sie nach einer Anweisung mit derMarke default:. Gibt es keine default:-Marke, überspringt switchden gesamten Codeblock.

Die folgende switch-Anweisung entspricht der Folge von if/else-Anweisungen aus dem letzten Abschnitt:

switch(n) {case 1: // Hier beginnen, wenn n == 1.

// Block 1 ausfuhren.break; // Hier enden.

case 2: // Hier beginnen, wenn n == 2.// Block 2 ausfuhren.break; // Hier enden.

case 3: // Hier beginnen, wenn n == 3.

60 | Kapitel 4: Anweisungen

Page 73: 3868993886_Script

// Block 3 ausfuhren.break; // Hier enden.

default: // Wenn alles andere scheitert ...// Block 4 ausfuhren.break; // Hier enden.

}

Beachten Sie den Einsatz des Schlüsselworts break am Ende dereinzelnen case-Abschnitte im Code oben. Die break-Anweisung, dieweiter unten in diesem Kapitel beschrieben wird, veranlasst denInterpreter, zum Ende der switch-Anweisung auszubrechen und mitder auf sie folgenden Anweisung fortzufahren. Die case-Marken ineiner switch-Anweisung markieren nur den Anfangspunkt des ge-wünschten Codes, keinen Endpunkt. Gibt es keine break-Anwei-sungen, würde switch mit der Ausführung seines Codeblocks dortbeginnen, wo sich die case-Marke mit dem Wert befindet, derihrem Ausdruck entspricht, und so lange mit der Ausführung vonAnweisungen fortfahren, bis das Ende des Blocks erreicht ist. In derRegel sollten Sie jeden case-Abschnitt mit einer break- order return-Anweisung beenden.

Hier ist ein etwas realistischeres Beispiel für eine switch-Anweisung.Sie wandelt einen Wert in Abhängigkeit von seinem Typ in einenString um:

function convert(x) {switch(typeof x) {

case 'number': // Die Zahl in eine hexadezimale// Ganzzahl umwandeln.

return x.toString(16);case 'string': // Den String in Anfuhrungszeichen

// eingeschlossen zuruckliefern.return '"' + x + '"';

default: // Jeden anderen Typ auf die ubliche// Weise umwandeln.

return String(x);}

}

Beachten Sie, dass auf das case in den beiden vorangehendenBeispielen Zahl- bzw. Stringliterale folgen. So wird die switch-An-weisung in der Praxis am häufigsten verwendet. Sie sollten aberwissen, dass der ECMAScript-Standard nach case einen beliebigenAusdruck gestattet.

Bedingungen | 61

Page 74: 3868993886_Script

SchleifenUm Bedingungsanweisungen zu verstehen, haben wir uns vor-gestellt, dass der JavaScript-Interpreter einen sich verzweigendenPfad durch Ihren Quellcode nimmt. Schleifenanweisungen sindAnweisungen, die den Pfad wieder auf sich selbst zurückbiegenund Teile Ihres Codes wiederholt ausführen. JavaScript hat vierSchleifenanweisungen: while, do/while, for und for/in.

whileDie while-Anweisung ist die grundlegende Schleifenanweisung vonJavaScript. Sie hat die folgende Syntax:

while (Ausdruck)Anweisung

Bei der Ausführung einer while-Anweisung wertet der Interpreterzuerst den Ausdruck aus. Ist der Wert des Ausdrucks ein falscherWert, überspringt der Interpreter die Anweisung, die als Inhalt derSchleife dient, und geht zur nächsten Anweisung in Ihrem Pro-gramm weiter. Ergibt der Ausdruck einen wahren Wert, führt derInterpreter die Anweisung aus, springt zurück zum Anfang derSchleife und wertet Ausdruck erneut aus. Das kann man auchfolgendermaßen ausdrücken: Der Interpreter führt Anweisung solange erneut aus, wie Ausdruck einen wahren Wert ergibt. BeachtenSie, dass Sie mit der Syntax while(true) eine Endlosschleife erstellenkönnen.

Hier ist ein Beispiel für eine while-Schleife, die die Zahlen von 0 bis9 ausgibt:

var count = 0;while (count < 10) {

console.log(count);count++;

}

Wie Sie sehen können, hat die Variable count zu Anfang den Wert 0und wird bei jeder Ausführung des Schleifeninhalts inkrementiert.Wurde die Schleife 10-mal ausgeführt, wird der Ausdruck false(d.h., der Wert der Variablen count ist nicht länger kleiner als 10).

62 | Kapitel 4: Anweisungen

Page 75: 3868993886_Script

Die while-Anweisung wird also beendet, und der Interpreter kannzur nächsten Zeile im Programm weitergehen.

do/whileDie do/while-Schleife ist wie eine while-Schleife, nur dass der Schlei-fenausdruck am Ende nicht am Anfang der Schleife getestet wird. Dasheißt, dass der Inhalt der Schleife mindestens einmal ausgeführt wird.Die Syntax dieser etwas ungewöhlichen Schleife lautet:

doAnweisung

while (Ausdruck);

Hier ist ein Beispiel für eine do/while-Schleife:

function printArray(a) {var len = a.length, i = 0;if (len == 0)

console.log("Leeres Array");else {

do {console.log(a[i]);

} while (++i < len);}

}

forDie for-Anweisung vereinfacht Schleifen, die einem verbreitetenMuster entsprechen. Die meisten Schleifen haben irgendeine ArtSchleifenzähler. Diese Variable wird initialisiert, bevor die Schleifebeginnt, und vor jedem Durchlauf der Schleife geprüft. Schließlichwird die Zählervariable am Ende der Schleife inkrementiert oder aufandere Weise aktualisiert, unmittelbar bevor sie wieder geprüftwird. Bei Schleifen dieser Art sind Initialisierung, Prüfung undAktualisierung die drei entscheidenden Manipulationen der Schlei-fenvariable. Die for-Anweisung kodiert diese drei Schritte als Aus-drücke und macht diese Ausdrücke zu einem expliziten Bestandteilder Schleifensyntax:

for(Initialisierung ; Test ; Inkrement)Anweisung

Schleifen | 63

Page 76: 3868993886_Script

Initialisierung, Test und Inkrement sind drei Ausdrücke (diedurch Semikola getrennt werden), die für die Initialisierung, Prü-fung und das Inkrementieren der Schleifenvariablen verantwortlichsind. Dass sie in der ersten Zeile der Schleife stehen, sorgt dafür,dass leicht zu erkennen ist, was eine for-Schleife macht. Das ver-hindert Fehler, zum Beispiel dass Sie vergessen, die Schleifenvaria-ble zu initialisieren oder zu inkrementieren.

Am einfachsten lässt sich die Funktionsweise der for-Schleife an-hand einer äquivalenten while-Schleife erklären:

Initialisierung;while(Test) {

AnweisungInkrement;

}

Anders gesagt: Der Ausdruck Initialisierung wird einmal aus-gewertet – bevor die Schleife beginnt. Soll er einen Zweck haben,muss dieser Ausdruck Seiteneffekte haben (in der Regel eine Zuwei-sung). JavaScript erlaubt, dass Initialisierung eine Variablende-klarationsanweisung mit var ist, damit Sie den Schleifenzählergleichzeitig deklarieren und initialisieren können. Der AusdruckTest wird vor jedem Durchlauf der Schleife geprüft und steuert, obder Inhalt der Schleife ausgeführt wird. Wird Test zu einem wahrenWert ausgewertet, wird die Anweisung ausgeführt, die den Inhalt derSchleife bildet. Schließlich wird der Ausdruck Inkrement ausgewer-tet. Auch das sollte ein Ausdruck mit Nebeneffekten sein, damit ereinen Sinn hat. Im Allgemeinen ist es ein Zuweisungsausdruck oderein Ausdruck, der die Operatoren ++ oder -- nutzt.

Die Zahlen von 0 bis 9 können wir mit einer for-Schleife wie derfolgenden ausgeben. Vergleichen Sie sie mit der äquivalenten while-Schleife, die weiter oben gezeigt wurde:

for(var count = 0; count < 10; count++)console.log(count);

64 | Kapitel 4: Anweisungen

Page 77: 3868993886_Script

for/inDie for/in-Anweisung nutzt das Schlüsselwort for, bildet aber eineganz andere Art von Schleife als die gewöhnliche for-Schleife. Einefor/in-Schleife sieht so aus:

for (Variable in Objekt)Anweisung

Variable bezieht sich typischerweise auf eine benannte Variable.Stattdessen kann aber auch eine var -Anweisung verwendet werden,die eine einzelne Variable direkt »vor Ort« deklariert. Objekt stehtfür einen Ausdruck, dessen Ergebnis ein Objekt ist. Wie üblich istAnweisung die Anweisung bzw. der Anweisungsblock, der als Inhaltder Schleife dient.

Es ist kein Problem, die Elemente eines Arrays mit einer gewöhnli-chen for-Schleife zu durchlaufen:

// Die Array-Indizes der Variablen i zuweisen.for(var i = 0; i < a.length; i++)

console.log(a[i]); // Den Wert jedes Array-Elements// ausgeben.

Die for/in-Schleife erleichtert es, das Gleiche für die Eigenschafteneines Objekts zu tun:

// Die Eigenschaftsnamen von o der Variablen p zuweisen.for(var p in o)

console.log(o[p]); // Den Wert jeder Eigenschaft// ausgeben.

Um eine for/in-Anweisung auswerten zu können, muss der Java-Script-Interpreter zunächst den object-Ausdruck auswerten. Da-nach wird der Schleifenkörper für jede aufzählbare Eigenschaft desErgebnisobjekts ausgeführt. Vor jeder Iteration weist der Interpreterdabei der Variablen den Namen der gerade bearbeiteten Eigenschaftzu.

Die for/in-Schleife durchläuft nicht alle Eigenschaften eines Ob-jekts, sondern nur die, die enumerierbar sind (siehe den Abschnitt»Eigenschaftsattribute« auf Seite 90 in Kapitel 5). Die diversen ein-gebauten Methoden, die vom Sprachkern von JavaScript definiertwerden, sind nicht enumerierbar. Alle Objekte haben beispielsweise

Schleifen | 65

Page 78: 3868993886_Script

eine toString()-Methode, trotzdem wird die toString-Eigenschaftvon der for/in-Schleife nicht durchlaufen. Neben den eingebautenMethoden sind auch eine Reihe weiterer Eigenschaften der einge-bauten Objekte nicht enumerierbar. Alle Eigenschaften und Metho-den, die von Ihrem Code definiert werden, sind hingegen enume-rierbar. (Aber in ECMAScript 5 können Sie sie mit den im Abschnitt»Eigenschaftsattribute« auf Seite 90 beschriebenen Techniken nichtenumerierbar machen.)

Die ECMAScript-Spezifikation beschreibt nicht, in welcher Reihen-folge die for/in-Schleife die Eigenschaften eines Objekts durchläuft.In der Praxis ist es jedoch so, dass die JavaScript-Implementierun-gen der wichtigsten Browser-Hersteller die Eigenschaften einfacherObjekte in der Reihenfolge ihrer Hinzufügung durchlaufen, d.h.,ältere Eigenschaften werden zuerst durchlaufen. Wurde ein Objektüber ein Objektliteral erstellt, entspricht die Enumerationsreihen-folge der Reihenfolge, in der die Eigenschaften im Literal erschie-nen. Hierbei sollten Sie bedenken, dass die Reihenfolge der Auf-zählung sich nicht auf alle Objekte anwenden lässt. Dies giltbesonders für Objekte, die als Eigenschaft Array-Indizes enthalten.Diese Eigenschaften werden nicht in der Reihenfolge ihrer Erstel-lung, sondern in numerischer Reihenfolge aufgezählt.

SprüngeEine weitere Kategorie von JavaScript-Anweisungen sind Sprung-anweisungen. Wie der Name impliziert, veranlassen sie den Java-Script-Interpreter, an eine andere Stelle des Quellcodes zu springen.Die break-Anweisung veranlasst den Interpreter, ans Ende einerSchleife oder einer anderen Anweisung zu springen. continue lässtden Interpreter den Rest des Schleifeninhalts überspringen und zumAnfang der Schleife zurückkehren, um einen neuen Schleifendurch-lauf zu beginnen. JavaScript erlaubt es, Anweisungen mit Namenoder Marken zu versehen, und break und continue können dieZielschleife oder Anweisung über diese Marke angeben. Die return-Anweisung veranlasst den Interpreter von einem Funktionsaufrufzu dem Code zurückzuspringen, der die Funktion aufruft, und gibtauch den Wert für den Aufruf an. Die throw-Anweisung löst eine

66 | Kapitel 4: Anweisungen

Page 79: 3868993886_Script

Exception aus und ist für den Einsatz mit der try/catch/finally-Anweisung gedacht, die einen Block mit Code einrichtet, für den einException-Handler vorhanden ist.

Markierte AnweisungenJede Anweisung kann markiert werden, indem ihr ein Bezeicher undein Doppelpunkt vorangestellt werden:

Bezeichner: Anweisung

Wenn Sie eine Anweisung markieren, geben Sie ihr einen Namen,den Sie nutzen können, um an anderer Stelle des Programms auf siezu verweisen. Sie können jede Anweisung markieren, obgleich es nursinnvoll ist, Anweisungen zu markieren, die einen Inhalt haben:Schleifen oder Bedingungen beispielsweise. Geben Sie einer Schleifeeinen Namen, können Sie im Inhalt der Schleife break- und continue-Anweisungen nutzen, um die Schleife zu verlassen oder sofort wiederan den Anfang der Schleife zu springen, um den nächsten Durchlaufzu beginnen. break und continue sind die einzigen JavaScript-Anwei-sungen, die Markierungen nutzen. Sie werden später in diesem Ka-pitel behandelt. Hier ist ein Beispiel einer markierten while-Schleifeund einer continue-Anweisung, die die Markierung nutzt.

mainloop: while(token != null) {// Code ausgespart ...continue mainloop; // Zum nachsten Durchlauf der

// angegebenen Schleife springen.// Weiterer ausgesparter Code ...

}

breakDie break-Anweisung bewirkt, wenn sie eigenständig verwendetwird, dass die innerste Schleife oder switch-Anweisung sofort ver-lassen wird. Ihre Syntax ist einfach:

break;

Weil sie das Verlassen einer Schleife oder einer switch-Anweisungbewirkt, ist diese Form der break-Anweisung nur innerhalb vonAnweisungen dieser Art zulässig.

Sprünge | 67

Page 80: 3868993886_Script

Sie haben bereits Beispiele für die break-Anweisung in einer switch-Anweisung gesehen. In Schleifen wird sie üblicherweise eingesetzt,um die Schleife frühzeitig abzubrechen, wenn es aus irgendeinemGrund nicht mehr erforderlich ist, die Schleife fortzusetzen. Wenneine Schleife komplexe Abbruchbedingungen hat, ist es häufigeinfacher, einige dieser Bedingungen über break-Anweisungen zuimplementieren, anstatt alle in einen einzigen Schleifenausdruck zupacken. Der folgende Code durchsucht die Elemente eines Arraysnach einem bestimmten Wert. Die Schleife wird normal beendet,wenn das Ende des Arrays erreicht wird. Sie wird mit einer break-Anweisung beendet, wenn sie das findet, was sie im Array sucht:

for(var i = 0; i < a.length; i++) {if (a[i] == target) break;

}

Auch wenn das in der Praxis selten vorkommt, erlaubt JavaScript,nach dem Schlüsselwort break eine Anweisungsmarkierung anzuge-ben (nur den Bezeichner, ohne Doppelpunkt):

break Markenname;

Wird break mit einer Marke verwendet, springt es zum Ende, d.h.,es beendet die umschließende Anweisung, die mit der entsprechen-den Marke markiert ist. Es ist ein Syntaxfehler, break in dieser Formzu verwenden, wenn es keine umschließende Anweisung mit derentsprechenden Marke gibt. Die markierte Anweisung muss beidieser Form der break-Anweisung keine Schleife oder switch-An-weisung sein: break kann aus einer beliebigen umschließendenAnweisung »ausbrechen«.

continueDie continue-Anweisung ähnelt der break-Anweisung. Aber anstatteine Schleife zu verlassen, stößt continue einen neuen Durchlauf derSchleife an. Die Syntax der continue-Anweisung ist ebenso einfachwie die der break-Anweisung:

continue;

68 | Kapitel 4: Anweisungen

Page 81: 3868993886_Script

Die continue-Anweisung kann ebenfalls mit einer Marke verwendetwerden:

continue Markenname;

Die continue-Anweisung kann in beiden Formen nur im Codeinhalteiner Schleife verwendet werden. Jeder Einsatz an anderer Stelleführt zu einem Syntaxfehler.

Das folgende Beispiel zeigt eine nicht markierte continue-Anwei-sung, die genutzt wird, um den Rest des aktuellen Schleifendurch-laufs zu überspringen, wenn ein Fehler eintritt:

for(i = 0; i < data.length; i++) {if (isNaN(data[i])) continue; // Nicht-Zahlen

// uberspringen.total += data[i];

}

Wie die break-Anweisung kann die continue-Anweisung bei ge-schachtelten Schleifen mit Marken verwendet werden, wenn dieSchleife, die neu gestartet werden soll, nicht die innerste umschlie-ßende Schleife ist.

returnDenken Sie daran, dass Funktionsaufrufe Ausdrücke sind und dassalle Ausdrücke einen Wert haben. Eine return-Anweisung in einerFunktion gibt den Wert des Aufrufs der Funktion an. So sieht dieSyntax der return-Anweisung aus:

return Ausdruck;

Eine return-Anweisung darf nur im Codeinhalt einer Funktion er-scheinen. Es ist ein Syntaxfehler, wenn sie an anderer Stelle auf-taucht. Wenn die return-Anweisung ausgeführt wird, liefert dieFunktion, die sie enthält, den Wert von Ausdruck an den Aufrufer.Zum Beispiel:

function square(x) { return x*x; } // Gibt x hoch 2 zuruck.square(2) // Dieser Aufruf wird zu 4 ausgewertet.

Enthält eine Funktion keine return-Anweisung, dann werden ein-fach alle Anweisungen im Codeinhalt der Funktion ausgeführt, bis

Sprünge | 69

Page 82: 3868993886_Script

das Ende der Funktion erreicht wird und die Kontrolle wieder anden Aufrufer übergeben wird. In diesem Fall wird der Aufrufaus-druck mit undefined ausgewertet. Die return-Anweisung kann auchohne Ausdruck verwendet werden, damit die Funktion bereits vorihrem Ende undefined zurückgibt. Zum Beispiel:

function display_object(o) {// Sofort zuruckkehren, wenn o null oder undefined ist.if (!o) return;// Hier folgt der Rest der Funktion ...

}

throwEine Exception (oder Ausnahme) ist ein Signal, das anzeigt, dassirgendeine Ausnahmebedingung oder ein Fehler aufgetreten ist.Löst man eine Exception aus, signalisiert man einen solchen Fehlerbzw. eine solche Ausnahmebedingung. Ausgelöste Exceptions fängtman ab, um sie zu behandeln – d.h., um die Aktionen vorzuneh-men, die erforderlich oder nötig sind, um das Programm trotz derException weiter auszuführen. In JavaScript werden Exceptionsausgelöst, wenn ein Laufzeitfehler auftritt und wenn das Programmmit der throw-Anweisung explizit eine Exception auslöst. Abge-fangen werden Exceptions mit der try/catch/finally-Anweisung,die als Nächstes beschrieben wird.

Die throw-Anweisung hat die folgende Syntax:

throw Ausdruck;

Ausdruck kann zu einem Wert eines beliebigen Typs ausgewertetwerden. Sie können eine Zahl verwenden, die einen Fehlercoderepräsentiert, oder einen String, der eine lesbare Fehlermeldungenthält. Die Klasse Error und ihre Unterklassen werden verwendet,wenn der JavaScript-Interpreter selbst einen Fehler auslöst. Sieselbst können diese auch nutzen. Ein Error-Objekt hat eine name-Eigenschaft, die den Fehlertyp angibt, und eine message-Eigen-schaft, die den String enthält, der der Konstruktorfunktion überge-ben wurde (siehe die Error-Klasse im Referenzabschnitt). Hier isteine Beispielfunktion, die ein Error-Objekt auslöst, wenn sie miteinem ungültigen Argument aufgerufen wird:

70 | Kapitel 4: Anweisungen

Page 83: 3868993886_Script

function factorial(x) {// Exception auslosen, wenn x ungultig ist!if (x < 0) throw new Error("x darf nicht negativ sein");// Andernfalls einen Wert berechnen und ganz normal// zuruckkehren.for(var f = 1; x > 1; f *= x, x--) /* leer */ ;return f;

}

Wenn eine Exception ausgelöst wird, beendet der JavaScript-Inter-preter sofort die normale Programmausführung und springt zumnächsten Exception-Handler. Der Exception-Handler wird mit dercatch-Klausel der try/catch/finally-Anweisung verfasst, die imnächsten Abschnitt beschrieben wird. Wenn mit dem Codeblock,in dem die Exception ausgelöst wurde, keine catch-Klausel verbun-den ist, prüft der Interpreter, ob eine mit dem nächsten umge-benden Codeblock verbunden ist. Das wird so lange fortgesetzt,bis ein Exception-Handler gefunden wurde. Wird eine Exception ineiner Funktion ausgelöst, die keine try/catch/finally-Anweisungzum Exception-Handling besitzt, wird die Exception an den Codeweitergegeben, der die Funktion aufgerufen hatte. Auf diese Weisedurchlaufen Exceptions die lexikalische Struktur von JavaScript-Methoden und den Aufrufstapel. Wird nirgendwo ein Exception-Handler gefunden, wird die Exception als Fehler behandelt unddem Benutzer gemeldet.

try/catch/finallyDie try/catch/finally-Anweisung ist der Exception-Handling-Me-chanismus von JavaScript. Die try-Klausel dieser Anweisung defi-niert einfach einen Codeblock, dessen Exceptions behandelt wer-den. Auf den try-Block folgt eine catch-Klausel, die einen Block mitAnweisungen enthält, die ausgeführt werden, wenn irgendwo imtry-Block Exceptions auftreten. Auf die catch-Klausel folgt einfinally-Block, der Aufräumcode enthält, dessen Ausführung garan-tiert ist – unabhängig davon, was im try-Block passiert. catch undfinally sind optional, aber einem try-Block muss mindestens einerdieser Blöcke folgen. try-, catch- und finally-Blöcke beginnen undenden mit geschweiften Klammern. Diese geschweiften Klammernsind ein erforderlicher Syntaxbestandteil und dürfen nicht wegge-

Sprünge | 71

Page 84: 3868993886_Script

lassen werden, selbst wenn eine Klausel nur eine einzige Anweisungenthält.

Der folgende Code illustriert Syntax und Zweck der try/catch/finally-Anweisung:

try {// Normalerweise lauft dieser Code ohne Probleme von// Anfang bis Ende durch. Aber gelegentlich kann er// eine Exception auslosen, entweder direkt mit einer// throw-Anweisung oder indirekt, weil eine Methode// aufgerufen wurde, die eine Exception auslost.

}catch (e) {

// Die Anweisungen in diesen Block werden nur dann// ausgefuhrt, wenn der try-Block eine Exception auslost.// Diese Anweisungen konnen die lokale Variable e nutzen,// um auf das Error-Objekt oder einen anderen ausgelosten// Wert zuzugreifen. Dieser Block kann die Exception// irgendwie verarbeiten, kann sie ignorieren, indem er// nichts tut, oder er kann sie mit throw erneut auslosen.

}finally {

// Dieser Block enthalt Anweisungen, die immer ausgefuhrt// werden, unabhangig davon, was im try-Block passiert.// Sie werden ausgefuhrt, wenn der try-Block// 1) normal endet, nachdem das Blockende erreicht wurde.// 2) durch eine break-, continue- oder return-Anweisung// verlassen wird.// 3) aufgrund einer Exception verlassen wird, die von// catch verarbeitet wird.// 4) aufgrund einer nicht abgefangenen Exception verlassen// wird, die noch aufsteigt.

}

Beachten Sie, dass auf das Schlüsselwort catch ein Bezeichner inKlammern folgt. Dieser Bezeichner ist wie ein Funktionsparameter.Wird eine Exception abgefangen, wird der mit der Exception ver-bundene Wert (beispielsweise ein Error-Objekt) diesem Parameterzugewiesen. Im Unterschied zu gewöhnlichen Variablen hat der miteiner catch-Klausel verbundene Bezeichner Blockgeltung – er ist nurinnerhalb des catch-Blocks definiert.

Hier ist ein realistisches Beispiel einer try/catch-Anweisung, das dieim letzten Abschnitt definierte Funktion factorial() und die client-

72 | Kapitel 4: Anweisungen

Page 85: 3868993886_Script

seitigen JavaScript-Methoden prompt() und alert() für die Eingabeund Ausgabe nutzt:

try {// Den Benutzer zur Eingabe einer Zahl auffordern.var n = Number(prompt("Geben Sie eine Zahl ein", ""));// Die Fakultat der Zahl berechnen und dabei davon// ausgehen, dass die Zahl gultig ist.var f = factorial(n);// Das Ergebnis anzeigen.alert(n + "! = " + f);

}catch (ex) { // War die Eingabe nicht gultig, landen

// wir hier.alert(ex); // Dem Benutzer mitteilen, was das

// Problem ist.}

Verschiedene AnweisungenDieser Abschnitt beschreibt die restlichen drei JavaScript-Anwei-sungen – with, debugger und use strict.

withWenn JavaScript den Wert einer Variabelen abfragt, sucht es zuerstnach einer in der aktuellen Funktion definierten Variable, danach(falls es sich um eine verschachtelte Funktion handelt) nach Varia-blen, die in umgebenden Funktionen definiert wurden, und schließ-lich nach globalen Variablen. Mit der with-Anweisung können Siedieses Verhalten zeitweilig verändern. Hierfür geben Sie an, welcheObjekteigenschaften als Variablen behandelt werden sollen. DieSyntax sieht wie folgt aus:

with (Objekt)Anweisung

In diesem Beispiel wird die Anweisung ausgeführt, als sei sie derKörper einer verschachtelten Funktion, der die Eigenschaften desObjekts als Parameter übergeben wurden.

Im Strict-Modus ist die with-Anweisung nicht erlaubt (siehe »usestrict«). Im normalen Modus sollte sie als veraltet betrachtet wer-

Verschiedene Anweisungen | 73

Page 86: 3868993886_Script

den: Vermeiden Sie sie, wenn irgend möglich. JavaScript-Code, derwith nutzt, ist schwer zu optimieren und läuft wahrscheinlich lang-samer als äquivalenter Code, der ohne with-Anweisung geschriebenwurde.

debuggerNormalerweise macht die debugger-Anweisung nichts. Aber wennein laufendes Debugger-Programm verfügbar ist, kann eine Imple-mentierung eine beliebige Debugging-Aktion durchführen (sie musses aber nicht tun). In der Praxis verhält sich diese Anweisung wie einBreakpoint: Die Ausführung des JavaScript-Codes wird angehalten,und Sie können den Debugger einsetzen, um die Werte von Varia-blen auszugeben, den Aufrufstapel zu untersuchen und so weiter.Nehmen Sie beispielsweise an, Sie erhalten in Ihrer Funktion f()eine Exception, weil sie mit einem undefinierten Argument aufgeru-fen wird, können aber nicht herausfinden, von wo die Funktionaufgerufen wird. Sie können sich das Debugging dieses Problemsdann erleichtern, indem Sie f() so ändern, dass sie folgendermaßenbeginnt:

function f(o) {if (o === undefined) debugger; // Vorubergehend zum Debugging

// eingefugt.// Hier folgt der Rest der Funktion.

}

Wird f() jetzt ohne Argument aufgerufen, wird die Ausführungangehalten. Sie können dann den Debugger einsetzen, um denAufrufstapel zu untersuchen und herauszufinden, wo der falscheAufruf herkommt.

Die debugger-Anweisung wurde der Sprache formell in ECMAScript5 hinzugefügt, war von den wichtigsten Browser-Herstellern aberschon seit geraumer Zeit implementiert.

»use strict«"use strict" ist eine Direktive, die in ECMAScript 5 eingeführtwurde. Direktiven sind keine Anweisungen (sind ihnen aber soähnlich, dass "use strict" hier beschrieben wird). "use strict"

74 | Kapitel 4: Anweisungen

Page 87: 3868993886_Script

enthält keine speziellen JavaScript-Schlüsselwörter: Es ist einfachein literaler JavaScript-String, der von ECMAScript 3-Interpreternschlicht ignoriert wird. Am Anfang eines Skripts oder im Körpereiner Funktion hat die Direktive für ECMAScript 5-Interpreterdagegen eine spezielle Bedeutung.

Die "use strict"-Direktive zeigt an, dass der nachfolgende Code (ineinem Skript oder einer Funktion) strenger Code ist. Strenger Codewird im Strict-Modus oder strengen Modus ausgeführt. Der Strict-Modus von ECMAScript 5 ist eine beschränkte Untermenge derSprache, die einige relevante Mängel der Sprache behebt und einestrengere Fehlerprüfung und eine erhöhte Sicherheit bietet. Zwi-schen dem Strict-Modus und dem normalen Modus gibt es diefolgenden Unterschiede:

• Die with-Anweisung ist im Strict-Modus nicht erlaubt.

• Im Strict-Modus müssen alle Variablen deklariert werden: Eswird ein ReferenceError ausgelöst, wenn Sie einem Bezeichnereinen Wert zuweisen, der keinen deklarierten Variablen, Pa-rametern oder Eigenschaften des globalen Objekts entspricht.(Im normalen Modus deklariert das implizit eine globale Va-riable, indem dem globalen Objekt eine neue Eigenschafthinzugefügt wird.)

• Im Strict-Modus hat this bei Funktionen, die als Funktionen(nicht als Methoden) aufgerufen wurden, den Wert undefined.(Im normalen Modus wird Funktionen, die als Funktionenaufgerufen werden, immer das globale Objekt als Wert vonthis übergeben.) Dieser Unterschied kann genutzt werden, umzu prüfen, ob eine Implementierung den Strict-Modus unter-stützt:

var hasStrictMode = (function() {"use strict";return this === undefined;

}());

• Im Strict-Modus wird bei Zuweisungen an nicht schreibbareEigenschaften und bei Versuchen, neue Eigenschaften aufnicht erweiterbaren Objekten zu erstellen, ein TypeError aus-gelöst. (Ohne Strict-Modus schlagen diese Versuche ohneweitere Folgen einfach fehl.) Wird im Strict-Modus versucht,

Verschiedene Anweisungen | 75

Page 88: 3868993886_Script

eine nicht konfigurierbare Eigenschaft oder einen Wert zulöschen, der zu keiner Eigenschaft gehört, wird ebenfalls einTypeError oder SyntaxError ausgelöst. (Ohne Strict-Modusschlägt der Versuch einfach fehl, und der delete-Ausdruckhat den Rückgabewert false.)

• Im Strict-Modus kann Code, der an eval() übergeben wird,keine Variablen oder Funktionen im Geltungsbereich des Auf-rufers deklarieren bzw. definieren wie im normalen Modus.Stattdessen leben Variablen- und Funktionsdefinitionen ineinem neuen Geltungsbereich, der für eval() erstellt wird.Dieser Geltungsbereich wird verworfen, wenn der eval()-Auf-ruf zurückkehrt.

• Im Strict-Modus sind keine oktalen Ganzzahlliterale erlaubt(das sind Literale, die mit einer 0 beginnen, auf die kein xfolgt). (Im normalen Modus erlauben einige Implementierun-gen oktale Literale.)

• Im Strict-Modus werden die Bezeichner eval und argumentswie Schlüsselwörter behandelt. Sie dürfen ihren Wert nichtändern.

76 | Kapitel 4: Anweisungen

Page 89: 3868993886_Script

KAPITEL 5

Objekte

JavaScripts fundamentaler Datentyp ist das Objekt. Ein Objekt istein zusammengesetzter Wert: Es fasst mehrere Werte (elementareWerte oder Objekte) zusammen und ermöglicht es, diese über einenNamen zu speichern und abzurufen. Ein Objekt ist eine ungeord-nete Sammlung von Eigenschaften, die jeweils einen Namen undeinen Wert haben. Eigenschaftsnamen sind Strings. Man kann alsosagen, dass Objekte Strings auf Werte abbilden. Diese String/Wert-Zuordnung ist unter den unterschiedlichsten Namen bekannt: Siekennen diese grundlegende Datenstruktur wahrscheinlich unterden Namen »Hash«, »Hashtable«, »Wörterbuch« oder »assoziativesArray«. Ein Objekt ist allerdings mehr als eine einfache String/Wert-Zuordnung. Ein JavaScript-Objekt kümmert sich nämlich nicht nurum seine Eigenschaften, es erbt auch die Eigenschaften eines ande-ren Objekts, das als sein »Prototyp« bezeichnet wird. Die Methodeneines Objekts sind üblicherweise geerbte Eigenschaften, und diese»Prototypvererbung« ist eine der wichtigsten Eigenschaften vonJavaScript.

JavaScript-Objekte sind dynamisch – Eigenschaften können norma-lerweise hinzugefügt und gelöscht werden –, können aber aucheingesetzt werden, um statische Objekte und die »Structs« statisch-typisierter Sprachen zu simulieren. Sie können auch genutzt werden(indem der Wert-Teil der String/Wert-Zuordnung ignoriert wird),um Mengen mit Strings zu repräsentieren.

Jeder Wert in JavaScript, der kein String, keine Zahl, nicht true,false, null oder undefined ist, ist ein Objekt.

| 77

Page 90: 3868993886_Script

Objekte sind veränderlich und werden über die Referenz statt überden Wert manipuliert: Verweist Variable x auf ein Objekt, hält dieVariable y, wenn der Code var y = x; ausgeführt wird, eine Referenzauf das gleiche Objekt, nicht auf eine Kopie. Alle Veränderungen,die über die Variable y am Objekt vorgenommen werden, sind auchüber die Variable x sichtbar.

Objekte erstellenObjekte können mit Objektliteralen, mit dem new-Schlüsselwortund (in ECMAScript 5) mit der Methode Object.create() erstelltwerden.

ObjektliteraleAm einfachsten erstellt man ein Objekt, indem man ein Objekt-literal in den JavaScript-Code einschließt. Ein Objektliteral ist einekommaseparierte Liste von Name/Wert-Paaren, die in geschweiftenKlammern stehen und durch Doppelpunkte voneinander getrenntsind. Ein Eigenschaftsname ist ein JavaScript-Bezeichner oder einStringliteral (der leere String ist erlaubt). Ein Eigenschaftswert ist einbeliebiger JavaScript-Ausdruck. Der Wert dieses Ausdrucks (daskann ein elementarer Wert oder ein Objektwert sein) wird zumWert der Eigenschaft. Hier sind einige Beispiele:

var empty = {}; // Ein Objekt ohne Eigenschaften.var point = { x:0, y:0 }; // Zwei Eigenschaften.var point2 = { // Komplexere Werte.

x:point.x,y:point.y+1

};var book = { // Eigenschaftsnamen konnen Leerzeichen,

"main title": "JavaScript", // und Bindestriche// enthalten. Nutzen Sie

'sub-title': "Pocket Ref", // dann Stringliterale."for": "all audiences", // for ist ein reserviertes

// Wort, deswegen auch hier// Anfuhrungszeichen.

};

78 | Kapitel 5: Objekte

Page 91: 3868993886_Script

Objekte mit new erstellenDer new-Operator erstellt und initialisiert ein neues Objekt. Auf dasSchlüsselwort new muss ein Funktionsaufruf folgen. Eine auf dieseWeise genutzte Funktion wird als Konstruktor bezeichnet undkümmert sich um die Initialisierung eines neu erstellten Objekts.Der Sprachkern von JavaScript bringt eingebaute Konstruktoren fürnative Typen mit. Zum Beispiel:

var o = new Object(); // Ein leeres Objekt: Entspricht {}.var a = new Array(); // Ein leeres Array: Entspricht [].var d = new Date(); // Ein Date-Objekt erstellen,

// das die aktuelle Zeit// reprasentiert.

var r = new RegExp("js"); // Ein RegExp-Objekt fur// Mustervergleiche erstellen.

Neben diesen eingebauten Konstruktoren definiert man üblicher-weise seine eigenen Konstruktorfunktionen zur Initialisierung neuerstellter Objekte. Wie man das macht, wird in Kapitel 8 behandelt.

PrototypenBevor wir uns der dritten Technik zur Erstellung von Objektenzuwenden können, müssen wir kurz innehalten, um Prototypen zuerklären. Mit jedem JavaScript-Objekt ist ein zweites JavaScript-Objekt (oder null, was allerdings selten ist) verknüpft. Dieseszweite Objekt wird als der Prototyp bezeichnet, und das erste Ob-jekt erbt Eigenschaften vom Prototyp.

Alle Objekte, die über Objektliterale erstellt wurden, haben dasgleiche Prototypobjekt, auf das wir in JavaScript-Code mit Ob-ject.prototype verweisen können. Objekte, die mit dem Schlüssel-wort new und einem Konstruktoraufruf erstellt wurden, nutzen denWert der prototype-Eigenschaft der Konstruktorfunktion als Pro-totyp. Ein mit new Object() erstelltes Objekt erbt also, genau wie einmit {} erstelltes, von Object.prototype. Analog nutzt ein mit newArray() erstelltes Objekt Array.prototype als Prototyp und ein mitnew Date() erstelltes Date.prototype.

Objekte erstellen | 79

Page 92: 3868993886_Script

Object.prototype ist eines der wenigen Objekte, das keinen Pro-totyp hat: Es erbt keinerlei Eigenschaften. Andere Prototypobjektesind gewöhnliche Objekte, die einen Prototyp haben. Alle einge-bauten Konstruktoren (und die meisten benutzerdefinierten Kons-truktoren) haben einen Prototyp, der von Object.prototype erbt.Beispielsweise erbt Date.prototype Eigenschaften von Object.pro-totype. Ein mit new Date() erstelltes Date-Objekt erbt also Eigen-schaften von Date.prototype und Object.prototype. Diese verbun-dene Folge von Prototypobjekten wird als eine Prototypkettebezeichnet.

Wie die Eigenschaftsvererbung funktioniert, erfahren Sie im Ab-schnitt »Vererbung von Eigenschaften« auf Seite 82. Wie man denPrototyp eines Objekts abfragt, werden wir im Abschnitt »Dasprototype-Attribut« auf Seite 93 betrachten. Und Kapitel 8 erläutertdie Verbindung zwischen Prototypen und Konstruktoren ausführ-licher: Dieses Kapitel zeigt Ihnen, wie Sie neue »Klassen« vonObjekten definieren, indem Sie eine Konstruktorfunktion schreibenund ihre prototype-Eigenschaft auf das Prototypobjekt setzen, dasvon den Instanzen genutzt wird, die mit diesem Konstruktor erstelltwerden.

Object.create()ECMAScript 5 definiert eine Funktion, Object.create(), die einneues Objekt erstellt und ihr erstes Argument als Prototyp diesesObjekts einsetzt. Object.create() akzeptiert außerdem ein optiona-les zweites Argument, das die Eigenschaften des neuen Objektsbeschreibt. Dieses zweite Argument werden wir uns im Abschnitt»Eigenschaftsattribute« auf Seite 90 ansehen.

Object.create() ist eine statische Funktion, keine Methode, die aufindividuellen Objekten aufgerufen wird. Wenn Sie sie nutzen wol-len, müssen Sie nur das gewünschte Prototypobjekt übergeben:

// o1 erbt die Eigenschaften x und y.var o1 = Object.create({x:1, y:2});

Sie können null übergeben, um ein Objekt zu erstellen, das keinenPrototyp hat. Aber wenn Sie das tun, erbt das neu erstellte Objektüberhaupt nichts, nicht einmal elementare Methoden wie toString()

80 | Kapitel 5: Objekte

Page 93: 3868993886_Script

(was auch heißt, dass es nicht mit dem +-Operator verwendet werdenkann):

// o2 erbt keine Eigenschaften oder Methoden.var o2 = Object.create(null);

Wenn Sie ein gewöhnliches leeres Objekt erstellen wollen (wie dieObjekte, die von {} oder new Object() geliefert werden), übergebenSie Object.prototype:

// o3 ist wie {} oder new Object().var o3 = Object.create(Object.prototype);

Die Möglichkeit, neue Objekte mit frei gewähltem Prototyp zu er-stellen (anders formuliert: die Möglichkeit, einen »Erben« für belie-bige Objekte zu erstellen), ist äußerst mächtig. In ECMAScript 3können wir sie mit einer Funktion wie in Beispiel 5-1 simulieren.

Beispiel 5-1: Ein neues Objekt erstellen, das von einem Prototyp erbt

// inherit() liefert ein neu erstelltes Objekt, das die// Eigenschaften des Prototypobjekts p erbt. Sie nutzt// die ECMAScript 5-Funktion Object.create(), wenn// diese definiert ist, und weicht andernfalls auf eine// altere Technik aus.function inherit(p) {

if (p == null) // p muss ein Objekt ungleich null sein.throw TypeError();

if (Object.create) // Ist Object.create() definiert, ...return Object.create(p); // diese Funktion nutzen.

var t = typeof p; // Andernfalls ein paar Typprufungen// durchfuhren.

if (t !== "object" && t !== "function")throw TypeError();

function f() {}; // Eine leere Konstruktorfunktion// definieren.

f.prototype = p; // Ihre prototype-Eigenschaft auf p// setzen.

return new f(); // f() nutzen, um einen "Erben" von p// zu erstellen.

}

Der Code in der Funktion inherit() wird verständlicher werden,wenn wir uns in Kapitel 8 Konstruktoren angesehen haben.

Objekte erstellen | 81

Page 94: 3868993886_Script

EigenschaftenDer wichtigste Teil eines Objekts sind seine Eigenschaften, die wirin diesem Abschnitt detailliert behandeln werden.

Eigenschaften abfragen und setzenDen Wert einer Eigenschaft erhalten Sie mit dem Punktoperator (.)oder dem »Eckige Klammern«-Operator ([]), die beide im Ab-schnitt »Auf Eigenschaften zugreifen« auf Seite 28 beschriebenwurden. Der linksseitige Operand dieser Operation sollte ein Aus-druck sein, der zu einem Objekt ausgewertet wird. Wenn Sie denPunktoperator nutzen, muss der rechtsseitige Operand ein Bezeich-ner sein, der die Eigenschaft angibt. Wenn Sie eckige Klammernnutzen, muss der Wert in der Klammer ein Ausdruck sein, der zueinem String ausgewertet wird, der den Namen der gewünschtenEigenschaft angibt:

// Die "author"-Eigenschaft von book abrufen.var author = book.author;// Die "surname"-Eigenschaft von author abrufen.var name = author.surname// Die "main title"-Eigenschaft von book abrufen.var title = book["main title"]

Sie setzen oder erstellen eine Eigenschaft, indem Sie wie bei derAbfrage den Punkt oder die eckigen Klammern nutzen, den Aus-druck mit dem entsprechenden Operator aber auf die linke Seiteeiner Zuweisung stellen:

// Eine "edition"-Eigenschaft auf book erstellen.book.edition = 6;// Die "main title"-Eigenschaft setzen.book["main title"] = "ECMAScript";

Vererbung von EigenschaftenJavaScript-Objekte haben einen Satz »eigener Eigenschaften« underben zusätzlich einen Satz von Eigenschaften von ihrem Prototyp-objekt. Wenn wir das verstehen wollen, müssen wir uns ausführ-licher mit dem Eigenschaftszugriff beschäftigen. Die Beispiele in

82 | Kapitel 5: Objekte

Page 95: 3868993886_Script

diesem Abschnitt nutzen die Funktion inherit() aus Beispiel 5-1,um Objekte mit bestimmten Prototypen zu erstellen.

Angenommen, Sie fragen die Eigenschaft x von Objekt o ab. Hat okeine eigene Eigenschaft dieses Namens, wird das Prototypobjektvon o nach der Eigenschaft x abgefragt. Wenn das Prototypobjektkeine Eigenschaft dieses Namens hat, aber selbst einen Prototypbesitzt, erfolgt die Abfrage auf dem Prototyp des Prototyps. Daswird fortgesetzt, bis die Eigenschaft x gefunden wird oder einObjekt mit dem Prototyp null durchsucht wird. Wie Sie sehenkönnen, erstellt das prototype-Attribut eines Objekts eine Ketteoder verkettete Liste, aus der Eigenschaften geerbt werden:

// o erbt Objektmethoden von Object.prototypevar o = {}o.x = 1; // und hat die eigene Eigenschaft x.// p erbt Eigenschaften von o und Object.prototypevar p = inherit(o);p.y = 2; // und hat die eigene Eigenschaft y.// q erbt Eigenschaften von p, o und Object.prototypevar q = inherit(p);q.z = 3; // und hat die eigene Eigenschaft z.// toString wird von Object.prototype geerbt.var s = q.toString();// x und y werden von o und p geerbt.q.x + q.y // => 3

Nehmen Sie jetzt an, Sie weisen der Eigenschaft x von Objekt oeinen Wert zu. Besitzt o bereits eine eigene (nicht geerbte) Eigen-schaft namens x, ändert die Zuweisung einfach den Wert dervorhandenen Eigenschaft. Andernfalls erstellt die Zuweisung eineneue Eigenschaft namens x auf dem Objekt o. Wenn o die Eigen-schaft x zuvor erbte, wird diese geerbte Eigenschaft jetzt von der neuerstellten eigenen Eigenschaft gleichen Namens verdeckt.

Eigenschaften löschenDer delete-Operator (siehe »Der delete-Operator« auf Seite 49)entfernt eine Eigenschaft aus einem Objekt. Sein Operand sollteein Eigenschaftszugriffsausdruck sein. Überraschenderweise ope-

Eigenschaften | 83

Page 96: 3868993886_Script

riert delete nicht auf dem Wert der Eigenschaft, sondern auf derEigenschaft selbst:

delete book.author; // Das book-Objekt hat jetzt// keine author-Eigenschaft mehr.

delete book["main title"]; // Und jetzt auch keinen// "main title" mehr.

Der delete-Operator löscht nur eigene Eigenschaften, keine geerb-ten. (Wenn Sie eine geerbte Eigenschaft löschen wollen, müssen Siesie aus dem Prototypobjekt löschen, auf dem sie definiert wurde.Dieser Vorgang wirkt sich dann auf alle Objekte aus, die von diesemPrototyp erben.)

Eigenschaften prüfenJavaScript-Objekte kann man als Eigenschaftsmengen betrachten,und es ist häufig hilfreich, wenn man das Enthaltensein in derMenge prüfen kann – d.h., wenn man prüfen kann, ob ein Objekteine Eigenschaft mit einem bestimmten Namen hat. Das können Siemit dem in-Operator und den Methoden hasOwnProperty() undpropertyIsEnumerable() prüfen oder indem Sie einfach die Eigen-schaft abfragen.

Der in-Operator erwartet auf der linken Seite einen Eigenschafts-namen (als String) und auf der rechten Seite ein Objekt. Er lieferttrue, wenn das Objekt eine eigene oder eine geerbte Eigenschaftdieses Namens hat:

var o = { x: 1 }"x" in o; // true: o hat eine eigene

// Eigenschaft namens "x"."y" in o; // false: o hat keine Eigenschaft namens "y"."toString" in o; // true: o erbt die Eigenschaft toString.

Die hasOwnProperty()-Methode eines Objekts prüft, ob das Objekteine eigene Eigenschaft mit dem angegebenen Namen hat. Beigeerbten Eigenschaften liefert sie false:

var o = { x: 1 }o.hasOwnProperty("x"); // true: o hat eine eigene

// Eigenschaft namens x.o.hasOwnProperty("y"); // false: o hat keine Eigenschaft

// namens y.

84 | Kapitel 5: Objekte

Page 97: 3868993886_Script

// toString ist eine geerbte Eigenschaft.o.hasOwnProperty("toString"); // false

Die propertyIsEnumerable()-Methode verfeinert die hasOwnProper-ty()-Prüfung. Sie liefert nur dann true, wenn die angegebeneEigenschaft eine eigene Eigenschaft ist und ihr enumerable-Attributauf true gesetzt ist. Bestimmte eingebaute Eigenschaften sind nichtenumerierbar. Eigenschaften, die von gewöhnlichem JavaScript-Code erstellt werden, sind enumerierbar, es sei denn, Sie habeneine der später vorgestellten ECMAScript 5-Methoden genutzt, umsie nicht enumerierbar zu machen:

var o = inherit({ y: 2 });o.x = 1;// true: o hat eine eigene enumerierbare Eigenschaft// namens x.o.propertyIsEnumerable("x"); // true// y ist eine geerbte, keine eigene Eigenschaft.o.propertyIsEnumerable("y"); // false// false: nicht enumerierbar.Object.prototype.propertyIsEnumerable("toString");

Häufig müssen Sie den in-Operator gar nicht nutzen, sondernkönnen die Eigenschaft einfach abfragen und !== einsetzen, um zuprüfen, dass der Eigenschaftszugriff keinen undefinierten Wert ge-liefert hat:

var o = { x: 1 }o.x !== undefined; // true: o hat eine Eigenschaft

// namens x.o.y !== undefined; // false: o hat keine Eigenschaft

// namens y.o.toString !== undefined; // true: o erbt eine toString-

// Eigenschaft.

Der in-Operator kann eine Sache, die die Technik des einfachenEigenschaftszugriffs nicht leisten kann. in kann zwischen Eigen-schaften unterscheiden, die nicht existieren, und Eigenschaften, dieexistieren, aber auf undefined gesetzt sind. Betrachten Sie denfolgenden Code:

var o = { x: undefined }o.x !== undefined // false: Eigenschaft existiert, ist

// aber undefined.o.y !== undefined // false: Eigenschaft existiert gar nicht.

Eigenschaften | 85

Page 98: 3868993886_Script

"x" in o // true: Eigenschaft existiert."y" in o // false: Eigenschaft existiert nicht.delete o.x; // Eigenschaft x loschen."x" in o // false: Eigenschaft existiert nicht mehr.

Eigenschaften enumerierenManchmal wollen wir nicht die Existenz einzelner Eigenschaftenprüfen, sondern alle Eigenschaften eines Objekts durchlaufen oderauflisten. Das macht man üblicherweise mit der for/in-Schleife,aber ECMAScript 5 führt zwei praktische Alternativen ein.

Die for/in-Schleife wurde im Abschnitt »for/in« auf Seite 65 be-trachtet. Sie führt den Schleifeninhalt einmal für jede enumerierbare(eigene oder geerbte) Eigenschaft des angegebenen Objekts aus undweist den Namen der Eigenschaft der Schleifenvariablen zu. Einge-baute Methoden, die Objekte erben, sind nicht enumerierbar, aberEigenschaften, die Ihr Code Objekten hinzufügt, sind enumerierbar(es sei denn, Sie nutzen eine der später vorgestellten Methoden, umsie nicht enumerierbar zu machen). Zum Beispiel:

// Drei enumerierbare eigene Eigenschaften.var o = {x:1, y:2, z:3};// nicht enumerierbar.o.propertyIsEnumerable("toString") // => false// Die Schleife gibt x, y und z aus, aber nicht toString.for(p in o) console.log(p);

Einige Hilfsbibliotheken fügen Object.prototype neue Methoden(und andere Eigenschaften) hinzu, damit diese von allen Objektengeerbt und genutzt werden können. Vor ECMAScript 5 gab esjedoch keine Möglichkeit, diese hinzugefügten Methoden nicht-enumerierbar zu machen. Sie werden von for/in-Schleifen alsoaufgeführt. Davor können Sie sich schützen, indem Sie die Eigen-schaften filtern, die von for/in geliefert werden. Hier sind zweiMöglichkeiten, wie Sie das erreichen können:

for(p in o) {if (!o.hasOwnProperty(p)) // Geerbte Eigenschaften

// uberspringen.continue;

}

86 | Kapitel 5: Objekte

Page 99: 3868993886_Script

for(p in o) {if (typeof o[p] === "function") // Methoden uberspringen.

continue;}

Hier ist eine kleine Hilfsfunktion, die eine for/in-Schleife verwen-det, um die Eigenschaften eines Objekts in ein anderes zu kopieren:

/** Kopiert die enumerierbaren Eigenschaften von p in o und lie-* fert o. Haben o und p eine Eigenschaft gleichen Namens, wird* die Eigenschaft von o uberschrieben.*/

function extend(o, p) {for(prop in p) { // Fur alle Eigenschaften in p

o[prop] = p[prop]; // die Eigenschaften o hinzufugen.}return o;

}

Neben der for/in-Schleife definiert ECMAScript 5 zwei Funktionenzur Enumeration von Eigenschaftsnamen. Die erste, Object.keys(),liefert ein Array mit den Namen der enumerierbaren eigenen Eigen-schaften eines Objekts. Die zweite ECMAScript 5-Funktion zurEigenschaftsenumeration ist Object.getOwnPropertyNames(). Siefunktioniert wie Object.keys(), liefert aber nur die Namen dereigenen Eigenschaften des angegebenen Objekts, nicht einfach dieNamen aller enumerierbaren Eigenschaften.

Eigenschaften und Objekte serialisierenDie Serialisierung eines Objekts ist ein Vorgang, bei dem derZustand eines Objekts in einen String umgewandelt wird, aus demes später wiederhergestellt werden kann. ECMAScript 5 bietet dienativen Funktionen JSON.stringify() und JSON.parse() zur Seria-lisierung und Wiederherstellung von JavaScript-Objekten. DieseFunktionen nutzen das Datenaustauschformat JSON (siehehttp://json.org). JSON steht für »JavaScript Object Notation« undhat große Ähnlichkeit mit JavaScript-Objekt- und -Array-Literalen:

o = {x:1, y:[false,null,""]}; // Ein Testobjekt definieren.s = JSON.stringify(o); // s ist

// '{"x":1,"y":{"z":[false,null,""]}}'.p = JSON.parse(s); // p ist eine tiefe Kopie von o.

Eigenschaften | 87

Page 100: 3868993886_Script

Die native Implementierung dieser Funktionen in ECMAScript 5wurde eng an die gemeinfreie ECMAScript 3-Implementierungunter http://json.org/json2.js angelehnt. In praktischer Hinsichtsind beide Implementierungen identisch. Mit dem Modul json2.jskönnen Sie diese ECMAScript 5-Funktionen also auch in ECMA-Script 3 nutzen.

Beachten Sie, dass die JSON-Syntax eine Untermenge der Java-Script-Syntax ist und nicht alle JavaScript-Werte darstellen kann.Objekte, Arrays, Strings, endliche Zahlen, true, false und nullwerden unterstützt und können serialisiert und wiederhergestelltwerden.

Eigenschafts-Getter und -SetterWie bereits gesagt, besitzt eine Eigenschaft immer einen Namenund einen Wert. In ECMAScript 5 (und in jüngeren ECMAScript3-Versionen wichtiger Browser außer dem IE) kann der Wert durcheine oder zwei Methoden ersetzt werden, die als Getter und Setterbezeichnet werden. Eigenschaften, die von Gettern und Setterndefiniert werden, werden gelegentlich als Zugriffseigenschaften be-zeichnet, um sie von Dateneigenschaften zu unterscheiden, die ein-fach einen Wert haben.

Wenn ein Programm den Wert einer Zugriffseigenschaft abfragt,ruft JavaScript die Getter-Methode auf (ohne ihr Argumente zuübergeben). Der Rückgabewert dieser Methode wird zum Wert desEigenschaftszugriffsausdrucks. Wenn ein Programm den Wert einerZugriffseigenschaft setzt, ruft JavaScript die Setter-Methode auf undübergibt dabei den Wert auf der rechten Seite der Zuweisung. DieseMethode ist dafür verantwortlich, dass der Eigenschaftswert »ir-gendwie« gesetzt wird. Der Rückgabewert der Setter-Methode wirdignoriert.

Am einfachsten definiert man Zugriffseigenschaften mit einer Er-weiterung der Objektliteralsyntax:

var o = {// Eine gewohnliche Dateneigenschaft.data_prop: value,

88 | Kapitel 5: Objekte

Page 101: 3868993886_Script

// Eine uber ein Funktionspaar definierte// Zugriffseigenschaft.get accessor_prop() { /* Liefert den Wert. */ },set accessor_prop(value) { /* Setzt den Wert. */ }

};

Zugriffseigenschaften werden mit einer oder zwei Funktionen de-finiert, deren Namen dem Namen der Eigenschaft entsprechen undbei deren Definition das Schlüsselwort function durch get und/oderset ersetzt wird. Beachten Sie, dass kein Doppelpunkt eingesetztwird, um den Namen der Eigenschaft von der Funktion zu trennen,die auf diese Eigenschaft zugreift, dass nach dem Funktionsinhaltaber weiterhin ein Komma erforderlich ist, um die Methode von dernächsten Methode oder Dateneigenschaft zu trennen. BetrachtenSie als Beispiel das folgende Objekt, das einen Punkt in einemzweidimensionalen kartesischen Koordinatensystem repräsentiert.Es hat gewöhnliche Dateneigenschaften, die die X- und Y-Koor-dinaten des Punktes repräsentieren, und Zugriffseigenschaften fürdie äquivalenten Polarkoordinaten des Punkts:

var p = {// x und y sind gewohnliche Dateneigenschaften.x: 1.0,y: 1.0,

// r ist eine les- und schreibbare Zugriffseigenschaft// mit Getter und Setter. Vergessen Sie das Komma// hinter den Zugriffsmethoden nicht.get r() {

return Math.sqrt(this.x*this.x + this.y*this.y);},set r(newvalue) {var oldvalue = Math.sqrt(this.x*this.x + this.y*this.y);var ratio = newvalue/oldvalue;this.x *= ratio;this.y *= ratio;

},

// theta ist eine schreibgeschutzte Zugriffseigenschaft,// die nur einen Getter hat.get theta() { return Math.atan2(this.y, this.x); }

};

Hier ein weiteres Beispiel für ein nützliches Objekt mit einer Zu-griffseigenschaft:

Eigenschaften | 89

Page 102: 3868993886_Script

// Dieses Objekt generiert streng wachsende Seriennummern.var serialnum = {

// Diese Dateneigenschaft enthalt die nachste// Seriennummer. Das $ im Eigenschaftsnamen weist// darauf hin, dass das eine private Eigenschaft ist.$n: 0,

// Den nachsten Wert liefern und die Dateneigenschaft// inkrementieren.get next() { return this.$n++; },

// Die Dateneigenschaft auf einen neuen Wert setzen,// wenn dieser großer als der aktuelle ist.set next(n) {

if (n >= this.$n) this.$n = n;else throw "Seriennummer muss auf einen \

großeren Wert gesetzt werden!";}

};

EigenschaftsattributeNeben einem Namen und einem Wert haben Eigenschaften Attri-bute, die festlegen, ob die Eigenschaft geschrieben, enumeriert undkonfiguriert werden kann. In ECMAScript 3 gibt es keine Möglich-keit, diese Attribute zu setzen: Alle Eigenschaften, die von ECMA-Script 3-Programmen erstellt werden, sind schreibbar, enumerier-bar und konfigurierbar, und es gibt keine Möglichkeit, das zuändern. Dieser Abschnitt erläutert die ECMAScript 5-API zumAbfragen und Setzen von Eigenschaftsattributen.

Für den Zweck dieses Abschnitts werden wir die Getter- und Setter-Methoden einer Zugriffseigenschaft als Eigenschaftsattribute be-trachten. Dieser Logik gemäß könnte man sogar sagen, dass derWert einer Dateneigenschaft ebenfalls ein Attribut ist. Man könntedann also sagen, dass eine Eigenschaft einen Namen und vier At-tribute hat. Die vier Attribute einer Dateneigenschaft sind value,writable, enumerable und configurable. Zugriffseigenschaften habendie Attribute value und writable: Ihre Schreibbarkeit wird durch dasVorhandensein oder Fehlen eines Setters bedingt. Die vier Attributeeiner Zugriffseigenschaft wären dann get, set, enumerable undconfigurable.

90 | Kapitel 5: Objekte

Page 103: 3868993886_Script

Die ECMAScript 5-Methoden zum Abfragen und Setzen der Attri-bute einer Eigenschaft nutzen sogenannte Eigenschaftsdeskriptorenzur Repräsentation der vier Attribute. Ein Eigenschaftsdeskriptor-objekt hat Eigenschaften mit den gleichen Namen wie die Attributeder Eigenschaft, die es beschreibt. Das Eigenschaftsdeskriptor-objekt einer Dateneigenschaft hat demnach also Eigenschaftennamens value, writable, enumerable und configurable. Der De-skriptor für eine Zugriffseigenschaft hat anstelle der Eigenschaftenvalue und writable die Eigenschaften get und set. Die Eigenschaf-ten writable, enumerable und configurable haben boolesche Werte,die Eigenschaften get und set haben natürlich Funktionswerte.

Den Eigenschaftsdeskriptor für eine bestimmte Eigenschaft einesObjekts erhalten Sie über Object.getOwnPropertyDescriptor():

// Liefert {value: 1, writable:true,// enumerable:true, configurable:true}Object.getOwnPropertyDescriptor({x:1}, "x");

// Jetzt die theta-Eigenschaft des oben definierten// p-Objekts abfragen.// Liefert { get: /*func*/, set:undefined,

enumerable:true, configurable:true}Object.getOwnPropertyDescriptor(p, "theta");

Wie der Name impliziert, funktioniert Object.getOwnPropertyDe-scriptor() nur bei eigenen Eigenschaften. Attribute geerbter Eigen-schaften können Sie nur abfragen, indem Sie explizit die Prototyp-kette (siehe Object.getPrototypeOf() in »Das prototype-Attribut«auf Seite 93) durchwandern.

Sie setzen die Attribute einer Eigenschaft oder erstellen eine neueEigenschaft mit den angegebenen Attributen, indem Sie Object.de-fineProperty() aufrufen und das zu verändernde Objekt, den Na-men der zu ändernden oder zu erstellenden Eigenschaft und einpassendes Eigenschaftsdeskriptorobjekt übergeben:

var o = {}; // Zu Anfang gibt es keine Eigenschaften.// Eine nicht enumerierbare Dateneigenschaft x mit dem// Wert 1 hinzufugen.Object.defineProperty(o, "x", { value : 1,

writable: true,enumerable: false,configurable: true});

Eigenschaften | 91

Page 104: 3868993886_Script

// Prufen, dass die Eigenschaft existiert, aber nicht// enumerierbar ist.o.x; // => 1Object.keys(o) // => []

// Die Eigenschaft x jetzt so andern, dass sie// schreibgeschutzt ist.Object.defineProperty(o, "x", { writable: false });

// Versuchen, den Wert der Eigenschaft zu andern.o.x = 2; // Schlagt schweigend fehl oder lost im

// Strict-Modus einen TypeError aus.o.x // => 1

// Die Eigenschaft ist immer noch konfigurierbar –// wir konnen ihren Wert also so andern:Object.defineProperty(o, "x", { value: 2 });o.x // => 2

// Jetzt aus der Dateneigenschaft x eine// Zugriffseigenschaft machen.Object.defineProperty(o, "x", {

get: function() { return 0; }});o.x // => 0

Der Eigenschaftsdeskriptor, den Sie an Object.defineProperty()übergeben, muss nicht alle vier Attribute enthalten. Wenn Sie eineneue Eigenschaft erstellen, wird für nicht angegebene Attributefalse oder undefined verwendet. Wenn Sie ein vorhandenes Attri-but ändern, werden die nicht angegebenen Attribute einfach nichtverändert. Beachten Sie, dass diese Methode eine vorhandene ei-gene Eigenschaft ändert oder eine neue eigene Eigenschaft erstellt,aber keine geerbten Eigenschaften verändert.

Wenn Sie gleichzeitig mehrere Eigenschaften erstellen oder ver-ändern wollen, nutzen Sie Object.defineProperties(). Das ersteArgument ist das Objekt, das Sie verändern wollen. Das zweiteArgument ist ein Objekt, das die Namen der zu erstellenden oderzu verändernden Eigenschaften den Eigenschaftsdeskriptoren fürdiese Eigenschaften zuordnet. Zum Beispiel:

var p = Object.defineProperties({}, {x: { value: 1, writable: true,

92 | Kapitel 5: Objekte

Page 105: 3868993886_Script

enumerable:true, configurable:true },y: { value: 1, writable: true,

enumerable:true, configurable:true },r: {

get: function() {return Math.sqrt(this.x*this.x+this.y*this.y)

},enumerable:true,configurable:true

}});

Im Abschnitt »Objekte erstellen« auf Seite 78 haben Sie die ECMA-Script 5-Methode Object.create() kennengelernt. Dort haben Sieerfahren, dass das erste Argument für diese Methode das Prototyp-objekt für das neu erstellte Objekt ist. Diese Methode akzeptiertdarüber hinaus ein optionales zweites Argument, das dem zweitenArgument für Object.defineProperties() entspricht. Wenn Sie anObject.create() einen Satz von Eigenschaftsdeskriptoren überge-ben, werden diese eingesetzt, um dem neu erstellten Objekt Eigen-schaften hinzuzufügen.

ObjektattributeMit jedem Objekt sind die Attribute prototype, class und extensibleverknüpft.

Das prototype-AttributDas prototype-Attribut eines Objekts gibt das Objekt an, von demdas Objekt seinen Prototyp erbt. Das prototype-Attribut wird ge-setzt, wenn ein Objekt erstellt wird. Im Abschnitt »Prototypen« aufSeite 79 wurde gesagt, dass Objekte, die auf Basis von Objekt-literalen erstellt werden, Object.prototype als Prototyp haben. Ob-jekte, die mit new erstellt werden, nutzen den Wert der prototype-Eigenschaft ihrer Konstruktorfunktion als Prototyp. Objekte, diemit Object.create() erstellt werden, nutzen das erste Argumentdieser Funktion (das null sein kann) als ihren Prototyp.

In ECMAScript 5 können Sie den Prototyp eines Objekts abfragen,indem Sie dieses Objekt an Object.getPrototypeOf() übergeben.

Objektattribute | 93

Page 106: 3868993886_Script

Eine äquivalente Funktion gibt es in ECMAScript 3 nicht, aber es istmanchmal möglich, den Prototyp eines Objekts o über den Aus-druck o.constructor.prototype zu erhalten.

Ob ein Objekt der Prototyp (oder ein Bestandteil der Prototypkette)eines anderen Objekts ist, ermitteln Sie mit der Methode isPro-totypeOf(). Ob p der Prototyp von o ist, ermitteln Sie also mitp.isPrototypeOf(o). Zum Beispiel:

var p = {x:1}; // Ein Prototypobjekt definieren.var o = Object.create(p); // Ein Objekt mit diesem Prototyp

// erstellen.p.isPrototypeOf(o) // => true: o erbt von p.Object.prototype.isPrototypeOf(p) // True fur jedes Objekt.

Beachten Sie, dass isPrototypeOf() eine ähnliche Funktion erfülltwie der instanceof-Operator.

Das class-AttributDas class-Attribut eines Objekts ist ein String, der Informationenzum Typ des Objekts angibt. Weder ECMAScript 3 noch ECMA-Script 5 bieten eine Möglichkeit, dieses Attribut zu setzen, und esgibt nur eine indirekte Technik, um es abzufragen. Die Standard-toString()-Methode (die von Object.prototype geerbt wird) lieferteinen String der Form:

[object Klasse]

Wenn Sie die Klasse eines Objekts erhalten wollen, können Sie alsodarauf die toString()-Methode aufrufen und aus dem zurückgelie-ferten String die Zeichen von der achten bis zur vorvorletzten Posi-tion herausziehen. Das Problematische dabei ist, dass die meistenObjekte andere, nützlichere toString()-Methoden erben. Die rich-tige Version von toString() können wir also nur indirekt mithilfeder Methode Function.call() (siehe den Abschnitt »IndirekterAufruf« auf Seite 124) aufrufen. Beispiel 5-2 definiert eine Funktion,die die Klasse jedes Objekts liefert, das Sie ihr übergeben.

Beispiel 5-2: Eine classof()-Funktion

function classof(o) {if (o === null) return "Null";

94 | Kapitel 5: Objekte

Page 107: 3868993886_Script

if (o === undefined) return "Undefined";return Object.prototype.toString.call(o).slice(8,-1);

}

Das extensible-AttributDas extensible-Attribut eines Objekts legt fest, ob dem Objekt neueEigenschaften hinzugefügt werden können oder nicht. ECMAScript5 definiert Funktionen zum Abfragen und Setzen der Erweiterbar-keit eines Objekts. Ob ein Objekt erweiterbar ist, ermitteln Sie,indem Sie es an Object.isExtensible() übergeben. Wenn Sie einObjekt nicht-erweiterbar machen wollen, können Sie das erreichen,indem Sie es an Object.preventExtensions() übergeben.

Object.seal() leistet das Gleiche wie Object.preventExtensions(),macht aber nicht nur das Objekt nicht erweiterbar, sondern zu-gleich auch alle eigenen Eigenschaften des Objekts nicht konfigu-rierbar. Das bedeutet, dass dem Objekt keine neuen Eigenschaftenhinzugefügt werden können und dass seine vorhandenen Eigen-schaften nicht gelöscht oder konfiguriert werden können. Mit Ob-ject.isSealed() können Sie ermitteln, ob ein Objekt versiegelt ist.

Object.freeze() sperrt ein Objekt noch rigoroser ab. Es macht dasObjekt nicht nur nicht-erweiterbar und seine Eigenschaften nicht-konfigurierbar, es macht auch alle Dateneigenschaften des Objektsschreibgeschützt. Nutzen Sie Object.isFrozen(), um zu prüfen, obein Objekt eingefroren wurde.

Bedenken Sie, dass es keine Möglichkeit gibt, um die Auswirkungenvon Object.preventExtensions(), Object.seal() und Object.freeze() wieder rückgängig zu machen. Zudem wirken sich dieseFunktionen nur auf das übergebene Objekt aus: Sie haben keinenEinfluss auf den Prototyp des Objekts. Schließlich sollten Sie daraufachten, dass diese drei Funktionen alle das übergebene Objektzurückgeben. Das bedeutet, sie können auch in verschachteltenFunktionsaufrufen verwendet werden:

// Ein versiegeltes Objekt mit eingefrorenem Prototyp// und einer nicht enumerierbaren Eigenschaft erstellen.o = Object.seal(Object.create(Object.freeze({x:1}),

{y: { value: 2,writable: true}}));

Objektattribute | 95

Page 108: 3868993886_Script
Page 109: 3868993886_Script

KAPITEL 6

Arrays

Ein Array ist eine geordnete Sammlung von Werten. Die einzelnenWerte bezeichnet man als Elemente. Jedes Element hat eine nume-rische Position im Array, die als Index bezeichnet wird. JavaScript-Arrays sind typlos: Ein Array-Element kann beliebigen Typs sein,und ein Array kann Elemente unterschiedlichen Typs enthalten.Array-Elemente können sogar Objekte oder andere Arrays sein. Dasermöglicht es Ihnen, komplexe Datenstrukturen wie Arrays mitObjekten oder Arrays mit Arrays zu erstellen. JavaScript-Arrayssind nullbasiert und nutzen 32-Bit-Indizes: Der Index des erstenElements ist 0, und der höchste mögliche Index ist 4294967294(232–2) – Arrays können also maximal 4.294.967.295 Elementehaben. JavaScript-Arrays sind dynamisch: Sie wachsen oderschrumpfen nach Bedarf, und es ist nicht erforderlich, bei derErstellung eine feste Größe für das Array vorzugeben oder das Arrayneu zu allozieren, wenn sich seine Größe ändert. Jedes JavaScript-Array besitzt die Eigenchaft length. Sie gibt die Anzahl der Elementeim Array an.

JavaScript-Arrays sind eine spezialisierte Form von JavaScript-Ob-jekten, und Array-Indizes sind wenig mehr als Eigenschaftsnamen,die Ganzzahlen sind. Implementierungen optimieren Arrays übli-cherweise so, dass der Zugriff auf numerisch indizierte Array-Ele-mente bedeutend schneller ist als der Zugriff auf gewöhnliche Ob-jekteigenschaften.

Arrays erben Eigenschaften von Array.prototype. Dieser Prototypdefiniert einen umfangreichen Satz von Methoden zur Manipula-tion von Arrays. Die meisten dieser Methoden sind generisch. Das

| 97

Page 110: 3868993886_Script

bedeutet, dass sie nicht nur bei echten Arrays korrekt funktionieren,sondern bei jedem »Array-artigen Objekt«. In ECMAScript 5 ver-halten sich Strings wie Arrays mit Zeichen.

Arrays erstellenAm einfachsten erstellt man ein Array über ein Array-Literal, alsoeine einfache kommaseparierte Liste mit Array-Elementen in ecki-gen Klammern:

var empty = []; // Ein Array ohne Elemente.var primes = [2, 3, 5, 7]; // Ein Array mit 5 numerischen

// Elementen.var misc = [{}, true, "a"]; // Elemente unterschiedlicher

// Typen

Die Werte in einem Array-Literal müssen keine Konstanten sein,sondern können beliebige Ausdrücke sein:

var base = 1024;var table = [base, base+1, base+2, base+3];

Array-Literale können Objektliterale oder andere Array-Literaleenthalten:

var b = [[1,{x:1, y:2}], [2, {x:3, y:4}]];

Enthält ein Array zwei aufeinander folgende Kommas, sagt man imEnglischen, das Array sei sparse (»spärlich«). Fehlende Elementeerhalten den Wert undefined:

var count = [1,,3]; // Elemente an den Indizes 0 und 2.count[1] // => undefinedvar undefs = [,,]; // Keine Elemente, trotzdem die Lange 2.

Da die Syntax für Array-Literale ein optionales nachstehendesKomma erlaubt, hat das Array [1,2,] eine Länge von 2, nicht von 3.

Arrays können außerdem mit dem Array()-Konstruktor erstelltwerden. Diesen Konstruktor kann man auf drei unterschiedlicheWeisen aufrufen:

• durch einen Aufruf ohne Argumente:var a = new Array();

98 | Kapitel 6: Arrays

Page 111: 3868993886_Script

Diese Form des Aufrufs erstellt ein leeres Array ohne Elementeund entspricht dem Array-Literal [].

• durch einen Aufruf mit einem numerischen Argument, das dieLänge angibt:

var a = new Array(10);

Diese Technik erstellt ein Array mit der angegebenen Länge.Diese Form des Array()-Konstruktors kann eingesetzt werden,um ein Array vorab zu reservieren, wenn Sie im Voraus wissen,wie viele Elemente erforderlich sein werden. Beachten Sie, dassim Array keine Werte gespeichert werden und die Array-Index-eigenschaften »0«, »1« usw. für das Array nicht einmal defi-niert sind.

• durch die explizite Angabe von zwei oder mehr beliebigenElementen oder einem nicht numerischen Element für dasArray:

var a = new Array(5, 4, 3, 2, 1, "Test");

In dieser Form werden die Konstruktorargumente zu Elemen-ten des neuen Arrays. Es ist fast immer einfacher, statt dieserForm des Array()-Konstruktors ein Array-Literal zu nutzen.

Array-Elemente und -LängeAuf die Elemente eines Arrays greifen Sie über den []-Operator zu.Links von den eckigen Klammern sollte eine Referenz auf ein Arrayerscheinen, und zwischen ihnen sollte ein beliebiger Ausdruckstehen, dessen Wert eine nicht-negative Ganzzahl ist (oder in einesolche umgewandelt werden kann). Diese Syntax können Sie glei-chermaßen zum Lesen wie zum Schreiben des Wertes eines Array-Elements einsetzen. Die folgenden Anweisungen sind also zulässig:

var a = ["Welt"]; // Mit einem Array mit einem Element// beginnen.

var value = a[0]; // Element 0 lesen.a[1] = 3.14; // Element 1 schreiben.i = 2;a[i] = 3; // Element 2 schreiben.a[i + 1] = "Hallo"; // Element 3 schreiben.a[a[i]] = a[0]; // 0 und 2 lesen, 3 schreiben.

Array-Elemente und -Länge | 99

Page 112: 3868993886_Script

Erinnern Sie sich daran, dass Arrays eine spezialisierte Art vonObjekten sind. Die eckigen Klammern, die genutzt werden, um aufdie Array-Elemente zuzugreifen, verhalten sich genau so wie dieeckigen Klammern, die genutzt werden, um auf Objekteigenschaftenzuzugreifen. JavaScript wandelt die von Ihnen angegebenen numeri-schen Array-Indizes in einen String um – die Zahl 1 wird zum String"1" – und nutzt diesen String dann als Eigenschaftsnamen.

Jedes Array hat eine length-Eigenschaft. Diese Eigenschaft ist es, dieArrays von gewöhnlichen JavaScript-Objekten unterscheidet. DieEigenschaft length gibt die Anzahl der im Array enthaltenen Ele-mente an (wobei davon ausgegangen wird, dass keine Elementefehlen). Der Wert von length ist um eins höher als der höchste imArray verwendete Index:

[].length // => 0: Das Array hat keine Elemente.['a','b','c'].length // => 3: Der hochste Index ist 2,

// die Lange ist 3.

Das Besondere bei Arrays ist, dass sie, wenn Sie als Eigenschafts-namen nicht-negative Ganzzahlen kleiner als 232 nutzen (oder denNamen entsprechend konvertieren), automatisch für Sie den Wertder Eigenschaft length verwalten.

Die Eigenschaft length ist schreibbar, das heißt, wenn Sie sie aufeine nicht-negative ganze Zahl n setzen, die kleiner als der aktuelleWert ist, werden alle Array-Elemente, deren Index größer odergleich n ist, aus dem Array gelöscht:

a=[1,2,3,4,5]; // Zunachst haben wir ein Array mit// 5 Elementen.

a.length = 3; // a ist jetzt [1,2,3].a.length = 0; // Alle Elemente loschen. a ist [].a.length = 5; // Die Lange ist 5, aber es gibt keine

// Elemente - wie new Array(5).

Sie können die length-Eigenschaft eines Arrays auch auf einen Wertsetzen, der größer als der aktuelle Wert ist. Das fügt dem Arraykeine neuen Elemente hinzu, sondern erstellt einfach einen spärli-chen Bereich am Ende des Arrays.

100 | Kapitel 6: Arrays

Page 113: 3868993886_Script

Arrays durchlaufenDas üblichste Mittel, um die Elemente in einem Array zu durch-laufen, ist die for-Schleife (siehe den Abschnitt »for« auf Seite 63):

var keys = Object.keys(o); // Ein Array mit den// Eigenschaftsnamen fur ein Objekt o

abrufen.var values = [] // Die entsprechenden

// Eigenschaftswerte in diesem Arrayspeichern.for(var i = 0; i < keys.length; i++) { // Fur jeden Index

// im Array:var key = keys[i]; // * den Namen an

// diesem Index abrufenvalues[i] = o[key]; // * den Wert der

// Eigenschaft im values-Arrayspeichern.}

Bei geschachtelten Schleifen oder einem anderen Kontext, in demdie Leistung relevant ist, werden Sie gelegentlich darauf stoßen,dass die einfache Schleife zum Durchlaufen eines Arrays so opti-miert wurde, dass die Array-Länge nur einmal nachgeschlagen wirdund nicht bei jedem Durchlauf:

for(var i = 0, len = keys.length; i < len; i++) {// Der Schleifeninhalt bleibt gleich.

}

ECMAScript 5 definiert eine Reihe von neuen Methoden zumDurchlaufen der Elemente eines Arrays, indem sie nacheinander aneine von Ihnen definierte Funktion übergeben werden. Die for-Each()-Methode ist die allgemeinste dieser Methoden:

var data = [1,2,3,4,5]; // Das sind die Daten, die wir// durchlaufen wollen.

var sumOfSquares = 0; // Diese Variable bei jeder// Iteration aktualisieren.

data.forEach(function(x) { // Jedes Element an die// Funktion ubergeben.

sumOfSquares += x*x; // Die Quadrate// zusammenzahlen.

});sumOfSquares // =>55: 1+4+9+16+25

Arrays durchlaufen | 101

Page 114: 3868993886_Script

Mehrdimensionale ArraysJavaScript unterstützt keine echten mehrdimensionalen Arrays,aber sie können mit Arrays nachgebaut werden, die selbst wiederArrays enthalten. Wollen Sie auf einen Wert in einem Array mitArrays zugreifen, nutzen Sie einfach den []-Operator doppelt. Neh-men Sie beispielsweise an, die Variable matrix wäre ein Array mitArrays mit Zahlen. Jedes Element matrix[x] ist ein Array mitZahlen. Auf eine bestimmte Zahl in diesem Array greifen Sie dannfolgendermaßen zu: matrix[x][y]. Hier ist ein konkretes Beispiel,das ein zweidimensionales Array als Multiplikationstabelle nutzt:

// Ein mehrdimensionales Array erstellen.var table = new Array(10); // Die 10 Zeilen der Tabelle.for(var i = 0; i < table.length; i++)

table[i] = new Array(10); // Jede Zeile hat 10 Felder.

// Das Array initialisieren.for(var row = 0; row < table.length; row++) {

for(col = 0; col < table[row].length; col++) {table[row][col] = row*col;

}}

// Das mehrdimensionale Array nutzen, um 5*7 zu berechnen.var product = table[5][7]; // 35

Array-MethodenArrays besitzen eine Reihe nützlicher Methoden, die wir in denfolgenden Abschnitten behandeln wollen.

join()Die Methode Array.join() wandelt alle Elemente eines Arrays inStrings um, verkettet diese und liefert den resultierenden String. Siekönnen einen optionalen String angeben, der im Ergebnisstring alsTrennzeichen zwischen den Elementen eingefügt wird. Wird keinTrennstring angegeben, wird ein Komma genutzt:

var a = [1, 2, 3];a.join(); // => "1,2,3"

102 | Kapitel 6: Arrays

Page 115: 3868993886_Script

a.join(" "); // => "1 2 3"a.join(""); // => "123"var b = new Array(5); // Ein Array der Lange 5 ohne

// Elemente.b.join('-') // => '----': Ein String mit 4

// Minuszeichen.

Die Methode Array.join() ist das Gegenstück zur MethodeString.split(), die ein Array erstellt, indem sie einen String in Teilezerlegt.

reverse()Die Methode Array.reverse() kehrt die Reihenfolge der Elementeim Array um und liefert das umgekehrte Array zurück. Die Umkeh-rung erfolgt vor Ort. Anders formuliert: Es wird kein neues Arraymit umgestellten Elementen erzeugt, sondern die Elemente werdenim vorhandenen Array umgestellt:

var a = [1,2,3];a.reverse().join() // => "3,2,1"a[0] // => 3: a ist jetzt [3,2,1].

sort()Array.sort() sortiert die Elemente eines Arrays vor Ort und liefertdas sortierte Array zurück. Wird sort() ohne Argumente aufgeru-fen, sortiert es die Array-Elemente in alphabetischer Reihenfolge:

var a = new Array("Banane", "Kirsche", "Apfel");a.sort();var s = a.join(", "); // s == "Apfel, Banane, Kirsche"

Wenn ein Array undefinierte Elemente enthält, werden diese ansEnde des Arrays sortiert.

Soll ein Array in anderer Reihenfolge als in alphabetischer Reihen-folge sortiert werden, müssen Sie sort() eine Vergleichsfunktion alsArgument übergeben. Diese Funktion entscheidet, welches ihrerbeiden Argumente im sortierten Array zuerst erscheinen soll. Solldas erste Argument vor dem zweiten erscheinen, muss die Ver-gleichsfunktion eine Zahl kleiner null liefern. Soll das erste Argu-ment nach dem zweiten erscheinen, muss die Vergleichsfunktion

Array-Methoden | 103

Page 116: 3868993886_Script

eine Zahl größer null liefern. Sind die beiden Werte äquivalent (d.h.,ist ihre Reihenfolge irrelevant), muss die Vergleichsfunktion 0 lie-fern. Beispielsweise würden Sie Folgendes tun, um Array-Elementein numerischer statt in alphabetischer Reihenfolge zu sortieren:

var a = [33, 4, 1111, 222];a.sort(); // Alphabetisch: 1111, 222, 33, 4a.sort(function(a,b) { // Numerisch: 4, 33, 222, 1111

return a-b; // Liefert < 0, 0 oder > 0});

a.sort(function(a,b) {return b-a}); // Umgekehrte// numerische Reihenfolge.

Eine alphabetische Sortierung ohne Berücksichtigung von Groß-/Kleinschreibung könnte so aussehen:

a = ['ameise', 'Bar', 'katze']a.sort(); // Mit Groß-/Kleinschreibung:

// ['ameise', 'Bar', 'katze']a.sort(function(s,t) { // Sortierung ohne Berucksichtigung

// von Groß-/Kleinschreibung.var a = s.toLowerCase();var b = t.toLowerCase();if (a < b) return -1;if (a > b) return 1;return 0;

}); // => ['ameise', 'Bar', 'katze']

concat()Die Methode Array.concat() erstellt und liefert ein neues Array mitallen Elementen aus dem Array, auf dem concat() aufgerufen wur-de, gefolgt von allen Argumenten für concat(). Ist eines dieser Ar-gumente selbst ein Array, werden die Elemente dieses Arrays an-gehängt, nicht das Array selbst. concat() verändert das Array, aufdem es aufgerufen wurde, nicht. Hier sind einige Beispiele:

var a = [1,2,3];a.concat(4, 5) // Liefert [1,2,3,4,5]a.concat([4,5]); // Liefert [1,2,3,4,5]a.concat([4,5],[6,7]) // Liefert [1,2,3,4,5,6,7]a.concat(4, [5,[6,7]]) // Liefert [1,2,3,4,5,[6,7]]

104 | Kapitel 6: Arrays

Page 117: 3868993886_Script

slice()Die Methode Array.slice() liefert einen Ausschnitt oder ein Teil-Array des angegebenen Arrays. Ihre beiden Argumente geben denAnfang und das Ende des zu liefernden Ausschnitts an. Das zurück-gelieferte Array enthält das vom ersten Argument angegebene Ele-ment und alle nachfolgenden Elemente bis zu dem Element, abernicht einschließlich des Elements, das durch das zweite Argumentangegeben wird. Wird nur ein Argument angegeben, enthält daszurückgelieferte Element alle Elemente von der Anfangsposition biszum Ende des Arrays. Wenn eines der Argumente negativ ist, gibt esdas Array-Element in Bezug auf das letzte Element an. Beachten Sie,dass slice() das Array, auf dem es aufgerufen wurde, nicht ver-ändert:

var a = [1,2,3,4,5];a.slice(0,3); // Liefert [1,2,3]a.slice(3); // Liefert [4,5]a.slice(1,-1); // Liefert [2,3,4]a.slice(-3,-2); // Liefert [3]

splice()Die Methode Array.splice() ist eine Allzweckmethode, mit derElemente in ein Array eingefügt oder aus einem Array entferntwerden können. Im Unterschied zu slice() und concat() verändertsplice() das Array, auf dem es aufgerufen wurde.

Das erste Argument für splice() gibt die Position im Array an, ander das Einfügen und/oder Löschen beginnen soll. Das zweiteArgument gibt die Anzahl an Elementen an, die aus dem Arraygelöscht (herausgeschnitten) werden sollen. Wird das zweite Argu-ment weggelassen, werden alle Elemente vom Startelement bis zumEnde des Arrays entfernt. splice() liefert ein Array mit den gelösch-ten Elementen oder ein leeres Array, falls keine Elemente gelöschtwurden. Zum Beispiel:

var a = [1,2,3,4,5,6,7,8];a.splice(4); // Liefert [5,6,7,8]; a ist [1,2,3,4]a.splice(1,2); // Liefert [2,3]; a ist [1,4]a.splice(1,1); // Liefert [4]; a ist [1]

Array-Methoden | 105

Page 118: 3868993886_Script

Die ersten beiden Argumente für splice() geben an, wie vieleArray-Elemente gelöscht werden sollen. Auf diese Argumente kön-nen beliebig viele weitere Argumente folgen, die Elemente angeben,die beginnend bei der Position, die durch das erste Argument an-gegeben wurde, in das Array eingefügt werden sollen. Zum Beispiel:

var a = [1,2,3,4,5];a.splice(2,0,'a','b'); // =>[]; a ist [1,2,'a','b',3,4,5]a.splice(2,2,3); // =>['a','b']; a ist [1,2,3,3,4,5]

Beachten Sie, dass splice() anders als concat() Arrays als Arrayseinfügt, nicht die Elemente dieser Arrays.

push() and pop()Die Methoden push() und pop() ermöglichen es Ihnen, mit Arraysso zu arbeiten, als wären es Stacks (Stapel). Die Methode push()hängt ein oder mehrere neue Elemente ans Ende des Arrays an undliefert die neue Länge des Arrays. Die Methode pop() macht dasGegenteil: Sie entfernt das letzte Element aus dem Array, verringertdie Array-Länge um eins und liefert den Wert des Elements, das sieentfernt hat. Beachten Sie, dass beide Methoden das Array vor Ortverändern, anstatt eine veränderte Kopie des Arrays zu erstellen:

var stack = []; // Stapel: []stack.push(1,2); // Stapel: [1,2] Liefert 2stack.pop(); // Stapel: [1] Liefert 2stack.push(3); // Stapel: [1,3] Liefert 2stack.pop(); // Stapel: [1] Liefert 3stack.push([4,5]); // Stapel: [1,[4,5]] Liefert 2stack.pop() // Stapel: [1] Liefert [4,5]stack.pop(); // Stapel: [] Liefert 1

unshift() und shift()Die Methoden unshift() und shift() verhalten sich ähnlich wiepush() und pop(), ergänzen bzw. löschen Elemente aber am Anfangund nicht am Ende des Arrays. unshift() fügt am Anfang des Arraysein oder mehrere Elemente ein, verschiebt die vorhandenen Array-Elemente zu höheren Indizes, um Platz zu schaffen, und liefert dieneue Länge des Arrays zurück. shift() entfernt und liefert das ersteElement des Arrays und verschiebt alle nachfolgenden Elemente um

106 | Kapitel 6: Arrays

Page 119: 3868993886_Script

einen Platz nach unten, um den leeren Platz am Anfang des Arrayszu füllen:

var a = []; // a:[]a.unshift(1); // a:[1] Liefert: 1a.unshift(22); // a:[22,1] Liefert: 2a.shift(); // a:[1] Liefert: 22a.unshift(3,[4,5]); // a:[3,[4,5],1] Liefert: 3a.shift(); // a:[[4,5],1] Liefert: 3a.shift(); // a:[1] Liefert: [4,5]a.shift(); // a:[] Liefert: 1

toString()Arrays haben wie alle JavaScript-Objekte eine toString()-Methode.Bei einem Array wandelt diese Methode alle Elemente in einenString um (indem bei Bedarf auf ihnen die Methode toString()aufgerufen wird) und gibt sie in einer kommaseparierten Liste aus.Beachten Sie, dass die Ausgabe keine eckigen Klammern oder eineandere Art Begrenzer um den Wert enthält. Zum Beispiel:

[1,2,3].toString() // => '1,2,3'["a", "b", "c"].toString() // => 'a,b,c'[1, [2,'c']].toString() // => '1,2,c'

ECMAScript 5-Array-MethodenECMAScript 5 definiert neue Array-Methoden zum Durchlaufen,Abbilden, Filtern, Prüfen, Reduzieren und Durchsuchen von Ar-rays. Die meisten dieser Methoden erwarten eine Funktion als erstesArgument und rufen diese Funktion einmal für alle (oder wenigs-tens einige) Elemente des Arrays auf. In den meisten Fällen werdendie von Ihnen übergebenen Funktionen mit drei Argumenten auf-gerufen: dem Wert des Array-Elements, dem Index des Array-Ele-ments und dem Array selbst. Häufig benötigen Sie nur das erstedieser Argumente und können die Werte für das zweite und dritteignorieren. Die meisten ECMAScript 5-Array-Methoden, die eineFunktion als erstes Argument erwarten, akzeptieren ein optionaleszweites Argument. Wird dieses angegeben, wird die angegebeneFunktion aufgerufen, als wäre sie eine Methode des zweiten Argu-ments. Das heißt, das zweite Argument, das Sie übergeben, wird in

ECMAScript 5-Array-Methoden | 107

Page 120: 3868993886_Script

der übergebenen Funktion zum Wert des this-Schlüsselworts. DerRückgabewert der von Ihnen angegebenen Funktion ist wichtig,aber die unterschiedlichen Methoden behandeln Rückgabewerteauf unterschiedliche Weise. Keine der ECMAScript 5-Array-Metho-den modifiziert das Array, auf dem sie aufgerufen wird. Die an dieArray-Methode übergebene Funktion kann das Array dagegenselbstverständlich verändern.

forEach()Die forEach()-Methode durchläuft das Array und ruft für jedesElement die von Ihnen angegebene Funktion auf:

var data = [1,2,3,4,5]; // Die Summe aller Elemente des// Arrays berechnen.

var sum = 0; // Mit 0 anfangen.data.forEach(function(value) { sum += value; });sum // => 15

// Jetzt jedes Array-Element inkrementieren.data.forEach(function(v, i, a) { a[i] = v + 1; });data // => [2,3,4,5,6]

map()Die map()-Methode übergibt alle Elemente des Arrays, auf dem sieaufgerufen wird, an die Funktion, die Sie angeben, und erzeugt da-raus ein neues Array, das die von der Funktion zurückgegebenenWerte enthält:

a = [1, 2, 3];b = a.map(function(x) { return x*x; }); // b ist [1, 4, 9]

filter()Die filter()-Methode liefert ein Array mit einer Untermenge derElemente des Arrays, auf der sie aufgerufen wird. Die Funktion, dieSie ihr übergeben, sollte ein Prädikat sein: eine Funktion, die trueoder false liefert. Das Prädikat wird genau so aufgerufen wie beiforEach() und map(). Wenn der Rückgabewert true ist oder einWert, der in true umgewandelt wird, ist das an das Prädikat überge-

108 | Kapitel 6: Arrays

Page 121: 3868993886_Script

bene Element ein Mitglied der Teilmenge und wird dem Arrayhinzugefügt, das zum Rückgabewert wird:

a = [5, 4, 3, 2, 1];a.filter(function(x) { return x < 3 }); // => [2,1]a.filter(function(x,i) { return i%2==0 }); // => [5,3,1]

every() und some()Die Methoden every() und some() sind Array-Prädikate: Sie wen-den die von Ihnen übergebene Prädikatfunktion auf die Elementedes Arrays an und liefern dann true oder false.

Die every()-Methode ist wie der mathematische Allquantor 8: Erliefert true, wenn Ihre Prädikatfunktion für alle Elemente des Arraystrue liefert:

a = [1,2,3,4,5];// Alle Werte < 10?a.every(function(x) { return x < 10; }) // => true// Sind alle Werte gerade?a.every(function(x) { return x%2 === 0; }) // => false

Die Methode some() ist wie der mathematische Existenzquantor 9:Sie liefert true, wenn es im Array mindestens ein Element gibt, fürdas das Prädikat true liefert. false liefert sie nur, wenn das Prädikatfür alle Elemente des Arrays false liefert:

a = [1,2,3,4,5];// Hat a einige gerade Zahlen?a.some(function(x) { return x%2===0; }) // => true// Enthalt a Nicht-Zahl-Werte?a.some(isNaN) // => false

Beachten Sie, dass every() und some() das Durchlaufen der Array-Elemente abbrechen, sobald sie wissen, welcher Wert zurückgelie-fert werden muss. Beachten Sie außerdem, dass every() bei einemAufruf auf einem leeren Array true liefert; some liefert hierbei false.

reduce(), reduceRight()Die Methoden reduce() und reduceRight() kombinieren die Ele-mente eines Arrays und nutzen die von Ihnen angegebene Funktion,um einen einzigen Wert hervorzubringen. Das ist eine Operation,

ECMAScript 5-Array-Methoden | 109

Page 122: 3868993886_Script

die in der funktionalen Programmierung sehr häufig anzutreffen istund auch als »Injizierung« oder »Faltung« bezeichnet wird. Bei-spiele werden uns helfen, ihre Funktionsweise zu illustrieren:

var a = [1,2,3,4,5]// Summe der Werte.a.reduce(function(x,y) { return x+y }, 0); // => 15// Produkt der Werte.a.reduce(function(x,y) { return x*y }, 1); // => 120// Großter Wert.a.reduce(function(x,y) { return (x>y)?x:y; }); // => 5

reduce() erwartet zwei Argumente. Das erste ist die Funktion, diedie Reduktionsoperation ausführt. Die Aufgabe dieser Reduktions-funktion ist es, irgendwie zwei Werte auf einen zu reduzieren unddiesen reduzierten Wert zurückzuliefern. In den Beispielen obenkombinieren die Funktionen zwei Werte, indem sie sie addieren,multiplizieren und unter ihnen den größeren auswählen. Das zweite(optionale) Argument ist ein Initialisierungswert, der der Funktionübergeben wird.

Funktionen, die mit reduce() verwendet werden, sind anders alsFunktionen, die mit forEach() und map() verwendet werden. Dievertrauten Wert-, Index- und Array-Werte werden als zweites,drittes und viertes Argument übergeben. Das erste Argument istdas bislang akkumulierte Ergebnis der Reduktion. Beim erstenAufruf der Funktion ist dieses Argument der Startwert, den Sie alszweites Argument an reduce() übergeben haben. Bei nachfolgendenAufrufen ist es der Wert, den der letzte Aufruf der Funktion zurück-geliefert hat. Beim ersten Beispiel oben wird die Reduktionsfunk-tion zuerst mit den Argumenten 0 und 1 aufgerufen. Sie addiertdiese und liefert 1. Dann wird sie erneut mit den Argumenten 1 und2 aufgerufen und liefert 3. Beim nächsten Mal berechnet sie 3+3=6,dann 6+4=10 und schließlich 10+5=15. Dieser letzte Wert, 15, wirdzum Rückgabewert von reduce().

Vielleicht ist Ihnen aufgefallen, dass der dritte Aufruf von reduce()oben nur ein Argument hat: Es wird kein Anfangswert angegeben.Wenn Sie reduce() auf diese Weise ohne einen Anfangswert auf-rufen, wird das erste Element des Arrays als Anfangswert verwen-det. Das heißt, dass der erste Aufruf der Reduktionsfunktion daserste und zweite Element des Arrays als erstes und zweites Argu-

110 | Kapitel 6: Arrays

Page 123: 3868993886_Script

ment erhält. Bei den Summen- und Produktbeispielen oben hättenwir das Argument für den Anfangswert ebenfalls weglassen können.

reduceRight() funktioniert genau wie reduce(), verarbeitet dasArray aber vom höchsten zum niedrigsten Index (von rechts nachlinks), statt vom niedrigsten zum höchsten.

indexOf() und lastIndexOf()indexOf() und lastIndexOf() durchsuchen ein Array nach einemElement mit dem angegebenen Wert und liefern den Index desersten gefundenen derartigen Elements oder -1, wenn kein Vor-kommen gefunden wurde. indexOf() durchsucht das Array vonAnfang bis Ende, lastIndexOf() vom Ende zum Anfang:

a = [0,1,2,1,0];a.indexOf(1) // => 1: a[1] ist 1a.lastIndexOf(1) // => 3: a[3] ist 1a.indexOf(3) // => -1: Kein Element hat den Wert 3.

Im Unterschied zu den anderen in diesem Abschnitt beschriebenenMethoden erwarten indexOf() und lastIndexOf() kein Funktions-argument. Das erste Argument ist der zu suchende Wert. Das zweiteArgument ist optional: Es gibt den Array-Index an, bei dem dieSuche begonnen werden soll. Wird dieses Argument nicht angege-ben, beginnt indexOf() am Anfang des Arrays, lastIndexOf() anseinem Ende. Für das zweite Argument sind negative Werte erlaubt;sie werden als Abstand vom Array-Ende behandelt.

Der Array-TypIm gesamten Kapitel haben wir gesehen, dass Arrays Objekte mitein paar speziellen Verhalten sind. Wenn man ein unbekanntesObjekt erhält, ist es häufig hilfreich, wenn man irgendwie ermittelnkann, ob es sich um ein Array handelt oder nicht. In ECMAScript 5können Sie das mit der Funktion Array.isArray() tun:

Array.isArray([]) // => trueArray.isArray({}) // => false

Der Array-Typ | 111

Page 124: 3868993886_Script

Wir können eine isArray()-Funktion schreiben, die in allen Java-Script-Versionen wie folgt arbeitet:

var isArray = Array.isArray || function(o) {var ts = Object.prototype.toString;return typeof o === "object" &&ts.call(o) === "[object Array]";

};

Array-artige ObjekteWie wir gesehen haben, sind Arrays Objekte, die über eine length-Eigenschaft und spezielles Verhalten verfügen. Ein »Array-artiges«Objekt ist also ein einfaches JavaScript-Objekt mit numerischenEigenschaftsnamen und einer length-Eigenschaft. Derartige »Array-artige« Objekte tauchen in der Praxis tatsächlich gelegentlich auf,und obgleich Sie auf ihnen die Array-Methoden nicht direkt auf-rufen können und das spezielle Verhalten der length-Eigenschaftnicht von ihnen erwarten können, können Sie sie doch mit demgleichen Code durchlaufen wie ein echtes Array. Es stellt sichheraus, dass viele Array-Algorithmen mit Array-artigen Objektenebenso gut funktionieren wie mit echten Arrays:

// Ein Array-artiges Objekt.var a = {"0":"a", "1":"b", "2":"c", length:3};// Daruber iterieren, als sei es ein "echtes" Arrayvar total = 0;for(var i = 0; i < a.length; i++)

total += a[i];

Viele Array-Algorithmen funktionieren mit Array-artigen Objektengenauso gut wie mit echten Arrays. Die JavaScript-Array-Methodenwurden mit Absicht allgemein gehalten, damit sie korrekt funk-tionieren, wenn sie auf Array-artige Objekte angewandt werden. DaArray-artige Objekte nicht von Array.prototype erben, können Siedie Array-Methoden nicht direkt auf ihnen aufrufen. Sie können siejedoch indirekt über die Methode Function.call aufrufen (sieheden Abschnitt »Indirekter Aufruf« auf Seite 124):

// Ein Array-artiges Objekt.var a = {"0":"a", "1":"b", "2":"c", length:3};Array.prototype.join.call(a, "+") // => "a+b+c"Array.prototype.map.call(a, function(x) {

112 | Kapitel 6: Arrays

Page 125: 3868993886_Script

return x.toUpperCase();}) // => ["A","B","C"]// ein echtes Array als Kopie.Array.prototype.slice.call(a, 0) // => ["a","b","c"]

Einige Browser definieren allgemeine Array-Funktionen direkt amArray-Konstruktor. Bei entsprechender Browserunterstützung kön-nen die oben stehenden Beispiele so umgeschrieben werden:

var a = {"0":"a", "1":"b", "2":"c", length:3};Array.join(a, "+")Array.slice(a, 0)Array.map(a, function(x) { return x.toUpperCase(); })

Strings als ArraysIn ECMAScript 5 (und vielen jüngeren Browser-Implementierungenvor ECMAScript 5 – der IE8 eingeschlossen) verhalten sich Stringswie schreibgeschützte Arrays. Für den Zugriff auf einzelne Zeichenkönnen Sie statt der charAt()-Methode also eckige Klammern nut-zen:

var s = test;s.charAt(0) // => "t"s[1] // => "e"

Der typeof-Operator liefert bei Strings natürlich weiterhin »string«,und die Array.isArray()-Methode liefert weiterhin false, wenn Sieihr einen String übergeben.

Der größte Vorteil indexierbarer Strings ist einfach, dass wir charAt()durch eckige Klammern ersetzen können, die kompakter und les-barer sind. Der Umstand, dass sich Strings wie Arrays verhalten,bedeutet aber auch, dass wir auf sie die generischen Array-Methodenanwenden können. Zum Beispiel:

s = "Java"Array.prototype.join.call(s, " ") // => "J a v a"Array.prototype.filter.call(s, function(x) {

return x.match(/[^aeiou]/); // Findet nur Zeichen,// die keine Vokale sind.

}).join("") // => "Jv"

Strings als Arrays | 113

Page 126: 3868993886_Script
Page 127: 3868993886_Script

KAPITEL 7

Funktionen

Eine Funktion ist ein Block mit JavaScript-Code, der einmal defi-niert wird, aber beliebig oft ausgeführt oder aufgerufen werdenkann. Das hinter Funktionen stehende Konzept ist Ihnen vielleichtbereits unter Namen wie Subroutine oder Prozedur bekannt. Java-Script-Funktionen sind parametrisiert: Eine Funktionsdefinitionkann eine Liste mit Bezeichnern einschließen, die als Parameterbezeichnet werden und als lokale Variablen für den Codeinhalt derFunktion dienen. Funktionsaufrufe geben Werte oder Argumentefür die Parameter einer Funktion an. Funktionen nutzen häufig dieWerte ihrer Argumente, um einen Rückgabewert zu berechnen, derzum Wert des Funktionsaufrufausdrucks wird. Neben den Argu-menten hat jeder Aufruf einen weiteren Wert – den Aufrufkontext –,das ist der Wert des this-Schlüsselworts.

Wird eine Funktion einer Eigenschaft eines Objekts zugewiesen,bezeichnet man diese als eine Methode dieses Objekts. Wird eineFunktion auf einem oder über ein Objekt aufgerufen, ist diesesObjekt der Aufrufkontext oder this-Wert für die Funktion. Funk-tionen, die zur Initialisierung neuer Objekte gedacht sind, bezeich-net man als Konstruktoren. Konstruktoren wurden im Abschnitt»Objekte erstellen« auf Seite 78 beschrieben und werden uns erneutin Kapitel 8 beschäftigen.

In JavaScript sind Funktionen Objekte und können von Program-men manipuliert werden. JavaScript kann Funktionen beispiels-weise Variablen zuweisen oder an andere Funktionen übergeben.Da Funktionen Objekte sind, können Sie auf ihnen Eigenschaftensetzen und sogar Funktionen aufrufen.

| 115

Page 128: 3868993886_Script

JavaScript-Funktionsdefinitionen können in andere Funktioneneingebettet werden und haben Zugriff auf alle Variablen, die beiihrer Definition in Geltung waren. Das bedeutet, dass JavaScript-Funktionen Closures sind, und ermöglicht wichtige und mächtigeProgrammiertechniken.

Funktionen definierenFunktionen werden mit dem Schlüsselwort function definiert, dasin Funktionsdefinitionsausdrücken (siehe den Abschnitt »Funk-tionsdefinition« auf Seite 29) oder in Funktionsdeklarationsanwei-sungen (siehe den Abschnitt »function« auf Seite 56) verwendetwerden kann. In beiden Formen beginnt eine Funktionsdefinitionmit dem Schlüsselwort function, gefolgt von folgenden Komponen-ten:

• einem Bezeichner, der der Funktion einen Namen gibt. DerName ist ein erforderlicher Bestandteil von Funktionsdeklara-tionsanweisungen: Er wird als Name einer Variablen verwen-det, und das neu definierte Funktionsobjekt wird dieser Varia-blen zugewiesen. Bei einem Funktionsdefinitionsausdruck istder Name optional: Wenn er vorhanden ist, verweist er nurinnerhalb des Funktionsinhalts selbst auf das Funktions-objekt.

• einem Klammernpaar um eine kommaseparierte Liste mit nulloder mehr Bezeichnern. Diese Bezeichner sind die Parameter-namen für die Funktion und verhalten sich im Inhalt derFunktion wie lokale Variablen.

• einem Paar geschweifter Klammern, das keine oder mehrereJavaScript-Anweisungen enthält. Diese Anweisungen sind derInhalt oder Body der Funktion: Sie werden ausgeführt, wenndie Funktion aufgerufen wird.

Beispiel 7-1 zeigt einige Funktionsdefinitionen in Ausdrucks- undAnweisungsform. Beachten Sie, dass eine Funktion, die als Aus-druck definiert wurde, nur in einem umfangreicheren Ausdruck wieeiner Zuweisung oder einem Aufruf Sinn macht, der etwas mit derneu definierten Funktion unternimmt.

116 | Kapitel 7: Funktionen

Page 129: 3868993886_Script

Beispiel 7-1: JavaScript-Funktionen definieren

// Den Namen und den Wert jeder Eigenschaft von o ausgeben.// undefined liefern.function printprops(o) {

for(var p in o)console.log(p + ": " + o[p] + "\n");

}

// Den Abstand zwischen zwei Koordinaten (x1,y1) und (x2,y2)// berechnen.function distance(x1, y1, x2, y2) {

var dx = x2 - x1;var dy = y2 - y1;return Math.sqrt(dx*dx + dy*dy);

}

// Eine rekursive Funktion (eine Funktion, die sich selbst// aufruft), die die Fakultat berechnet. Erinnern Sie sich, dass x!// das Produkt von x und aller positiven Ganzzahlen// kleiner x ist.function factorial(x) {

if (x <= 1) return 1;return x * factorial(x-1);

}

// Dieser Funktionsausdruck definiert eine Funktion, die ihr// Argument quadriert.// Beachten Sie, dass wir ihn einer Variablen zuweisen.var square = function(x) { return x*x; }

// Funktionsausdrucke konnen Namen enthalten,// was fur Rekursionen nutzlich ist.var f = function fact(x) {

if (x <= 1) return 1;else return x*fact(x-1);

};

// Funktionsausdrucke konnen auch als Argumente// fur andere Funktionen eingesetzt werden:data.sort(function(a,b) { return a-b; });

// Funktionsausdrucke werden gelegentlich// unmittelbar bei der Definition aufgerufen:var tensquared = (function(x) {return x*x;}(10));

Funktionen definieren | 117

Page 130: 3868993886_Script

Beachten Sie, dass der Funktionsname bei allen Funktionen, die alsAusdruck definiert werden, optional ist. Eine Funktionsdeklarati-onsanweisung deklariert eigentlich eine Variable und weist ihr einFunktionsobjekt zu. Ein Funktionsdefinitionsausdruck deklarierthingegen keine Variable. Aber bei Funktionen, die wie die Fakul-tätsfunktion oben auf sich selbst verweisen müssen, ist ein Nameerlaubt. Wenn ein Funktionsdefinitionsausdruck einen Namen ent-hält, enthält der lokale Funktionsgeltungsbereich dieser Funktioneine Bindung dieses Namens an das Funktionsobjekt. Die Folge ist,dass der Funktionsname in der Funktion zu einer lokalen Variablenwird. Die meisten Funktionen, die als Ausdrücke definiert werden,benötigen keine Namen – ihre Definition bleibt so kompakter.Funktionsdefinitionsausdrücke sind besonders gut für Funktionengeeignet, die wie die Funktionen der letzten beiden Beispiele obennur ein einziges Mal verwendet werden.

Wie im Abschnitt »function« auf Seite 56 beschrieben, werdenFunktionsdeklarationsanweisungen an den Anfang des umschlie-ßenden Skripts oder der umschließenden Funktion »gehoben«,sodass Funktionen, die auf diese Weise deklariert werden, ausCode aufgerufen werden können, der vor der eigentlichen Defini-tion steht. Das gilt jedoch nicht für Funktionen, die als Ausdrückedefiniert werden: Wenn Sie eine Funktion aufrufen wollen, müssenSie auf sie verweisen können. Auf Funktionen, die als Ausdrückedefiniert werden, können Sie aber erst zugreifen, nachdem sie einerVariablen zugewiesen wurden. Funktionen, die mit Ausdrückendefiniert werden, dürfen also nicht vor ihrer Definition aufgerufenwerden.

Beachten Sie, dass die meisten, aber nicht alle Funktionen in Bei-spiel 7-1 eine return-Anweisung (siehe den Abschnitt »return« aufSeite 69) enthalten. Die return-Anweisung veranlasst, dass dieAusführung der Funktion beendet und der Rückgabewert ihresAusdrucks (wenn es einen gibt) an den Aufrufer zurückgegebenwird. Wenn mit der return-Anweisung kein Ausdruck verbundenist, liefert sie den Wert undefined. Enthält eine Funktion keinereturn-Anweisung, werden einfach alle Anweisungen in ihrem In-halt ausgeführt, bevor – nach Abarbeitung der letzten Anweisung –der Wert undefined an den Aufrufer geliefert wird.

118 | Kapitel 7: Funktionen

Page 131: 3868993886_Script

Geschachtelte FunktionenIn JavaScript können Funktionen in andere Funktionen geschach-telt werden. Zum Beispiel:

function hypotenuse(a, b) {function square(x) { return x*x; }return Math.sqrt(square(a) + square(b));

}

Das Interessante an geschachtelten Funktionen sind die für siegeltenden Regeln der Variablengeltung: Sie können auf die Para-meter und Variablen der Funktion (oder Funktionen) zugreifen, indie sie eingebettet sind. Im Code oben kann die innere Funktionsquare() beispielsweise die Parameter a und b lesen und schreiben,die von der äußeren Funktion hypotenuse() definiert werden. DieseGeltungsbereichsregeln für geschachtelte Funktionen sind wichtig,und wir werden uns im Abschnitt »Closures« auf Seite 130 erneutmit ihnen befassen.

Wie im Abschnitt »function« auf Seite 56 gesagt wurde, sind Funk-tionsdeklarationsanweisungen keine echten Anweisungen und wer-den, wie von der ECMAScript-Spezifikation verlangt, nur auf derobersten Codeebene erlaubt. Sie dürfen nur in globalem Code oder inanderen Funktionen erscheinen, nicht in Schleifen, Bedingungs-anweisungen, try/catch/finally-Konstrukten oder with-Anweisun-gen. Beachten Sie, dass es diese Einschränkung nur für Funktionengibt, die als Anweisungen deklariert werden. Funktionsdefinitions-ausdrücke können an beliebiger Stelle in Ihrem JavaScript-Code er-scheinen.

Funktionen aufrufenDer JavaScript-Code, der den Inhalt einer Funktion bildet, wirdnicht ausgeführt, wenn die Funktion definiert wird, sondern wennsie aufgerufen wird. JavaScript-Funktionen können auf vier Weisenaufgerufen werden:

• als Funktionen

• als Methoden

Funktionen aufrufen | 119

Page 132: 3868993886_Script

• als Konstruktoren

• indirekt über ihre call()- und apply()-Methoden

FunktionsaufrufAls Funktionen oder Methoden werden Funktionen mit einem Auf-rufausdruck aufgerufen (siehe den Abschnitt »Aufruf« auf Seite 29).Ein Aufrufausdruck besteht aus einem Funktionsausdruck, der zueinem Funktionsobjekt ausgewertet wird, einer öffnenden Klam-mer, einer kommaseparierten Liste mit null oder mehr Argument-ausdrücken und einer schließenden Klammer. Wenn der Funk-tionsausdruck ein Eigenschaftszugriffsausdruck ist – d.h., wenn dieFunktion eine Eigenschaft eines Objekts oder ein Element einesArrays ist –, handelt es sich um einen Methodenaufrufausdruck.Mit diesem Fall werden wir uns unten befassen. Der folgende Codeenthält einige Funktionsaufrufausdrücke:

printprops({x:1});var total = distance(0,0,2,1) + distance(2,1,3,5);var probability = factorial(5)/factorial(13);

Die einzelnen Argumentausdrücke (die zwischen den Klammern) ineinem Funktionsaufruf werden ausgewertet, und die resultierendenWerte werden zu den Argumenten der Funktion. Diese Werte wer-den den Parametern zugewiesen, die in der Funktionsdefinitionangegeben wurden. Im Inhalt der Funktion wird eine Referenz aufeinen Parameter zum entsprechenden Argumentwert ausgewertet.

Bei einem gewöhnlichen Funktionsaufruf wird der Rückgabewertder Funktion zum Wert des Aufrufausdrucks. Wenn die Funktionzurückkehrt, weil der Interpreter ihr Ende erreicht, ist der Rück-gabewert undefined. Wenn die Funktion zurückkehrt, weil derInterpreter eine return-Anweisung ausführt, ist der Rückgabewertder Wert des Ausdrucks, der auf return folgt, oder undefined, wenndie return-Anweisung keinen Wert enthält.

Bei Funktionsaufrufen in ECMAScript 3 und im normalen Modusvon ECMAScript 5 ist das globale Objekt der Aufrufkontext (derthis-Wert). Im Strict-Modus ist der Aufrufkontext hingegen unde-fined.

120 | Kapitel 7: Funktionen

Page 133: 3868993886_Script

Funktionen, die so geschrieben wurden, dass sie als Funktionenaufgerufen werden, nutzen das Schlüsselwort this üblicherweisenicht. Es kann jedoch genutzt werden, um zu prüfen, ob der Strict-Modus aktiviert ist:

// Eine Funktion definieren und aufrufen,// um zu ermitteln, ob wir uns im Strict-Modus befinden.var strict = (function() { return !this; }());

MethodenaufrufEine Methode ist bloß eine JavaScript-Funktion, die in einer Objekt-eigenschaft gespeichert ist. Wenn Sie eine Funktion f und einObjekt o haben, können Sie eine Methode m von o mit der folgendenZeile definieren:

o.m = f;

Nachdem auf dem Objekt o die Methode m() definiert wurde,können Sie diese folgendermaßen aufrufen:

o.m();

Und wenn m() zwei Argumente erwartet, sieht der Aufruf folgen-dermaßen aus:

o.m(x, y);

Der Code oben ist ein Aufrufausdruck: Er schließt den Funktions-ausdruck o.m und die zwei Argumentausdrücke x und y ein. DerFunktionsausdruck selbst ist ein Eigenschaftszugriffsausdruck (sie-he den Abschnitt »Auf Eigenschaften zugreifen« auf Seite 28), unddas bedeutet, dass die Funktion als Methode und nicht als gewöhn-liche Funktion aufgerufen wird.

Die Argumente und Rückgabewerte von Methodenaufrufen werdengenau so behandelt, wie es oben für einen gewöhnlichen Funktions-aufruf beschrieben wurde. Methodenaufrufe unterscheiden sichjedoch in einer wichtigen Hinsicht von Funktionsaufrufen: in Bezugauf den Aufrufkontext. Eigenschaftszugriffsausdrücke bestehen auszwei Teilen: einem Objekt (hier o) und einem Eigenschaftsnamen(m). Bei einem Methodenaufrufausdruck wie diesem wird das Ob-

Funktionen aufrufen | 121

Page 134: 3868993886_Script

jekt o zum Aufrufkontext, auf den der Inhalt der Funktion mit demSchlüsselwort this verweisen kann. Hier ist ein konkretes Beispiel:

var calculator = { // Ein Objektliteral.operand1: 1,operand2: 1,add: function() {

// Das this-Schlusselwort verweist auf dieses// Objekt.this.result = this.operand1 + this.operand2;

}};calculator.add(); // Ein Methodenaufruf, der 1+1 berechnet.calculator.result // => 2

Die meisten Methodenaufrufe nutzen für den Eigenschaftszugriffdie Punktnotation, aber auch Eigenschaftszugriffsausdrücke, dieeckige Klammern nutzen, veranlassen einen Methodenaufruf. Bei-spielsweise stellen beide nachfolgenden Ausdrücke Methodenauf-rufe dar:

o["m"](x,y); // Eine andere Moglichkeit, o.m(x,y) zu// formulieren.

a = [function(x) { return x+1 }];a[0](z) // Auch ein Methodenaufruf.

Methodenaufrufe können auch bei komplexeren Eigenschafts-zugriffsausdrücken beteiligt sein:

// Eine Methode auf customer.surname aufrufen.customer.surname.toUpperCase();// Die Methode m() auf dem Ruckgabewert von f() aufrufen.f().m();

Beachten Sie, dass this ein Schlüsselwort, aber keine Variable undkein Eigenschaftsname ist. Die JavaScript-Syntax erlaubt es nicht,this einen Wert zuzuweisen.

Im Gegensatz zu Variablen hat das Schlüsselwort this keinenGeltungsbereich. Verschachtelte Funktionen erben this auch nichtvon der umgebenden Funktion. Wird eine geschachtelte Funktionals Methode aufgerufen, ist ihr this-Wert das Objekt, auf dem sieaufgerufen wurde. Wird eine geschachtelte Funktion als Funktionaufgerufen, ist ihr this-Wert entweder das globale Objekt (im

122 | Kapitel 7: Funktionen

Page 135: 3868993886_Script

normalen Modus) oder undefined (im Strict-Modus). Häufig wirdfälschlich angenommen, dass eine geschachtelte Funktion, die alsFunktion aufgerufen wird, this nutzen kann, um den Aufrufkon-text der äußeren Funktion zu erhalten. Wenn Sie Zugriff auf denthis-Wert der äußeren Funktion brauchen, müssen Sie diesen Wertin einer Variablen speichern, die für die innere Funktion gilt. Zudiesem Zweck nutzt man häufig eine Variable mit dem Namen self.Zum Beispiel:

var o = { // Ein Objekt o.m: function() { // Die Methode m des Objekts.

var self = this; // Den this-Wert in einer// Variablen speichern.

console.log(this === o); // Gibt "true" aus.f(); // Jetzt die Hilfsfunktion f()

// aufrufen.

function f() {console.log(this === o); // Gibt "false" aus.console.log(self === o); // Gibt "true" aus.

}}

};o.m(); // Die Methode m auf dem Objekt o aufrufen.

KonstruktoraufrufSteht vor einem Funktions- oder Methodenaufruf das Schlüsselwortnew, dann ist es ein Konstruktoraufruf. (Konstruktoraufrufe wurdenim Abschnitt »Initialisierer« auf Seite 26 und im Abschnitt »Objektemit new erstellen« auf Seite 79 eingeführt, und Konstruktorenwerden in Kapitel 8 ausführlicher behandelt.) Konstruktoraufrufeunterscheiden sich von gewöhnlichen Funktions- oder Methoden-aufrufen in ihrer Behandlung der Argumente, des Aufrufkontextsund des Rückgabewerts.

Wenn ein Konstruktoraufruf eine Argumentliste in Klammern ein-schließt, werden diese Argumentausdrücke auf die gleiche Weiseausgewertet und der Funktion übergeben wie bei einem Funktions-oder Methodenaufruf. Aber wenn ein Konstruktor keinen Parame-ter hat, erlaubt die JavaScript-Syntax für Konstruktoraufrufe, dieArgumentliste und die Klammern vollständig wegzulassen. Bei

Funktionen aufrufen | 123

Page 136: 3868993886_Script

einem Konstruktoraufruf können Sie ein leeres Klammernpaar im-mer weglassen. Die beiden folgenden Zeilen sind beispielsweiseäquivalent:

var o = new Object();var o = new Object;

Ein Konstruktoraufruf erstellt ein neues, leeres Objekt, das von derprototype-Eigenschaft des Konstruktors erbt. Konstruktorfunktio-nen dienen zur Initialisierung von Objekten, und das neu erstellteObjekt wird als Aufrufkontext verwendet. Die Konstruktorfunktionkann darauf also mit dem Schlüsselwort this verweisen. BeachtenSie, dass das neue Objekt auch dann verwendet wird, wenn derKonstruktoraufruf wie ein Methodenaufruf aussieht. Das heißt,dass o im Ausdruck new o.m() nicht als Aufrufkontext verwendetwird.

Konstruktorfunktionen nutzen üblicherweise keine return-Anwei-sung. Normalerweise initialisieren sie das neue Objekt und kehrendann implizit zurück, wenn das Ende des Inhalts erreicht wird. Indiesem Fall ist das neue Objekt der Wert des Konstruktoraufruf-ausdrucks. Wenn ein Konstruktor hingegen explizit die return-An-weisung nutzt, um ein Objekt zu liefern, wird dieses Objekt zumWert des Aufrufausdrucks. Nutzt der Konstruktor return ohneWert oder mit einem elementaren Wert, wird dieser Rückgabewertignoriert und das neue Objekt als Wert des Aufrufs verwendet.

Indirekter AufrufJavaScript-Funktionen sind Objekte und haben wie alle JavaScript-Objekte Methoden. Zwei dieser Methoden, call() und apply(),rufen die Funktion indirekt auf. Das erste Argument für call() undapply() ist das Objekt, auf dem die Funktion aufgerufen werdensoll. Dieses Argument stellt den Aufrufkontext und wird im Inhaltder Funktion zum Wert des Schlüsselworts this. Eine Funktion f()rufen Sie (ohne Argumente) mit call() oder apply() folgenderma-ßen als Methode des Objekts o auf:

f.call(o);f.apply(o);

124 | Kapitel 7: Funktionen

Page 137: 3868993886_Script

Beide Codezeilen entsprechen dem folgenden Code (der voraus-setzt, dass o noch keine Eigenschaft namens m hat):

o.m = f; // f vorubergehend zu einer Methode// von o machen.

o.m(); // Ohne Argumente aufrufen.delete o.m; // Die temporare Methode loschen.

In Strict-Modus von ECMAScript 5 wird das erste Argument voncall() und apply() zum Wert von this – auch wenn es einelementarer Wert, null oder undefined ist. In ECMAScript 3 undim normalen Modus werden Werte, die null oder undefined sind,durch das globale Objekt ersetzt und elementare Werte durch dasentsprechende Wrapper-Objekt.

Alle Argumente, die auf das erste Argument mit dem Aufrufkontextfolgen, werden bei call() an die Funktion übergeben, die aufgeru-fen wird. Wenn Sie die Funktion f() als eine Methode des Objekts oaufrufen und ihr dabei zwei Zahlen übergeben wollen, könnten SieCode wie den folgenden nutzen:

f.call(o, 1, 2);

Die Methode apply() ist wie call(), aber die Argumente, die derFunktion übergeben werden, werden in einem Array angegeben:

f.apply(o, [1,2]);

Wenn eine Funktion so definiert ist, dass sie eine beliebige Anzahlan Argumenten erwartet, können Sie die Funktion mit apply() aufdem Inhalt eines Arrays beliebiger Länge aufrufen. Beispielsweisekönnten Sie, wenn Sie die größte Zahl in einem Array mit Zahlenfinden wollen, apply() nutzen, um die Elemente des Arrays an dieFunktion Math.max() zu übergeben:

var biggest = Math.max.apply(Math, array_of_numbers);

Beachten Sie, dass apply() neben echten Arrays auch Array-artigeObjekt akzeptiert. Insbesondere können Sie eine Funktion mit dengleichen Argumenten wie die aktuelle Funktion aufrufen, indem Siedas arguments-Array (siehe den Abschnitt »Argumentlisten variablerLänge:Das Arguments-Objekt« auf Seite 127) direkt an apply() übergeben.Der folgende Code zeigt dies:

Funktionen aufrufen | 125

Page 138: 3868993886_Script

// Auf dem Objekt o die Methode m durch eine Version// ersetzen, die vor und nach Aufruf der ursprunglichen// Methode Nachrichten ausgibt.function trace(o, m) {

var original = o[m]; // Die ursprungliche Methode// festhalten.

o[m] = function() { // Jetzt die neue Methode// definieren.

console.log(new Date(), "Starte:", m); // Ausgeben.// Originalmethode aufrufen.var result = original.apply(this, arguments);console.log(new Date(), "Beende:", m); // Ausgeben.// Ergebnis der Originalmethode zuruckgeben.return result;

};}

Diese trace()-Funktion erhält ein Objekt und einen Methoden-namen. Sie ersetzt die angegebene Methode durch eine Methode,die die ursprüngliche Methode mit zusätzlicher Funktionalität um-gibt. Derartige dynamische Änderungen bestehender Methodenwerden gelegentlich als »Monkey-Patching« bezeichnet.

Funktionsargumente und -parameterJavaScript-Funktionsdefinitionen geben keinen Typ vor, der für dieFunktionsparameter erwartet wird, und Funktionsaufrufe führenkeinerlei Typprüfungen für die Argumentwerte durch, die sie über-geben. JavaScript-Funktionsaufrufe prüfen nicht einmal die Anzahlder übergebenen Argumente. Die nachfolgenden Unterabschnittebeschreiben, was passiert, wenn eine Funktion mit weniger odermehr Argumenten aufgerufen wird, als Parameter deklariert sind.Sie zeigen auch, wie Sie explizit den Typ der Funktionsargumenteprüfen, wenn Sie sicherstellen müssen, dass eine Funktion nicht mitungeeigneten Argumenten aufgerufen wird.

Optionale ParameterWenn eine Funktion mit weniger Argumenten aufgerufen wird, alsParameter deklariert sind, werden die zusätzlichen Parameter aufden undefined-Wert gesetzt. Häufig ist es hilfreich, Funktionen so

126 | Kapitel 7: Funktionen

Page 139: 3868993886_Script

zu schreiben, dass einige Argumente optional sind und weggelassenwerden können, wenn die Funktion aufgerufen wird. Dazu müssenSie Parametern, die weggelassen werden, einen vernünftigen Vor-gabewert zuweisen. Hier ist ein Beispiel:

// Die Namen der enumerierbaren Eigenschaften von Objekt o ans// Array a anhangen und a liefern. Wird a nicht angegeben,// ein neues Array erstellen und liefern.function names(o, /* optional */ a) {

if (a === undefined) // Wenn undefined,a = []; // ein neues Array nutzen.

for(var property in o) a.push(property);return a;

}

// Diese Funktion kann mit 1 oder 2 Argumenten aufgerufen// werden:var a = names(o); // Die Eigenschaften von o in einem

// neuen Array abrufen.names(p,a); // Die Eigenschaften von p an dieses

// Array anhangen.

Statt einer if-Anweisung können Sie in der ersten Zeile dieserFunktion auch den ||-Operator auf folgende idiomatische Weisenutzen (siehe den Abschnitt »Logische Ausdrücke« auf Seite 42):

a = a || [];

Argumentlisten variabler Länge:Das Arguments-ObjektWenn eine Funktion mit mehr Argumenten aufgerufen wird, als esParameternamen gibt, gibt es keine Möglichkeit, direkt auf die nichtbenannten Werte zu verweisen. Das Arguments-Objekt bietet eineLösung für dieses Problem. Im Inhalt einer Funktion verweist derBezeichner arguments auf das Arguments-Objekt für diesen Aufruf.Das Arguments-Objekt ist ein Array-artiges Objekt (siehe den Ab-schnitt »Array-artige Objekte« auf Seite 112), über das Sie die an dieFunktion übergebenen Argumentwerte über eine Zahl statt übereinen Namen abrufen können.

Funktionsargumente und -parameter | 127

Page 140: 3868993886_Script

Angenommen, Sie definieren eine Funktion f, die ein Argument xerwartet. Wird die Funktion mit zwei Argumenten aufgerufen, kannauf das erste dieser Argumente im Funktionsinhalt über den Para-meternamen x oder über arguments[0] zugegriffen werden. Daszweite Argument ist nur über arguments[1] erreichbar. Wie echteArrays hat arguments außerdem eine length-Eigenschaft, die dieAnzahl an Elementen angibt, die es enthält. Im Inhalt der Funktionf hat arguments.length bei einem Aufruf mit zwei Argumenten alsoden Wert 2.

Ein wichtiges Einsatzgebiet des Arguments-Objekts sind Funktio-nen, die auf einer beliebigen Anzahl von Argumenten operieren. Diefolgende Funktion akzeptiert eine beliebige Anzahl numerischerArgumente und liefert den Wert des größten Arguments, das ihrübergeben wird (vergleiche auch die eingebaute FunktionMath.max(), die sich auf die gleiche Weise verhält):

function max(/* ... */) {var max = Number.NEGATIVE_INFINITY;// Die Argumente durchlaufen, nach dem großten suchen// und es festhalten.for(var i = 0; i < arguments.length; i++)

if (arguments[i] > max) max = arguments[i];// Das großte Argument liefern.return max;

}var largest = max(10, 100, 2, 4, 10000, 6); // => 10000

Funktionen wie diese, die eine beliebige Anzahl von Argumentenannehmen können, bezeichnet man als variadische Funktionen,Funktionen variabler Stelligkeit oder Varargs-Funktionen. DiesesBuch nutzt den informellsten Begriff, Varargs, der aus den erstenTagen der Programmiersprache C stammt.

Beachten Sie, dass Varargs-Funktionen einen Aufruf ohne Argu-mente nicht gestatten müssen. Es kann absolut sinnvoll sein, dasarguments[]-Objekt einzusetzen, um eine Funktion zu schreiben,die eine feste Anzahl benannter und erforderlicher Argumente er-wartet, auf die eine beliebige Anzahl unbenannter optionaler Argu-mente folgen kann.

128 | Kapitel 7: Funktionen

Page 141: 3868993886_Script

Funktionen als NamensräumeRufen Sie sich aus dem Abschnitt »Variablendeklaration« auf Seite 21ins Gedächtnis, dass JavaScript Funktionsgeltung hat: Variablen, diein einer Funktion deklariert werden, sind in der gesamten Funktion(einschließlich eingebetteter Funktionen) sichtbar, aber nicht außer-halb der Funktion. Variablen, die außerhalb einer Funktion deklariertwerden, sind globale Variablen und im gesamten JavaScript-Pro-gramm sichtbar. JavaScript bietet keine Möglichkeit, Variablen zudefinieren, die nur in einem Codeblock sichtbar sind. Aus diesemGrund ist es manchmal sinnvoll, eine Funktion nur zu dem Zweck zudefinieren, um einen temporären Namensraum zu schaffen, in demSie Variablen definieren können, ohne den globalen Namensraumzuzumüllen.

Nehmen Sie beispielsweise an, Sie hätten ein Modul mit JavaScript-Code, das Sie in vielen verschiedenen JavaScript-Programmen (oderbei clientseitigem JavaScript in vielen verschiedenen Webseiten)nutzen wollen. Nehmen Sie weiterhin an, dass dieser Code, wie esCode üblicherweise tut, Variablen definiert, um die Zwischen-ergebnisse seiner Berechnungen zu speichern. Das Problem ist,dass Sie, da dieses Modul in vielen verschiedenen Programmengenutzt wird, nicht wissen, ob die von ihm erstellten Variablen mitVariablen in Konflikt geraten, die von dem Code definiert werden,der das Modul importiert. Die Lösung ist natürlich, diesen Code ineine Funktion zu stecken und diese Funktion dann aufzurufen. Sobleiben die Variablen, die ansonsten zu globalen Variablen gewor-den wären, lokal für die Funktion:

function mymodule() {// Hier kommt der Modulcode hin.// Alle von diesem Modul genutzten Variablen sind// funktionslokal und mullen nicht den globalen// Namensraum zu.

}mymodule(); // Aber vergessen Sie nicht, die Funktion

// aufzurufen!

Dieser Code definiert nur eine einzige globale Variable: den Funk-tionsnamen »mymodule«. Wenn Ihnen schon die Definition einer

Funktionen als Namensräume | 129

Page 142: 3868993886_Script

einzigen Eigenschaft zu viel ist, können Sie eine anonyme Funktiondefinieren und diese im gleichen Ausdruck auch aufrufen:

(function() { // mymodule-Funktion als unbenannter// Ausdruck formuliert.

// Hier kommt der Modulcode hin.}()); // Ende des Funktionsliterals und Aufruf.

Diese Technik, eine Funktion in einem einzigen Ausdruck zudefinieren und aufzurufen, wird so häufig genutzt, dass sie bereitsidiomatisch geworden ist. Beachten Sie den Einsatz der Klammernim Code oben. Die öffnende Klammer vor function ist erforderlich,weil der JavaScript-Interpreter ohne sie versucht, das Schlüsselwortfunction als eine Funktionsdeklarationsanweisung zu parsen. Mitden Klammern erkennt der Interpreter das Konstrukt als einenFunktionsdefinitionsausdruck. Selbst wenn sie nicht erforderlichsind, ist es üblich, die Klammern um eine Funktion anzugeben, dieunmittelbar nach der Definition aufgerufen wird.

ClosuresWie die meisten modernen Programmiersprachen nutzt JavaScriptlexikalische Geltung. Das heißt, dass Funktionen mit der Varia-blengeltung ausgeführt werden, die wirksam war, als sie definiertwurden, nicht mit der Variablengeltung, die wirksam ist, wenn sieaufgerufen werden. Diese Kombination aus Funktionsobjekt undeinem Geltungsbereich für seine Definition (einer Sammlung vonVariablenbindungen) wird als Closure bezeichnet. Closures sindbesonders bei der Verwendung verschachtelter Funktionen interes-sant. Es gibt eine Reihe mächtiger Programmiertechniken, die der-artige Closures auf Basis geschachtelter Funktionen nutzen, und ihrEinsatz ist in der JavaScript-Programmierung mittlerweile sehr ver-breitet. Wenn man ihnen das erste Mal begegnet, können Closuresverwirrend scheinen. Trotzdem ist es wichtig, dass Sie sie zumindestso gut verstehen, dass Sie bequem mit ihnen arbeiten können.

Der erste Schritt zum Verständnis von Closures ist, dass wir unsnoch einmal die lexikalischen Geltungsregeln für geschachtelteFunktionen ansehen. Betrachten Sie den folgenden Code:

130 | Kapitel 7: Funktionen

Page 143: 3868993886_Script

var scope = "globale Geltung"; // Eine globale Variable.function checkscope() {

var scope = "lokale Geltung"; // Eine lokale Variable.function f() { return scope; }return f();

}checkscope() // => "lokale Geltung"

Die Funktion checkscope() deklariert eine lokale Variable. Anschlie-ßend definiert sie eine geschachtelte Funktion, die den Wert dieserVariablen liefert, und ruft sie auf. Ihnen sollte klar sein, warum einAufruf von checkscope() »lokale Geltung« liefert. Ändern wir denCode jetzt leicht ab. Haben Sie eine Ahnung, was der folgende Codeliefern wird?

var scope = "globale Geltung"; // Eine globale Variable.function checkscope() {

var scope = "lokale Geltung"; // Eine lokale Variable.function f() { return scope; }return f;

}checkscope()() // Was gibt dieser Code zuruck?

In diesem Code ist ein Klammernpaar aus checkscope() nach außengewandert. Es wird nicht mehr die geschachtelte Funktion aufgeru-fen und dann ihr Ergebnis geliefert, sondern checkscope() liefertjetzt das geschachtelte Funktionsobjekt selbst. Was aber geschieht,wenn wir die geschachtelte Funktion (mit dem zweiten Klammern-paar in der letzten Codezeile) außerhalb der Funktion aufrufen, inder sie definiert wurde?

Erinnern Sie sich an die Grundregel der lexikalischen Geltung:JavaScript-Funktionen werden mit der Geltungsbereichskette aus-geführt, die wirksam war, als sie definiert wurden. Die geschachtelteFunktion f() wurde unter einer Geltungsbereichskette definiert, inder die Variable scope an den Wert »lokale Geltung« gebunden war.Diese Bindung ist immer noch wirksam, wenn f ausgeführt wird,wo auch immer das erfolgt. Die letzte Codezeile oben liefert also»lokale Geltung«, nicht »globale Geltung«. Das ist in Kurzform dieüberraschende und mächtige Natur von Closures: Sie fangen dielokalen Variablenbindungen (und Parameterbindungen) der äuße-ren Funktion ein, in der sie definiert wurden.

Closures | 131

Page 144: 3868993886_Script

Closures fangen die lokalen Variablen eines Funktionsaufrufs einund können diese Variablen als privaten Zustand nutzen. Derfolgende Code verwendet ein Closure wie beschrieben:

var uniqueInteger = (function() { // Definieren und// aufrufen.

var counter = 0; // Privater Zustand der// nachfolgenden Funktion.

return function() { return counter++; };}());

Wenn Sie diesen Code verstehen wollen, müssen Sie ihn sehrsorgfältig lesen. Auf den ersten Blick scheint es, als würde dieseCodezeile der Variablen uniqueInteger eine Funktion zuweisen.Eigentlich definiert der Code aber eine Funktion und ruft sie auf(wie die öffnende Klammer auf der ersten Zeile andeutet). DerVariablen uniqueInteger wird tatsächlich also der Rückgabewertder Funktion zugewiesen. Schauen wir uns jetzt den Inhalt derFunktion an, sehen wir, dass dieser Rückgabewert eine weitereFunktion ist. uniqueInteger wird also das Objekt für eine geschach-telte Funktion zugewiesen. Die geschachtelte Funktion kann auf alleVariablen zugreifen, die gültig sind, und kann die counter-Variablenutzen, die von der äußeren Funktion definiert wird. Kehrt dieäußere Funktion zurück, kann kein anderer Code die Variablecounter mehr sehen – allein die innere Funktion hat noch daraufZugriff. Jeder Aufruf von uniqueInteger() gibt einen neuen Integer-wert zurück. JavaScript hat dabei keine Möglichkeit, den Wert derinneren counter-Variable zu verändern.

Private Variablen wie counter müssen nicht auf eine einzelne Funk-tion beschränkt sein. Es ist durchaus möglich, verschachtelte Funk-tionen in der gleichen äußeren Funktion zu definieren, die sich diegleichen privaten Variablen teilen und darauf zugreifen. Nehmenwir beispielsweise den folgenden Code:

function counter() {var n = 0;return {

count: function() { return n++; },reset: function() { n = 0; }

};}

132 | Kapitel 7: Funktionen

Page 145: 3868993886_Script

var c = counter(), // Zwei Zahler erstellen.d = counter();

c.count() // => 0d.count() // => 0: Sie zahlen unabhangig.c.reset() // Die Methoden reset() und count() teilen

// einen Zustand.c.count() // => 0: weil wir c zuruckgesetzt haben.d.count() // => 1: d wurde nicht zuruckgesetzt.

Die Funktion counter() liefert ein »Zählerobjekt«. Dieses Objekthat zwei Methoden: count() liefert die nächste ganze Zahl, undreset() setzt den internen Zustand zurück. Als Erstes müssen Sieverstehen, dass die beiden Methoden den Zugriff auf die privateVariable n teilen, als zweites, dass jeder Aufruf von counter() eineneue Geltungsbereichskette und eine neue private Variable erstellt.Wenn Sie counter() zweimal aufrufen, erhalten Sie also zwei unter-schiedliche Zählerobjekte mit unterschiedlichen privaten Variablen.Wird auf dem einen Zählerobjekt count() oder reset() aufgerufen,hat das keine Auswirkungen auf das andere Zählerobjekt.

In dem Beispiel oben wurden zwei Funktionen in der gleichenGeltungsbereichskette definiert und teilten sich den Zugriff auf diegleichen privaten Variablen. Das ist eine wichtige Technik, aber esist ebenso wichtig, dass Sie erkennen können, wenn Closures ver-sehentlich den Zugriff auf eine Variable teilen, wenn das eigentlichnicht erwünscht ist. Betrachten Sie den folgenden Code:

// Diese Funktion liefert eine Funktion, die immer v// liefert.function constant(v) { return function() { return v; }; }

// Ein Array mit konstanten Funktionen definieren:var funcs = [];for(var i = 0; i < 10; i++) funcs[i] = constant(i);

// Die Funktion beim Array-Element 5 liefert den Wert 5.funcs[5]() // => 5

Bei der Arbeit mit Code wie diesem, der mehrere Closures in einerSchleife erstellt, wird häufig der Fehler gemacht, die Schleife in dieFunktion zu verschieben, die die Closures definiert. Schauen Siesich beispielsweise den folgenden Code an:

Closures | 133

Page 146: 3868993886_Script

// Ein Array mit Funktionen liefern, die die Werte 0-9// liefern.function constfuncs() {

var funcs = [];for(var i = 0; i < 10; i++)

funcs[i] = function() { return i; };return funcs;

}

var funcs = constfuncs();funcs[5]() // Was liefert das?

Der Code oben definiert 10 Closures und speichert sie in einemArray. Die Closures werden alle im gleichen Aufruf der Funktiondefiniert, teilen also den Zugriff auf die Variablen i. Wenn const-funcs() zurückkehrt, ist der Wert der Variablen i gleich 10. Unddieser Wert wird von allen 10 Closures geteilt. Deswegen liefern alleFunktionen im zurückgelieferten Array mit Funktionen den glei-chen Wert, was in keiner Weise erwünscht war. Es ist wichtig, dassSie sich merken, dass die Geltungsbereichskette, die mit einerClosure verbunden ist, »live« ist. Innere Funktionen erstellen keineprivaten Kopien des Geltungsbereichs oder statische Schnapp-schüsse der Variablenbindungen.

Ein andere Sache, die Sie sich beim Schreiben von Closures merkensollten, ist, dass this ein JavaScript-Schlüsselwort ist, keine Varia-ble. Wie zuvor bereits erwähnt wurde, hat jeder Funktionsaufrufeinen this-Wert. Closures haben deswegen keinen Zugriff auf denthis-Wert der äußeren Funktion, es sei denn, die äußere Funktionspeichert diesen Wert in einer Variablen:

var self = this; // Diesen Wert fur den Zugriff aus// inneren Funktionen in einer// Variablen speichern.

Für die arguments-Bindung gilt das Gleiche. arguments ist zwar keinSprachschlüsselwort, wird aber automatisch für jeden Funktions-aufruf deklariert. Da eine Closure ihre eigene Bindung für argumentshat, kann sie auf die Argumente der äußeren Funktion nur zugrei-fen, wenn diese das Array in einer Variablen mit anderem Namengespeichert hat:

var outerArguments = arguments; // Fur den Zugriff aus// inneren Funktionen speichern.

134 | Kapitel 7: Funktionen

Page 147: 3868993886_Script

Funktionseigenschaften, -methoden und-konstruktorenWir haben gesehen, dass Funktionen in JavaScript-ProgrammenWerte sind. Der typeof-Operator liefert den String »function«,wenn er auf eine Funktion angewandt wird, aber Funktionen sindeigentlich nur eine besondere Art von JavaScript-Objekt. Da Funk-tionen Objekte sind, können sie wie alle anderen Objekte Eigen-schaften und Methoden haben. Es gibt sogar einen Function()-Konstruktor, mit dem neue Funktionsobjekte erstellt werdenkönnen. Die Funktionsmethoden call() und apply() behandelnwir im Abschnitt »Indirekter Aufruf« auf Seite 124. Die folgendenAbschnitte befassen sich mit den verbliebenen Funktionseigen-schaften und -methoden sowie mit dem Function()-Konstruktor.

Die length-EigenschaftIm Inhalt einer Funktion gibt arguments.length die Anzahl derArgumente an, die an die Funktion übergeben wurden. Die length-Eigenschaft einer Funktion selbst hat allerdings eine andere Bedeu-tung. Diese schreibgeschützte Eigenschaft liefert die Stelligkeit derFunktion – die Anzahl an Parametern, die sie in ihrer Parameterlistedeklariert. Diese Anzahl entspricht üblicherweise der Anzahl vonArgumenten, die die Funktion erwartet.

Die prototype-EigenschaftJede Funktion hat eine prototype-Eigenschaft, die auf das Objektverweist, das als Prototypobjekt bezeichnet wird. Jede Funktion hatein anderes Prototypobjekt. Wenn eine Funktion als Konstruktoreingesetzt wird, erbt das neu erstellte Objekt Eigenschaften vomPrototypobjekt. Prototypen und die Eigenschaft prototype wurdenim Abschnitt »Prototypen« auf Seite 79 betrachtet und werdenerneut in Kapitel 8 behandelt.

Funktionseigenschaften, -methoden und -konstruktoren | 135

Page 148: 3868993886_Script

Die bind()-MethodeDie Methode bind() wurde in ECMAScript 5 eingeführt, lässt sichin ECMAScript 3 aber leicht simulieren. Wie der Name impliziert,ist der wesentliche Zweck von bind(), eine Funktion an ein Objektzu binden. Wenn Sie bind() auf einer Funktion f aufrufen unddabei das Objekt o übergeben, liefert die Methode eine neue Funk-tion. Ein Aufruf der neuen Funktion (als Funktion) ruft die ur-sprüngliche Funktion f als Methode von o auf. Alle Argumente, dieSie der neuen Funktion übergeben, werden der ursprünglichenFunktion übergeben. Zum Beispiel:

// Diese Funktion muss gebunden werden.function f(y) { return this.x + y; }var o = { x : 1 }; // Das Objekt, an das wir binden werden.var g = f.bind(o); // Ein Aufruf von g(x) ruft o.f(x) auf.g(2) // => 3

Derartige Codebindungen kann man auch ganz leicht folgenderma-ßen erreichen:

// Liefert eine Funktion, die f als Methode von o aufruft// und dabei alle Argumente ubergibt.function bind(f, o) {

// Die bind()-Methode nutzen, wenn es eine gibt.if (f.bind) return f.bind(o);else return function() {

// Andernfalls die Bindung folgendermaßen// umsetzen.return f.apply(o, arguments);

};}

Die ECMAScript 5-Methode bind() bindet allerdings nicht bloßeine Funktion an ein Objekt. Sie führt auch eine Teilanwendungdurch: Alle bind() über das erste hinaus übergebenen Argumentewerden ebenso gebunden wie der this-Wert. Die Teilanwendungist eine in der funktionalen Programmierung verbreitete Technik,die gelegentlich auch als Currying bezeichnet wird. Hier ist einBeispiel für den Einsatz von bind() zur Teilanwendung von Funk-tionen:

var sum = function(x,y) { return x + y };// Eine neue Funktion wie sum erstellen, bei der der

136 | Kapitel 7: Funktionen

Page 149: 3868993886_Script

// this-Wert an null und der Wert des ersten Arguments// an 1 gebunden ist. Diese neue Funktion erwartet nur// noch ein Argument.var succ = sum.bind(null, 1);succ(2) // => 3: x ist an 1 gebunden, und fur y

// ubergeben wir 2.

Die toString()-MethodeWie alle JavaScript-Objekte haben Funktionen eine toString()-Me-thode. Die ECMAScript-Spezifikation verlangt, dass diese Methodeeinen String liefert, der der Syntax der Funktionsdeklarationsanwei-sung folgt. In der Praxis ist es so, dass die meisten (aber nicht alle)Implementierungen dieser toString()-Methode den vollständigenQuellcode der Funktion liefern. Eingebaute Funktionen liefernüblicherweise einen String, der als Funktionsinhalt etwas wie »[na-tive code]« einschließt.

Der Function()-KonstruktorFunktionen werden üblicherweise mit dem Schlüsselwort functiondefiniert, entweder als Funktionsdefinitionsanweisung oder alsFunktionsdeklarationsausdruck. Aber Funktionen können auchmit dem Function()-Konstruktor definiert werden. Zum Beispiel:

var f = new Function("x", "y", "return x*y;");

Diese Codezeile erstellt eine neue Funktion, die mehr oder mindereiner Funktion entspricht, die mit der folgenden vertrauten Syntaxdefiniert wurde:

var f = function(x, y) { return x*y; }

Der Function()-Konstruktor erwartet eine beliebige Anzahl vonStringargumenten. Das letzte Argument ist der Text des Funktions-inhalts. Er kann beliebige JavaScript-Anweisungen enthalten, dievoneinander durch Semikola abgegrenzt werden. Alle anderen Ar-gumente für den Konstruktor sind Strings, die die Parameternamenfür die Funktion angeben. Wenn Sie eine Funktion definieren, diekeine Argumente erwartet, übergeben Sie einfach nur einen einzigenString – den Funktionsinhalt – an den Konstruktor.

Funktionseigenschaften, -methoden und -konstruktoren | 137

Page 150: 3868993886_Script

Ein sehr wichtiger Aspekt des Function()-Konstruktors ist, dass mitihm erstellte Funktionen keine lexikalische Geltung nutzen. Statt-dessen werden sie immer so kompiliert, als wären es Funktionen aufoberster Programmebene: Das heißt, sie können zwar auf globaleVariablen zugreifen, nicht aber auf lokale.

138 | Kapitel 7: Funktionen

Page 151: 3868993886_Script

KAPITEL 8

Klassen

JavaScript-Objekte wurden in Kapitel 5 behandelt. Jenes Kapitelbehandelte Objekte als eine eindeutige Menge von Eigenschaften,die bei jedem Objekt anders ist. Häufig ist es jedoch nützlich,Klassen von Objekten zu definieren, die bestimmte Eigenschaftenteilen. Mitglieder oder Instanzen einer Klasse haben einerseits eigeneEigenschaften zum Speichern ihrer Zustände, andererseits aberauch gemeinsame Eigenschaften (in der Regel Methoden), die ihrVerhalten definieren. Dieses Verhalten wird von der Klasse definiertund von allen Instanzen geteilt. Stellen Sie sich beispielsweise eineKlasse namens Complex vor, die komplexe Zahlen repräsentiertund die arithmetischen Operationen auf ihnen definiert. Eine In-stanz der Klasse Complex hat Eigenschaften, die den reellen undden imaginären Anteil (Zustand) einer komplexen Zahl festhalten.Außerdem würde die Klasse Complex Methoden zur Addition undMultiplikation (also zum Verhalten) derartiger Zahlen definieren.

In JavaScript basieren Klassen auf einem prototypbasierten Ver-erbungsmechanismus. Wenn zwei Objekte Eigenschaften vom glei-chen Prototypobjekt erben, sagen wir, dass sie Instanzen der glei-chen Klasse sind. JavaScripts Prototypen und Vererbung haben wiruns in den Abschnitten »Prototypen« auf Seite 79 und »Vererbungvon Eigenschaften« auf Seite 82 angesehen. Mit dem Material injenen Abschnitten müssen Sie vertraut sein, wenn Sie dieses Kapitelverstehen wollen. Dieses Kapitel behandelt Prototypen im Ab-schnitt »Klassen und Prototypen« auf Seite 140.

Wenn zwei Objekte vom gleichen Prototyp erben, heißt das übli-cherweise (aber nicht notwendigerweise), dass beide mit der glei-

| 139

Page 152: 3868993886_Script

chen Konstruktorfunktion erstellt und initialisiert wurden. Kons-truktoren wurden in den Abschnitten »Initialisierer« auf Seite 26,»Objekte mit new erstellen« auf Seite 79 und »Konstruktoraufruf«auf Seite 123 behandelt und kommen in diesem Kapitel im Ab-schnitt »Klassen und Konstruktoren« auf Seite 142 erneut vor.

Wenn Sie mit stark typisierten objektorientierten Programmierspra-chen wie Java oder C++ vertraut sind, werden Sie feststellen, dassJavaScript-Klassen ganz anders sind als die Klassen in diesen Spra-chen. Es gibt einige syntaktische Ähnlichkeiten, und Sie können vieleEinrichtungen »klassischer« Klassen in JavaScript emulieren, aber ambesten ist es eigentlich, Sie machen sich von Anfang an klar, dassJavaScripts Klassen und sein prototypbasierter Vererbungsmecha-nismus sich grundlegend von den Klassen und dem klassenbasiertenVererbungsmechanismus von Java und ähnlichen Sprachen un-terscheiden. Der Abschnitt »Java-artige Klassen in JavaScript« aufSeite 147 zeigt, wie man klassische Klassen in JavaScript emuliert.Eines der wichtigen Kennzeichen von JavaScript-Klassen ist, dass siedynamisch erweitert werden können. Der Abschnitt »Klassen erwei-tern« auf Seite 153 erläutert, wie man das macht.

Klassen und PrototypenIn JavaScript besteht eine Klasse aus der Menge von Objekten, dieEigenschaften vom gleichen Prototyp erben. Das Prototypobjekt istdeswegen das zentrale Kennzeichen einer Klasse. In Beispiel 5-1haben wir eine inherit()-Funktion deklariert, die ein neu erstelltesObjekt liefert, das vom angegebenen Prototypobjekt erbt. In diesemKapitel verwenden wir anstelle der portableren Hilfsfunktion inhe-rit() die eingebaute ES5-Funktion Object.create(). Wenn wir einPrototypobjekt definieren und dann Object.create() nutzen, umObjekte zu erstellen, die von ihm erben, haben wir eine JavaScript-Klasse definiert. In der Regel verlangen die Instanzen von Klasseneine weitere Initialisierung, und meist definiert man dazu eineFunktion, die ein neues Objekt erstellt und initialisiert. Beispiel 8-1zeigt dies: Es definiert ein Prototypobjekt für eine Klasse, die einenWertebereich repräsentiert, und definiert außerdem eine »Fabrik-funktion«, die neue Instanzen der Klasse erstellt und initialisiert.

140 | Kapitel 8: Klassen

Page 153: 3868993886_Script

Beispiel 8-1: Eine einfache JavaScript-Klasse

// range.js: Eine Klasse, die einen Wertebereich reprasentiert.

// Das ist eine Fabrikfunktion, die ein neues Range-Objekt// liefert.function range(from, to) {

// Nutzt inherit(), um ein Objekt zu erstellen, das vom// unten definierten Prototypobjekt erbt. Das// Prototypobjekt wird als Eigenschaft dieser Funktion// gespeichert und definiert die gemeinsamen Methoden// (Verhalten) fur alle Range-Objekte.var r = Object.create(range.methods);

// Ober- und Untergrenze (Zustand) des neuen Range-Objekts// speichern.// Das sind nicht geerbte Eigenschaften, die fur dieses// Objekt spezifisch sind.r.from = from;r.to = to;

// Schließlich das neue Objekt liefern.return r;

}

// Dieses Prototypobjekt definiert Methoden, die von// allen Range-Objekten geerbt werden.range.methods = {

// Liefert true, wenn x im Intervall liegt, andernfalls// false.includes: function(x) {

return this.from <= x && x <= this.to;},// f einmal fur alle ganzen Zahlen im Intervall aufrufen.// Diese Funktion funktioniert nur bei Zahlbereichen.foreach: function(f) {

for(var x=Math.ceil(this.from); x <= this.to; x++)f(x);

},// Liefert eine Stringdarstellung des Intervalls.toString: function() {

return "(" + this.from + "..." + this.to + ")";}

};

// Hier sind Beispiele fur die Verwendung von Range-Objekten.var r = range(1,3); // Ein Range-Objekt erstellen.

Klassen und Prototypen | 141

Page 154: 3868993886_Script

r.includes(2); // => true: 2 fallt in den Wertebereich.r.foreach(console.log); // Liefert 1 2 3.console.log(r); // Liefert (1...3).

An dem Code in Beispiel 8-1 gibt es einige Dinge, die wir unsgenauer ansehen sollten. Der Code definiert die Fabrikfunktionrange(), mit der neue Range-Objekte erstellt werden. Beachten Sie,dass wir eine Eigenschaft dieser range()-Funktion, range.methods,als praktischen Platz zur Speicherung des Prototypobjekts gewählthaben, durch das die Klasse definiert wird. Dass wir das Prototyp-objekt hier speichern, ist weder etwas Besonderes noch etwasIdiomatisches. Beachten Sie zweitens, dass die Funktion range()auf allen Range-Objekten die beiden Eigenschaften from und todeklariert. Das sind Eigenschaften, die nicht vererbt oder gemein-sam genutzt werden. Sie repräsentieren den einzigartigen Zustandder einzelnen Range-Objekte. Beachten Sie außerdem, dass die inrange.methods definierten vererbten Methoden das Schlüsselwortthis einsetzen, um auf das Objekt zu verweisen, auf dem sie auf-gerufen wurden, damit sie die Eigenschaften from und to nutzenkönnen. Dieser Rückgriff auf das Schlüsselwort this ist ein fun-damentales Kennzeichen von Methoden, die auf Instanzen vonKlassen aufgerufen werden.

Klassen und KonstruktorenBeispiel 8-1 zeigt eine Möglichkeit, eine JavaScript-Klasse zu defi-nieren. Das ist aber nicht das übliche Verfahren, weil dieses Beispielkeinen Konstruktor definierte. Ein Konstruktor ist eine Funktion, diezur Initialisierung neu erstellter Objekte gedacht ist. Konstruktorenwerden, wie im Abschnitt »Konstruktoraufruf« auf Seite 123 be-schrieben, mit dem Schlüsselwort new aufgerufen. Konstruktor-aufrufe mit new erstellen das neue Objekt automatisch, der Kons-truktor muss also nur noch den Zustand des Objekts initialisieren.Der entscheidende Punkt von Konstruktoraufrufen ist, dass die pro-totype-Eigenschaft des Konstruktors als Prototyp des neuen Objektsgenutzt wird. Das bedeutet, dass alle Objekte, die mit dem gleichenKonstruktor erstellt wurden, vom gleichen Objekt erben und deswe-gen Instanzen der gleichen Klasse sind. Beispiel 8-2 zeigt, wie wir die

142 | Kapitel 8: Klassen

Page 155: 3868993886_Script

Klasse Range aus Beispiel 8-1 so ändern könnten, dass statt einerFabrikfunktion eine Konstruktorfunktion genutzt wird:

Beispiel 8-2: Eine Range-Klasse mit Konstruktor

// range2.js: Eine weitere Klasse, die einen Wertebereich// reprasentiert.

// Das ist eine Konstruktorfunktion, die neue Range-Objekte// initialisiert.// Beachten Sie, dass sie kein Objekt erstellt oder liefert.// Sie initialisiert es nur.function Range(from, to) {

// Die Unter- und Obergrenze (Zustand) des neuen// Range-Objekts speichern.// Das sind nicht geerbte Eigenschaften, die fur dieses// Objekt spezifisch sind.this.from = from;this.to = to;

}

// Alle Range-Objekte erben von diesem Objekt.// Beachten Sie, dass der Eigenschaftsname "prototype" sein// muss.Range.prototype = {

// Liefert true, wenn x ins Intervall fallt, andernfalls// false.includes: function(x) {

return this.from <= x && x <= this.to;},// f einmal fur alle ganzen Zahlen im Intervall aufrufen.foreach: function(f) {

for(var x=Math.ceil(this.from); x <= this.to; x++)f(x);

},// Liefert eine Stringdarstellung des Intervalls.toString: function() {

return "(" + this.from + "..." + this.to + ")";}

};

// Hier sind Beispiele fur die Verwendung von Range-Objekten.var r = new Range(1,3); // Ein Range-Objekt erstellen.r.includes(2); // => true: 2 liegt im Bereich.r.foreach(console.log); // Liefert 1 2 3console.log(r); // Liefert (1...3)

Klassen und Konstruktoren | 143

Page 156: 3868993886_Script

Es lohnt sich, Beispiel 8-1 und Beispiel 8-2 sorgfältig zu vergleichenund die Unterschiede zwischen diesen beiden Verfahren zur Defini-tion von Klassen festzuhalten. Beachten Sie zunächst, dass wir dieFabrikfunktion range() in Range() umbenannt haben, als wir sie ineinen Konstruktor umgewandelt haben. Das ist eine äußerst üblicheProgrammierkonvention: In gewisser Hinsicht definieren Kons-truktorfunktionen Klassen, und die Namen von Klassen beginnenmit Großbuchstaben. Gewöhnliche Funktionen und Methoden ha-ben Namen, die mit Kleinbuchstaben beginnen.

Beachten Sie als Nächstes, dass der Range()-Konstruktor (am Endedes Beispiels) mit dem Schlüsselwort new aufgerufen wird, währenddie Fabrikfunktion range() ohne es aufgerufen wurde. Beispiel 8-1nutzt einen gewöhnlichen Funktionsaufruf (siehe den Abschnitt»Funktionsaufruf« auf Seite 120), um das neue Objekt zu erstellen,und Beispiel 8-2 nutzt einen Konstruktoraufruf (siehe den Abschnitt»Konstruktoraufruf« auf Seite 123). Weil der Range()-Konstruktormit new aufgerufen wird, muss er Object.create() nicht aufrufenund auch nichts anderes unternehmen, um das neue Objekt zuerstellen. Das neue Objekt wird automatisch erstellt, bevor derKonstruktor aufgerufen wird, und er kann darauf über das Schlüs-selwort this zugreifen. Der Range()-Konstruktor muss den Wert,auf den this verweist, nur noch initialisieren. Konstruktoren müs-sen das neu erstellte Objekt nicht einmal zurückliefern. Der Kons-truktoraufruf erstellt automatisch das neue Objekt, ruft den Kons-truktor als eine Methode dieses Objekts auf und liefert das neueObjekt zurück.

Ein weiterer entscheidender Unterschied zwischen Beispiel 8-1 undBeispiel 8-2 ist der Name, der dem Prototypobjekt gegeben wurde.Beim ersten Beispiel war der Prototyp range.methods. Das war einpraktischer und aussagekräftiger Name, aber er war willkürlich. Imzweiten Beispiel ist der Prototyp Range.prototype, und dieser Namemuss so lauten. Ein Aufruf des Range()-Konstruktors nutzt auto-matisch Range.prototype als Prototyp des neuen Range-Objekts.

Beachten Sie schließlich, dass etwas bei Beispiel 8-1 und Beispiel 8-2gleich bleibt: Die Range-Methoden werden bei beiden Klassen aufdie gleiche Weise definiert und aufgerufen.

144 | Kapitel 8: Klassen

Page 157: 3868993886_Script

Konstruktoren und KlassenidentitätWie wir gesehen haben, ist das Prototypobjekt für die Identität einerKlasse entscheidend: Zwei Objekte sind nur dann Instanzen dergleichen Klasse, wenn sie vom gleichen Prototypobjekt erben. DieKonstruktorfunktion, die den Zustand eines neuen Objekts initiali-siert, ist nicht entscheidend: Wenn zwei Konstruktorfunktionenprototype-Eigenschaften haben, die auf das gleiche Prototypobjektzeigen, können beide Konstruktoren genutzt werden, um Instanzender gleichen Klasse zu erstellen.

Obgleich Konstruktoren nicht so entscheidend sind wie Prototy-pen, dient der Konstruktor als öffentliches Gesicht der Klasse. Dasoffensichtlichste Zeichen dafür ist, dass der Name der Kons-truktorfunktion üblicherweise als Klassenname übernommen wird.Wir sagen beispielsweise, dass der Range()-Konstruktor Range-Ob-jekte erstellt. Entscheidender aber ist, dass Konstruktoren mit deminstanceof-Operator genutzt werden, um zu prüfen, ob ein ObjektMitglied einer Klasse ist. Wenn wir ein Objekt r haben und wissenwollen, ob es ein Range-Objekt ist, können wir Folgendes schrei-ben:

// Liefert true, wenn r von Range.prototype erbt.r instanceof Range

Der instanceof-Operator prüft nicht, ob r tatsächlich mit demRange-Konstruktor initialisiert wurde. Er prüft, ob das Objekt vonRange.prototype erbt. Trotzdem stärkt die instanceof-Syntax dieVerwendung von Konstruktoren als öffentliche Identität einer Klas-se. Wir werden den instanceof-Operator später in diesem Kapitelerneut sehen.

Die constructor-EigenschaftIn Beispiel 8-2 haben wir Range.prototype auf ein neues Objektgesetzt, das die Methoden für unsere Klasse enthielt. Obgleich espraktisch war, diese Methoden als Eigenschaften eines einzigenObjektliterals zu formulieren, war das für die Erstellung des neuenObjekts eigentlich nicht notwendig. Jede beliebige JavaScript-Funk-tion kann als Konstruktor verwendet werden, und Konstruktor-

Klassen und Konstruktoren | 145

Page 158: 3868993886_Script

aufrufe benötigen eine prototype-Eigenschaft. Deswegen hat jedeJavaScript-Funktion automatisch eine prototype-Eigenschaft. DerWert dieser Eigenschaft ist ein Objekt, das eine nicht enumerierbareconstructor-Eigenschaft hat. Der Wert dieser constructor-Eigen-schaft ist das Funktionsobjekt:

// F.prototype.constructor === F for any function F.var F = function() {}; // Das ist ein Funktionsobjekt.var p = F.prototype; // Das ist das Prototypobjekt,

// das mit ihr verknupft ist.var c = p.constructor; // Das ist die mit dem Prototyp

// verknupfte Funktion.c === F // => true: Fur jede Funktion gilt

// F.prototype.constructor==F.

Die Existenz dieses vordefinierten Prototypobjekts in der con-structor-Eigenschaft bedeutet, dass Objekte üblicherweise eineconstructor-Eigenschaft erben, die auf ihren Konstruktor zeigt. DaKonstruktoren als öffentliche Identität einer Klasse dienen, gibtdiese constructor-Eigenschaft die Klasse eines Objekts an:

var o = new F(); // Ein Objekt o der Klasse F erstellen.o.constructor === F // => true: Die Eigenschaft

// constructor gibt die Klasse an.

Abbildung 8-1 illustriert dieses Verhältnis zwischen der Kons-truktorfunktion, ihrem Prototypobjekt, der Referenz vom Prototypzum Konstruktor und den Instanzen, die mit dem Konstruktorerstellt wurden.

Konstruktor Prototyp Instanzen

Range() new Range(1,2)

new Range(3,4)

prototypeconstructorincludes: ...foreach: ...toString: ...

erbt

erbt

Abbildung 8-1: Eine Konstruktorfunktion, ihr Prototyp und Instanzen

Beachten Sie, dass Abbildung 8-1 unseren Range()-Konstruktor alsBeispiel nutzt. Eigentlich aber überschreibt die in Beispiel 8-2 defi-

146 | Kapitel 8: Klassen

Page 159: 3868993886_Script

nierte Klasse das vordefinierte Range.prototype-Objekt mit einemeigenen Objekt. Und das neue Prototypobjekt, das sie definiert, hatkeine constructor-Eigenschaft. Instanzen der so definierten KlasseRange haben also tatsächlich überhaupt keine constructor-Eigen-schaft. Dieses Problem können wir beheben, indem wir dem Pro-totyp diese Eigenschaft explizit hinzufügen:

Range.prototype = {constructor: Range, // Die Ruckreferenz auf den

// Konstruktor explizit setzen.includes: function(x) {

return this.from <= x && x <= this.to;},// etc...

};

Eine weitere verbreitete Technik ist es, das vordefinierte Prototyp-objekt mit seiner constructor-Eigenschaft zu nutzen und ihm nach-einander die Methoden hinzuzufügen:

// Das vordefinierte Range.prototype-Objekt erweitern,// damit wir die automatisch erstellte Eigenschaft// Range.prototype.constructor nicht uberschreiben.Range.prototype.includes = function(x) {

return this.from<=x && x<=this.to;};Range.prototype.foreach = function(f) {

for(var x=Math.ceil(this.from); x <= this.to; x++)f(x);

};Range.prototype.toString = function() {

return "(" + this.from + "..." + this.to + ")";};

Java-artige Klassen in JavaScriptWenn Sie in Java oder einer ähnlichen stark typisierten objekt-orientierten Sprache programmiert haben, sind Sie vielleicht darangewöhnt, mit vier bestimmten Arten von Klassenmembern zu arbei-ten:

InstanzfelderDas sind instanzspezifische Eigenschaften oder Variablen, dieden Zustand einzelner Objekte speichern.

Java-artige Klassen in JavaScript | 147

Page 160: 3868993886_Script

InstanzmethodenDas sind Methoden, die allen Instanzen der Klasse gemeinsamsind, aber über die einzelnen Instanzen aufgerufen werden.

KlassenfelderDas sind Eigenschaften oder Variablen, die mit der Klasse stattmit den Instanzen der Klasse verbunden sind.

KlassenmethodenDas sind Methoden, die mit der Klasse statt mit den Instanzenverbunden sind.

JavaScript unterscheidet sich von Java insofern, als dass Funktionenin JavaScript Werte sind und es keine scharfe Trennung zwischenMethoden und Feldern gibt. Wenn der Wert einer Eigenschaft eineFunktion ist, definiert diese Eigenschaft eine Methode, andernfalls istes nur eine gewöhnliche Eigenschaft oder ein »Feld«. Trotz diesesUnterschieds können wir die vier Kategorien von Klassenmembernaus Java in JavaScript simulieren. In JavaScript sind an jeder Klassen-definition drei verschiedene Objekte beteiligt (siehe Abbildung 8-1),und die Eigenschaften dieser drei Objekte können wie unterschiedli-che Arten von Klassenmembern agieren:

KonstruktorobjektWie angemerkt, definiert die Konstruktorfunktion (ein Objekt)einen Namen für eine JavaScript-Klasse. Eigenschaften, die Siediesem Konstruktor hinzufügen, dienen als Klassenfelder undKlassenmethoden.

PrototypobjektDie Eigenschaften dieses Objekts werden von allen Instanzender Klasse geerbt, und Eigenschaften, deren Werte Funktionensind, verhalten sich wie Instanzmethoden der Klasse.

InstanzobjektJede Instanz einer Klasse ist ein selbstständiges, unabhängigesObjekt, und direkt auf einer Instanz definierte Eigenschaftenwerden von den anderen Instanzen nicht geteilt. Auf Instanzendefinierte Eigenschaften, die keine Funktionen sind, verhaltensich wie Instanzfelder der Klasse.

148 | Kapitel 8: Klassen

Page 161: 3868993886_Script

Wir können den Prozess der Klassendefinition in JavaScript aufeinen Algorithmus mit drei Schritten reduzieren. Zunächst schrei-ben wir eine Konstruktorfunktion, die Instanzeigenschaften aufneuen Objekten setzt. Zweitens definieren wir auf dem prototype-Objekt des Konstruktors die Instanzmethoden. Drittens definierenwir die Klassenfelder und Klassenmethoden auf dem Konstruktorselbst. Wir können diesen Algorithmus sogar als eine einfachedefineClass()-Funktion implementieren:

// Eine einfache Funktion zur Definition einfacher Klassen.function defineClass(constructor, // Initialisierung

methods, // Instanzmethodenstatics) // Klasseneigenschaften

{if (methods) {

// Prototyp-Methoden kopierenfor(var m in methods)

constructor.prototype[m] = methods[m];}if (statics) {

// Statische Eigenschaften in den Konstruktor// kopieren.for(var s in statics)

constructor[s] = statics[s];}

return constructor;}

// Das ist eine einfache Variante unserer Klasse Range.var SimpleRange =

defineClass(function(f,t) { this.f = f; this.t = t; },{

includes: function(x) {return this.f <= x && x <= this.t;

},toString: function() {

return this.f + "..." + this.t;}

},{

Java-artige Klassen in JavaScript | 149

Page 162: 3868993886_Script

upto: function(t) {return new SimpleRange(0, t);

}}

);

Unveränderliche KlassenIm Abschnitt »Eigenschaftsattribute« auf Seite 90 haben wir dieECMAScript 5-Methode Object.defineProperties() vorgestellt.Mit ihr lassen sich schreibgeschützte und nicht-enumerierbare Ei-genschaften definieren, die von Eigenschaftsdeskriptoren auch anObject.create übergeben werden können. Wir können diese Mög-lichkeiten von ES5 verwenden, um Klassen zu definieren, derenInstanzen unveränderlich (»immutable«) sind. In Beispiel 8-3 sehenSie eine unveränderliche Version unserer Range-Klasse, deren In-stanzmethoden, wie die Methoden der eingebauten Klassen, nichtenumerierbar sind. Zusätzlich nutzt Beispiel 8-3 noch einen raf-finierten Trick. Es nutzt eine Konstruktorfunktion, die beim Aufrufohne das Schlüsselwort new wie eine Factory-Funktion arbeitet.

Beispiel 8-3: Eine unveränderliche Klasse mit nicht-enumerierbarenMethoden

// Diese Funktion funktioniert mit und ohne 'new'.// Sie ist eine Konstruktor- und Fabrikfunktion zugleich.function Range(from,to) {

// Dies sind die Deskriptoren mit schreibgeschutzten// Methoden.var props = {

from: { value:from, enumerable:true },to: { value:to, enumerable:true }

};

if (this instanceof Range) // Als Konstruktor aufgerufenObject.defineProperties(this, props);

else // Als Fabrik-Funktion aufgerufenreturn Object.create(Range.prototype, props);

}

// Prototyp mit nicht-enumerierbaren Eigenschaften erstellen.Object.defineProperties(Range.prototype, {

includes: {value: function(x) {

150 | Kapitel 8: Klassen

Page 163: 3868993886_Script

return this.from <= x && x <= this.to;},writable: true, configurable: true

},foreach: {

value: function(f) {for(var x=Math.ceil(this.from); x<=this.to; x++)

f(x);},writable: true, configurable: true

},toString: {

value: function() {return "(" + this.from + "..." + this.to + ")";

},writable: true, configurable: true

}});

UnterklassenIn der objektorientierten Programmierung kann eine Klasse B eineKlasse A erweitern oder von einer Klasse A abgeleitet werden. Wirsagen, dass A die Oberklasse und B die Unterklasse ist. Instanzenvon B erben alle Instanzmethoden von A. Die Klasse B kann ihreeigenen Instanzmethoden definieren, von denen einige Methodengleichen Namens von Klasse A überschreiben können.

Das Wichtigste bei der Erstellung von Unterklassen in JavaScript ist,dass man das Prototyp-Objekt korrekt initialisiert. Wenn ein Ob-jekt O eine Instanz der Klasse B ist und B eine Unterklasse von A ist,dann muss O auch Eigenschaften von A erben. Das erreichen wir,indem wir dafür sorgen, dass das Prototypobjekt von B vom Pro-totypobjekt von A erbt. Wenn wir die Object.create()-Funktionverwenden, könnten wir das Ganze auch so schreiben wie imfolgenden Beispiel gezeigt. (Alternativ dazu könnten wir auch dieFunktion inherit() aus Beispiel 5-1) benutzen.)

// Unterklasse B erbt von Oberklasse A.B.prototype = Object.create(A.prototype);// Die geerbte Konstruktoreigenschaft uberschreiben.B.prototype.constructor = B;

Unterklassen | 151

Page 164: 3868993886_Script

Diese zwei Codezeilen sind beim Erstellen von Unterklassen inJavaScript besonders wichtig. Ohne sie wäre das Prototyp-Objektnur ein einfaches Objekt, das von Object.prototype erbt. Das würdejedoch bedeuten, dass Ihre Klasse – wie alle Klassen – einfach nureine Unterklasse von Objekt wäre. Durch die Erweiterung vondefineClass() um diese zwei Zeilen ist es recht einfach, die Funk-tion in defineSubclass() umzuwandeln.

Beispiel 8-4 definiert eine Klasse namens DateRange als Unterklassevon Range. Date-Objekte können in JavaScript per < und > ver-glichen werden. Das heißt, DateRange erbt die Methoden in-cludes() und toString(). Gleichzeitig wird die Methode foreach()überschrieben, damit über die Tage innerhalb des definierten Be-reichs enumiert werden kann. Hierbei sollten Sie beachten, wie derDateRange.prototype erstellt wird und wie DateRange() seinen Ober-klassen-Konstruktor (mit Hilfe der call()-Methode) aufruft, umdas neue Objekt zu initialisieren.

Beispiel 8-4: Eine Range-Unterklasse

// Eine Unterklasse unserer Range-Klasse. Sie erbt die// Methoden includes() und toString() und uberschreibt die// foreach-Methode, damit sie mit Datumsangaben funktioniert.function DateRange(from, to) {

// Den Oberklassen-Konstruktor zum Initialisieren nutzen.Range.call(this, from, to);

}

// Die folgenden zwei Zeilen sind der Schlussel fur die// Erstellung von Unterklassen. Der Unterklassen-Prototyp// muss dabei vom Prototyp der Oberklasse erben.DateRange.prototype = Object.create(Range.prototype);DateRange.prototype.constructor = DateRange;

// Dieses "statische" Feld der Unterklasse enthalt// die Anzahl der Millisekunden eines Tages.DateRange.DAY = 1000*60*60*24;

// f einmal fur jeden Tag im Bereich aufrufen.DateRange.prototype.foreach = function(f) {

var d = this.from;while(d < this.to) {

f(d);d = new Date(d.getTime() + DateRange.DAY);

152 | Kapitel 8: Klassen

Page 165: 3868993886_Script

}}

var now = new Date();var tomorrow = new Date(now.getTime() + DateRange.DAY);var nextweek = new Date(now.getTime() + 7*DateRange.DAY);var week = new DateRange(now, nextweek);

week.includes(tomorrow) // => trueweek.foreach(function(d) { // Wochentage ausgeben

console.log(d.toLocaleDateString());});

Klassen erweiternDer prototypbasierte Vererbungsmechanismus von JavaScript istdynamisch: Ein Objekt erbt seine Eigenschaften von seinem Pro-totyp. Das gilt selbst dann, wenn die Eigenschaften des Prototypssich nach Erstellung des Objekts verändern. Das hat zu Folge, dasswir JavaScript-Klassen einfach dadurch erweitern können, dass wirihren Prototyp-Objekten neue Methoden hinzufügen. Hier ist derCode, mit dem die Range-Klasse um eine neue Methode erweitertwird:

// Einen neuen Bereich mit negativen Endpunkten// zuruckgeben.Range.prototype.negate = function() {

return new Range(-this.to, -this.from);};

Diese Vorgehensweise funktioniert übrigens auch für das Prototyp-Objekt der eingebauten JavaScript-Klassen. Es ist ebenso »offen«,wodurch Sie die Methoden um zusätzliche Zahlen, Strings, Arrays,Funktionen und so weiter erweitern können. Hier ein paar Beispie-le:

// Die Funktion f n-mal aufrufen und dabei die Nummer der// Iteration ubergeben, z.B. um "Hallo" dreimal auszugeben:// var n = 3;// n.times(function(n) { console.log(n + " Hallo"); });Number.prototype.times = function(f, context) {

var n = Number(this);for(var i = 0; i < n; i++) f.call(context, i);

};

Klassen erweitern | 153

Page 166: 3868993886_Script

// Falls die ES5-Methode String.trim() nicht existiert,// wird sie hier definiert. Sie entfernt Leerzeichen am// Anfang und Ende eines Strings.String.prototype.trim =

String.prototype.trim || function() {if (!this) return this;return this.replace(/^\s+|\s+$/g, "");

};

// Den Namen einer Funktion oder "" zuruckgeben.// Existiert eine name-Eigenschaft, wird diese verwendet.// Ansonsten wird die Funktion in einen String umgewandelt// und der Name auf diese Weise ermittelt.Function.prototype.getName = function() {

return this.name ||this.toString().match(/function\s*([^(]*)\(/)[1];

};

Es ist auch möglich, Object.prototype mit zusätzlichen Methodenzu versehen, die dann allen Objekten zur Verfügung stehen. DiesesVorgehen ist jedoch nicht empfehlenswert, da es vor ECMAScript 5keine Möglichkeit gibt, diese Zusatzmethoden nicht-enumerierbarzu machen. Wenn Sie Object.prototype um weitere Eigenschaftenerweitern, würden diese außerdem in allen for/in -Schleifen mitausgegeben werden.

154 | Kapitel 8: Klassen

Page 167: 3868993886_Script

KAPITEL 9

Reguläre Ausdrücke

Ein regulärer Ausdruck (kurz Regex) ist ein Objekt, das ein Zeichen-muster beschreibt. Die RegExp-Klasse von JavaScript repräsentiertreguläre Ausdrücke, und sowohl String als auch RegExp definierenMethoden, die reguläre Ausdrücke nutzen, um mächtige Muster-vergleichs- und Suchen-und-ersetzen-Operationen auf Text durch-zuführen. Dieses Kapitel beginnt mit der Definition der Syntax, diereguläre Ausdrücke zur Beschreibung von Textmustern nutzen.Dann geht es zu einer Beschreibung der String- und RegExp-Me-thoden über, die reguläre Ausdrücke nutzen.

Suchmuster mit regulären AusdrückendefinierenIn JavaScript werden reguläre Ausdrücke durch RegExp-Objekterepräsentiert. RegExp-Objekte können zwar mit dem RegExp()-Konstruktor erstellt werden, häufiger wird dazu aber eine spezielleLiteralsyntax genutzt. Wie Stringliterale als Zeichen in Anführungs-zeichen angegeben werden, so werden Regexliterale als Zeichen ineinem Paar Schrägstriche (/) angegeben. Ihr JavaScript-Code kannalso Zeilen wie diese enthalten:

var pattern = /s$/;

Diese Zeile erstellt ein neues RegExp-Objekt und weist es der Varia-blen pattern zu. Dieses RegExp-Objekt findet jeden String, der mitdem Buchstaben »s« endet. Dieser Regex hätte folgendermaßenäquivalent mit dem RegExp()-Konstruktor definiert werden können:

var pattern = new RegExp("s$");

| 155

Page 168: 3868993886_Script

Regexmuster bestehen aus einer Folge von Zeichen. Die meistenZeichen, einschließlich aller alphanumerischen Zeichen, beschrei-ben einfach Zeichen, die wörtlich gefunden werden müssen. DerRegex /java/ findet also jeden String, der die Zeichenfolge »java«enthält. Andere Zeichen in regulären Ausdrücken werden nichtwörtlich gesucht, sondern haben eine spezielle Bedeutung. DerRegex /s$/ enthält zwei Zeichen. Das erste, »s«, findet sich selbst.Das zweite, »$«, ist ein spezielles Metazeichen, das das Stringendefindet. Dieser Regex findet also jeden String, der den Buchstaben»s« als letztes Zeichen hat.

Die folgenden Abschnitte beschreiben die verschiedenen Zeichenund Metazeichen, die in JavaScript in regulären Ausdrücken ver-wendet werden.

Literale ZeichenSämtliche alphabetischen Zeichen passen in regulären Ausdrückenauf sich selbst. Bestimmte nicht-alphabetische lassen sich durch dieVerwendung von Escape-Sequenzen ebenfalls literal finden. EineListe dieser Zeichen finden Sie in Tabelle 9-1.

Tabelle 9-1: Literale Zeichen für reguläre Ausdrücke

Zeichen Findet

Alphanumerische Zeichen sich selbst

\0 NUL-Zeichen (\u0000)

\t Tabulator(\u0009)

\n Zeilenumbruch (\u000A)

\v Vertikaler Tabulator (\u000B)

\f Seitenvorschub (\u000C)

\r Wagenrücklauf (\u000D)

\x nn Ein durch die Hexadezimalzahl nn angegebenes Latin-Zeichen,z.B. ist \x0A das Gleiche wie \n.

\u xxxx Ein durch die Hexadezimalzahl xxxx angegebenes Unicode-Zeichen, z.B. ist \u0009 das Gleiche wie \t.

c X Das Steuerzeichen ^X, z.B. ist \cJ zum Zeilenumbruchzeichen\n äquivalent.

156 | Kapitel 9: Reguläre Ausdrücke

Page 169: 3868993886_Script

Eine Reihe von Interpunktionszeichen haben in regulären Ausdrü-cken eine spezielle Bedeutung. Das sind:

^ $ . * + ? = ! : | \ / ( ) [ ] { }

Die Bedeutung dieser Zeichen wird in den nachfolgenden Abschnit-ten behandelt. Einige dieser Zeichen haben nur in einem bestimm-ten Kontext eines regulären Ausdrucks eine spezielle Bedeutungund werden in anderem Kontext literal behandelt. Allgemein ist esjedoch so, dass Sie jedem dieser Zeichen, wenn Sie sie wörtlich ineinem Regex angeben wollen, einen Backslash, \, voranstellenmüssen. Andere Interpunktionszeichen wie Anführungszeichenoder @ haben keine spezielle Bedeutung und finden sich in einemregulären Ausdruck selbst.

ZeichenklassenEinzelne Zeichen können zu Zeichenklassen kombiniert werden,indem man sie in eckige Klammern einfasst. Eine Zeichenklassefindet ein beliebiges der Zeichen, die sie enthält. Der Regex /[abc]/findet also einen der Buchstaben a, b oder c. Man kann auchnegierte Zeichenklassen definieren, die alle Zeichen außer denenfinden, die in den eckigen Klammern angegeben sind. Eine negierteZeichenklasse definiert man, indem man als erstes Zeichen hinterder öffnenden eckigen Klammer ein Caret-Zeichen (^) angibt. DerRegex /[^abc]/ findet also ein beliebiges Zeichen außer a, b oder c.In Zeichenklassen können Bindestriche eingesetzt werden, um ei-nen Zeichenbereich anzugeben. Einen bliebigen Kleinbuchstabenaus dem lateinischen Alphabet finden Sie beispielsweise mit/[a-z]/, eine beliebige Ziffer oder einen beliebigen Buchstaben mit/[a-zA-Z0-9]/. Zeichenklassen funktionieren auch mit Unicode-Zeichen. Um beispielsweise ein kyrillisches Zeichen zu finden,könnten Sie den Ausdruck /[\u0400-\u04FF]/ verwenden.

Für bestimmte, häufig verwendete Zeichenklassen enthält die Syn-tax der regulären Ausdrücke außerdem eine Reihe von Abkürzun-gen. Eine Liste dieser Zeichen und eine Beschreibung der Syntaxfinden Sie in Tabelle 9-2.

Suchmuster mit regulären Ausdrücken definieren | 157

Page 170: 3868993886_Script

Tabelle 9-2: Regex-Zeichenklassen

Zeichen Findet

[...] Ein beliebiges Zeichen, das zwischen den eckigen Klammern steht.

[^...] Ein beliebiges Zeichen, das nicht zwischen den eckigen Klammern steht.

. Ein beliebiges Zeichen außer dem Zeilenumbruchzeichen oder einemanderen Unicode-Zeilenendezeichen.

\w Ein ASCII-Wortzeichen. Entspricht [a-zA-Z0-9_].

\W Ein Zeichen, das kein ASCII-Wortzeichen ist. Entspricht [^a-zA-Z0-9_].

\s Ein Unicode-Whitespace-Zeichen.

\S Ein Zeichen, das kein Unicode-Whitespace-Zeichen ist. Beachten Sie, dass\w und \S nicht das Gleiche sind.

\d Eine ASCII-Ziffer. Entspricht [0-9].

\D Ein Zeichen, das keine ASCII-Ziffer ist. Entspricht [^0-9].

[\b] Ein wörtliches Backspace-Zeichen (Sonderfall).

Beachten Sie, dass die speziellen Escape-Sequenzen für Zeichenklas-sen in eckigen Klammern genutzt werden können. \s findet ein be-liebiges Whitespace-Zeichen und \d eine beliebige Ziffer. /[\s\d]/findet also ein Whitespace-Zeichen oder eine Ziffer.

WiederholungAuf ein Zeichen oder eine Zeichenklasse können weitere Angabenfolgen, die festlegen, wie oft das betreffende Zeichen vorkommensoll. Eine Zusammenfassung der Syntax finden Sie in Tabelle 9-3.

Tabelle 9-3: Regexwiederholungszeichen

Zeichen Bedeutung

{n , m} Findet das vorangehende Element mindestens n-mal, aber nicht öfter alsm-mal.

{n ,} Findet das vorangehende Element n-mal oder öfter.

{n} Findet genau n Vorkommen des vorangehenden Elements.

? Findet null oder ein Vorkommen des vorangehenden Elements. Das heißt,das vorangehende Element ist optional. Entspricht {0,1}.

+ Findet ein oder mehrere Vorkommen des vorangehenden Elements.Entspricht {1,}.

* Findet null oder mehrere Vorkommen des vorangehenden Elements.Entspricht {0,}.

158 | Kapitel 9: Reguläre Ausdrücke

Page 171: 3868993886_Script

Die folgenden Zeilen bieten einige Beispiele:

/\d{2,4}/ // Findet zwei bis vier Ziffern./\w{3}\d?/ // Findet genau drei Wortzeichen und eine

// optionale Ziffer./\s+java\s+/ // Findet "java" mit einem oder mehreren

// Whitespace-Zeichen davor und dahinter./[^(]*/ // Findet null oder mehrere Zeichen, die

// keine '(' sind.

Geben Sie acht, wenn Sie die Wiederholungszeichen * und ? nutzen.Da diese Zeichen angeben, dass das vorangehende Element null-malangetroffen werden darf, dürfen sie auch nichts finden. Beispiels-weise findet der Regex /a*/ auch den String »bbbb«, da er nullVorkommen des Buchstabens a enthält!

Nicht-gierige Wiederholung

Die in Tabelle 9-3 aufgeführten Wiederholungszeichen finden soviele Vorkommen wie möglich, solange die nachfolgenden Ele-mente des Regex noch Entsprechungen finden können. Eine der-artige Wiederholung nennt man »gierig«. Man kann aber auchangeben, dass die Wiederholung nicht gierig sein soll. Dazu gebenSie hinter dem bzw. den Wiederholungszeichen einfach ein Frage-zeichen an: ??, +?, *? oder gar {1,5}?. Ein Beispiel: Der reguläreAusdruck /a+/ findet ein oder mehrere Vorkommen des Buchsta-bens »a«. Wird er auf den String »aaa« angewandt, findet er alle dreiBuchstaben. /a+?/ findet ebenfalls ein oder mehrere Vorkommendes Buchstabens »a«, sucht aber so wenige Zeichen wie nötig. Wirddieses Muster auf den gleichen String angewandt, findet es nur daserste a-Zeichen.

Alternierung, Gruppierung und ReferenzierungDie Regexsyntax schließt spezielle Zeichen für Alternativen, grup-pierende Unterausdrücke und Referenzen auf vorangehende Unter-ausdrücke ein. Das |-Zeichen trennt Alternativen. Beispielsweisefindet /ab|cd|ef/ den String »ab« oder den String »cd« oder denString »ef«. Und /\d{3}|[a-z]{4}/ findet entweder drei Ziffern odervier Kleinbuchstaben.

Suchmuster mit regulären Ausdrücken definieren | 159

Page 172: 3868993886_Script

Beachten Sie, dass Alternativen von links nach rechts in Betrachtgezogen werden, bis ein Treffer gefunden wird. Passt die linkeAlternative, wird die rechte auch ignoriert, wenn sie zu einem»besseren« Treffer führen würde. Wird das Muster /a|ab/ auf denString »ab« angewandt, findet es also nur das erste Zeichen.

Klammern dienen in regulären Ausdrücken unterschiedlichen Zwe-cken. Ein Zweck ist die Gruppierung mehrerer Elemente zu einemUnterausdruck, damit diese Elemente von |, *, +, ? und so weiter alseine Einheit betrachtet werden können. Beispielsweise findet/java(script)?/ »java«, optional gefolgt von »script«. Und/(ab|cd)+|ef/ findet entweder »ef« oder ein oder mehrere Vorkom-men der Strings »ab« oder »cd«

Außerdem dienen Klammern zur Definition von Untermusterninnerhalb des gesamten Musters. Wenn der Vergleich eines regulä-ren Ausdrucks mit einem String erfolgreich war, kann man Teile desZielstrings abrufen, die von einem bestimmten in Klammern ste-henden Untermuster gefunden wurden. (Wie man sich diese gefun-denen Teilstrings beschafft, werden Sie weiter unten erfahren.)Nehmen Sie beispielsweise an, Sie suchen nach einem oder mehre-ren Kleinbuchstaben, auf die eine oder mehrere Ziffern folgen. Dazukönnten Sie das Muster /[a-z]+\d+/ nutzen. Aber was ist, wenn Siesich eigentlich nur für die Ziffern am Ende des Treffers interes-sieren? Wenn Sie diesen Teil des Musters in Klammern einschlie-ßen, also (/[a-z]+(\d+)/) schreiben, können Sie, wie später erläu-tert wird, die Ziffern aus allen Treffern abrufen, die Sie finden.

Eng verwandt damit ist die Verwendung von Unterausdrücken inKlammern, um später im gleichen regulären Ausdruck wieder aufsie zu verweisen. Das macht man mit einem \-Zeichen, auf das eineoder mehrere Ziffern folgen. Die Ziffern verweisen auf die Positiondes Unterausdrucks in Klammern im regulären Ausdruck. So be-zieht sich \3 beispielsweise auf den dritten Unterausdruck.

Eine Referenz auf einen vorangehenden Unterausdruck in einemregulären Ausdruck verweist nicht auf das Muster für diesen Unter-ausdruck, sondern auf den Text, der von diesem Unterausdruckgefunden wurde. Referenzen können also eingesetzt werden, um dieEinschränkung zu erzwingen, dass mehrere Teile eines Strings

160 | Kapitel 9: Reguläre Ausdrücke

Page 173: 3868993886_Script

genau die gleichen Zeichen enthalten. Der folgende reguläre Aus-druck findet beispielsweise ein oder mehrere Zeichen in einfachenoder doppelten Anführungszeichen, verlangt aber nicht, dass dasöffnende und schließende Anführungszeichen gleicher Art sind(d.h., dass beide einfache oder doppelte Anführungszeichen sind):

/['"][^'"]*['"]/

Dass die Anführungszeichen gleicher Art sind, können Sie mit einerReferenz fordern:

/(['"])[^'"]*\1/

Das \1 findet eben das, was der erste Unterausdruck in Klammernfand. In diesem Beispiel erzwingt die Referenz die Einschränkung,dass das schließende Anführungszeichen dem öffnenden entspricht.

Man kann in einem regulären Ausdruck auch Elemente gruppieren,ohne eine nummerierte Referenz auf sie zu erstellen. Statt mit (und)beginnen Sie die Gruppe einfach mit (?: und lassen sie mit ) enden.

Tabelle 9-4: Zeichen für Alternierung, Gruppierung und Referenzierung inregulären Ausdrücken

Zeichen Bedeutung

| Alternierung. Findet entweder den Unterausdruck auf der linken Seite oder denauf der rechten.

(...) Gruppierung. Gruppiert Elemente zu einer Einheit, auf die *, +, ?, | und soweiter angewendet werden können. Speichert außerdem die Zeichen, die vondieser Gruppe gefunden wurden, damit sie später mit Referenzen wieder-verwendet werden können.

(?:...) Nur Gruppierung. Gruppiert Elemente zu einer Einheit, speichert die von derGruppe gefundenen Zeichen aber nicht.

\ n Findet die gleichen Zeichen wie die, die Gruppe n zuvor fand. Gruppen sindUnterausdrücke in (eventuell auch geschachtelten) Klammern. Die Gruppen-nummern basieren auf der Abfolge der öffnenden Klammer von links nachrechts. Gruppen, die mit (?: gebildet werden, werden nicht gezählt.

Die Position des Treffers angebenWie bereits beschrieben wurde, finden viele Elemente eines regulä-ren Ausdrucks ein einzelnes Zeichen in einem String. Beispielsweisefindet \s ein einzelnes Whitespace-Zeichen. Andere Elemente von

Suchmuster mit regulären Ausdrücken definieren | 161

Page 174: 3868993886_Script

regulären Ausdrücken finden Positionen zwischen Zeichen anstellevon Zeichen selbst. \b findet beispielsweise eine Wortgrenze – dieGrenze zwischen einem \w- (ASCII-Wortzeichen) und einem\W-Zeichen (Nicht-Wortzeichen) oder die Grenze zwischen einemASCII-Wortzeichen und dem Stringanfang oder -ende. Elementewie \b geben keine Zeichen an, die im geprüften String vorkommenmüssen. Stattdessen geben sie die Positionen an, an denen einTreffer auftreten kann. Gelegentlich werden derartige Elemente alsAnker bezeichnet, weil sie das Muster an einer bestimmten Positionim Zielstring verankern. Die am häufigsten genutzten Ankerele-mente sind ^, das das Muster an den Stringanfang bindet, und $,das das Muster am Stringende verankert.

Wollen Sie das allein auf einer Zeile stehende Wort »JavaScript«finden, können Sie beispielsweise den regulären Ausdruck /^Java-Script$/ nutzen. Wollen Sie die Zeichenfolge »Java« als eigenstän-diges Wort finden (nicht als Präfix wie in »JavaScript«), können Siees mit dem Muster /\sJava\s/ versuchen, das jeweils ein Whitespa-ce-Zeichen vor und nach dem Wort verlangt. Diese Lösung birgtaber zwei Probleme. Zunächst findet sie »Java« weder am Anfangnoch am Ende eines Strings, sondern nur, wenn es mit einemLeerraumzeichen auf beiden Seiten erscheint. Zweitens hat derString, den dieses Muster liefert, wenn es einen Treffer findet, anseinem Anfang und Ende Whitespace-Zeichen, was vermutlichnicht Ihren Anforderungen entspricht. Statt mit \s nach dem tat-sächlichen Whitespace-Zeichen zu suchen, können Sie mit \b Wort-grenzen suchen. Der Ausdruck lautet dann /\bJava\b/. Das Ele-ment \B verankert den Treffer an einer Position, die keineWortgrenze ist. Das Muster /\B[Ss]cript/ findet also »JavaScript«und »postscript«, aber nicht »script« oder »Scripting«.

Tabelle 9-5 fasst die Ankerzeichen für reguläre Ausdrücke zusam-men.

Tabelle 9-5: Ankerzeichen für reguläre Ausdrücke

Zeichen Bedeutung

^ Findet den Stringanfang oder in zeilenübergreifenden Suchen einen Zeilen-anfang.

$ Findet das Stringende oder in zeilenübergreifenden Suchen ein Zeilenende.

162 | Kapitel 9: Reguläre Ausdrücke

Page 175: 3868993886_Script

Tabelle 9-5: Ankerzeichen für reguläre Ausdrücke (Fortsetzung)

Zeichen Bedeutung

\b Findet eine Wortgrenze. Das heißt, der Ausdruck findet die Position zwischeneinem \w- und einem \W-Zeichen oder zwischen einem \w-Zeichen und demAnfang oder dem Ende des Strings. (Beachten Sie jedoch, dass [\b] einenBackspace findet.)

\B Findet eine Position, die keine Wortgrenze ist.

(?=p) Ein positiver Lookahead. Verlangt, dass die nachfolgenden Zeichen dem Musterp entsprechen, schließt diese Zeichen aber nicht in den Treffer ein.

(?!p) Ein negativer Lookahead. Verlangt, dass die nachfolgenden Zeichen demMuster p nicht entsprechen.

SchalterEs gibt noch ein weiteres Element der Regexsyntax. Regexschaltergeben übergeordnete Regeln für den Mustervergleich an. Im Gegen-satz zur übrigen Syntax für reguläre Ausdrücke, werden diese Schal-ter rechts vom zweiten Schrägstrich angegeben. JavaScript unter-stützt drei Schalter. Der Schalter i gibt an, dass der Mustervergleichohne Berücksichtigung von Groß-/Kleinschreibung erfolgen soll. DerSchalter g gibt an, dass der Mustervergleich global ist – d.h., dass alleTreffer im durchsuchten String gefunden werden sollen. Der Schalterm gibt an, dass der Mustervergleich zeilenübergreifend (d.h. mehr-zeilig) sein soll. In diesem Modus finden die Anker ^ und $ in Strings,die Zeilenumbruchzeichen enthalten, außer dem Stringanfang und-ende auch noch den Zeilenanfang und das Zeilenende. Diese Schal-ter können in beliebiger Kombination angegeben werden. Beispiels-weise passt das Muster /java$/im auf »java« und »Java\ nist gut«.

Tabelle 9-6 fasst diese Schalter für reguläre Ausdrücke zusammen.Weiter unten werden Sie mehr zum Schalter g sehen.

Tabelle 9-6: Regexschalter

Zeichen Bedeutung

i Einen Vergleich durchführen, der die Groß-/Kleinschreibung ignoriert.

g Einen globalen Vergleich durchführen, d.h., alle Treffer finden und nicht nachdem ersten Treffer abbrechen.

m Zeilenübergreifender Modus. ^ findet den Anfang einer Zeile oder eines Stringsund $ das Ende einer Zeile oder eines Strings.

Suchmuster mit regulären Ausdrücken definieren | 163

Page 176: 3868993886_Script

Mustervergleiche mit regulärenAusdrückenDieser Abschnitt betrachtet Methoden des String- und RegExp-Ob-jekts, die reguläre Ausdrücke nutzen, um Mustervergleiche undSuchen-und-ersetzen-Operationen durchzuführen.

Stringmethoden für MustervergleicheStrings bieten vier Methoden, die reguläre Ausdrücke nutzen. Dieeinfachste davon ist search(). Diese Methode nimmt einen regulä-ren Ausdruck als Argument und liefert entweder die Zeichenposi-tion des Anfangs des ersten passenden Teilstrings oder –1, wenn eskeinen Treffer gibt. Der folgende Aufruf liefert zum Beispiel 4:

"JavaScript".search(/script/i);

search() unterstützt keine globale Suche und ignoriert einen even-tuell im Regexargument enthaltenen g-Schalter.

Die Methode replace() führt eine Suchen-und-ersetzen-Operationdurch. Sie nimmt einen regulären Ausdruck als erstes Argumentund einen Ersetzungsstring als zweites. Sie durchsucht den String,auf dem sie aufgerufen wurde, nach Treffern für das angegebeneMuster. Enthält der reguläre Ausdruck den Schalter g, ersetzt re-place() alle Treffer im String durch den angegebenen Ersetzungs-string, andernfalls ersetzt es nur den ersten gefundenen Treffer. Istdas erste Argument für replace() ein String und kein regulärerAusdruck, sucht die Methode nach wörtlichen Vorkommen desStrings und wandelt den String nicht wie search() mit dem Reg-Exp()-Konstruktor in einen regulären Ausdruck um. Beispielsweisekönnen Sie replace() folgendermaßen nutzen, um eine einheitlicheGroß-/Kleinschreibung des Worts »JavaScript« in einem Text zuerhalten:

text.replace(/javascript/gi, "JavaScript");

replace() hat allerdings noch mehr zu bieten. Erinnern Sie sichdaran, dass Unterausdrücke in Klammern von links nach rechtsnummeriert werden und dass der von ihnen gefundene Text gespei-chert wird. Wenn im Ersetzungsstring ein $, gefolgt von einer Ziffer,

164 | Kapitel 9: Reguläre Ausdrücke

Page 177: 3868993886_Script

vorkommt, ersetzt replace() diese beiden Zeichen durch den Text,der vom referenzierten Unterausdruck gefunden wurde. Sie könnendieses Feature beispielsweise nutzen, um bei einem Zitat geradeAnführungszeichen durch typografische Anführungszeichen zu er-setzen, die hier mit ASCII-Zeichen simuliert werden:

// Ein Zitat besteht aus einem Anfuhrungszeichen, einer// beliebigen Folge von Zeichen, die keine// Anfuhrungszeichen sind (die wir speichern lassen), und// einem weiteren Anfuhrungszeichen.var quote = /"([^"]*)"/g;// Die geraden Anfuhrungszeichen durch typografische// ersetzen und das (in $1 gespeicherte) Zitat unverandert// lassen.text.replace(quote, '»$1”');

Das zweite Argument für replace() kann auch eine Funktion sein,die den Ersetzungsstring dynamisch ermittelt. Wenn Sie eine Funk-tion übergeben, wird diese für jeden Treffer einmal aufgerufen. Daserste Argument der Funktion ist hierbei der Text des gefundenenStrings. Die folgenden Argumente enthalten jeweils den Text, derauf die einzelnen Unterausdrücke (in runden Klammern) des Mus-ters passt. Der Rückgabewert der Funktion wird als Ersetzungs-string verwendet.

Die match()-Methode ist die allgemeinste der Regex-bezogenenMethoden von String. Ihr einziges Argument ist ein regulärer Aus-druck, und sie liefert ein Array, das die Ergebnisse des Vergleichsenthält. Wenn für den regulären Ausdruck der Schalter g gesetzt ist,liefert die Methode ein Array, das alle Treffer enthält, die im Stringvorkommen. Zum Beispiel:

"1 plus 2 equals 3".match(/\d+/g) // => ["1","2","3"]

Wenn der Schalter g für den regulären Ausdruck nicht gesetzt ist,führt match() keine globale Suche durch und liefert einfach denersten Treffer. Aber match() liefert auch dann ein Array, wenn keineglobale Suche durchgeführt wird. In diesem Fall ist das erste Ele-ment der gefundene String. Die weiteren Elemente enthalten dieSubstrings, die von möglichen Unterausdrücken in runden Klam-mern gefunden wurden. Vergleicht man das mit der replace()-Me-thode, enthält a[n] also den Inhalt von $n.

Mustervergleiche mit regulären Ausdrücken | 165

Page 178: 3868993886_Script

Betrachten Sie beispielsweise den folgenden Code zum Parsen einerURL:

var url = /(\w+):\/\/([\w.]+)\/(\S*)/;var text = "Visit http://www.example.com/~david";var result = text.match(url);if (result != null) {

var fullurl = result[0]; // => Der ganze Eintragvar protocol = result[1]; // => "http"var host = result[2]; // => "www.example.com"var path = result[3]; // => "~david"

}

Die letzte Regex-bezogene Methode des String-Objekts ist split().Diese Methode zerlegt den String, auf dem sie aufgerufen wird, inein Array mit Teilstrings und nutzt dabei ihr Argument als Trenn-zeichen. Zum Beispiel:

"123,456,789".split(","); // => ["123","456","789"]

Die split()-Methode kann auch einen regulären Ausdruck alsArgument annehmen. Diese Fähigkeit macht die Methode mächti-ger. Beispielsweise können Sie so ein Trennzeichen angeben, daseine beliebige Anzahl von Whitespace-Zeichen auf beiden Seitengestattet:

"1 , 2,3".split(/\s*,\s*/); // => ["1","2","3"]

RegExp-Eigenschaften und -MethodenJedes RegExp-Objekt hat fünf Eigenschaften. Die source-Eigen-schaft enthält den regulären Ausdruck selbst. Die Eigenschaft glo-bal gibt an, ob für den regulären Ausdruck der Schalter g gesetzt ist.Die Eigenschaft ignoreCase gibt an, ob für den regulären Ausdruckder Schalter i gesetzt ist. Die Eigenschaft multiline gibt an, ob fürden regulären Ausdruck der Schalter m gesetzt ist. Die letzte Eigen-schaft ist lastIndex, eine les- und schreibbare ganze Zahl. BeiMustern, bei denen der Schalter g gesetzt ist, speichert diese Eigen-schaft die Position im String, an der die nächste Suche beginnensoll. Sie wird von den Methoden exec() und test() genutzt, dieunten beschrieben werden.

166 | Kapitel 9: Reguläre Ausdrücke

Page 179: 3868993886_Script

RegExp-Objekte definieren zwei Methoden, die Mustervergleichs-operationen durchführen und sich ähnlich verhalten wie die zuvorbeschriebenen Stringmethoden. Die wichtigste Mustervergleichs-methode von RegExp ist exec(). Sie ähnelt der Stringmethodematch(), die im Abschnitt »Mustervergleiche mit regulären Ausdrü-cken« auf Seite 164 beschrieben wurde, erwartet aber einen Stringals Argument, kein RegExp-Objekt wie die Stringmethode. Dieexec()-Methode führt einen regulären Ausdruck auf dem angegebe-nen String aus. Das heißt, sie durchsucht den String nach einemTreffer. Findet sie keinen, liefert sie null. Findet sie einen, liefert sieein Array, das dem entspricht, das match() bei nicht-globalen Ver-gleichen liefert. Das Element 0 des Arrays enthält den String, dervom regulären Ausdruck gefunden wurde, und alle nachfolgendenElemente enthalten Teilstrings, die von Unterausdrücken in Klam-mern gefunden wurden. Außerdem enthält die index-Eigenschaftdie Zeichenposition, an der der Treffer gefunden wurde, und dieinput-Eigenschaft enthält den String, der durchsucht wurde.

Im Unterschied zu match() liefert exec() ein Array der gleichen Art,wenn der reguläre Ausdruck global ist, d.h., wenn für ihn derSchalter g gesetzt ist. Erinnern Sie sich daran, dass die Methodematch() ein Array mit den Treffern liefert, wenn ihr ein globalerregulärer Ausdruck übergeben wird. exec() hingegen liefert immernur einen Treffer und bietet vollständige Informationen zu diesemeinen Treffer. Wenn exec() auf einem gewöhnlichen regulärenAusdruck aufgerufen wird, für den g gesetzt ist, wird die lastIndex-Eigenschaft des RegExp-Objekts auf die Zeichenposition unmittel-bar nach dem gefundenen Teilstring gesetzt. Wenn die exec()-Me-thode ein zweites Mal für den gleichen regulären Ausdruckaufgerufen wird, beginnt sie die Suche bei der durch die EigenschaftlastIndex angegebenen Position. Findet exec() keinen Treffer, wirdlastIndex auf 0 zurückgesetzt. (Sie können lastIndex auch jederzeitselbst setzen.) Dieses spezielle Verhalten erlaubt es Ihnen, exec()mehrfach in einer Schleife aufzurufen, um alle Treffer für einenregulären Ausdruck in einem String zu durchlaufen.

Zum Beispiel:

var pattern = /Java/g;var text = "JavaScript macht mehr Spaß als Java!";

Mustervergleiche mit regulären Ausdrücken | 167

Page 180: 3868993886_Script

var result;while((result = pattern.exec(text)) != null) {

alert("Gefunden: '" + result[0] + "'" +" Position: " + result.index +"; Nachste Suche: " + pattern.lastIndex);

}

Die andere RegExp-Methode ist test(). test() ist eine erheblicheinfachere Methode als exec(). Sie erwartet einen String und lieferttrue, wenn der String einen Treffer für den regulären Ausdruckenthält:

var pattern = /java/i;pattern.test("JavaScript"); // Liefert true

Ein Aufruf von test() ist äquivalent zu einem Aufruf von exec(), zudem true geliefert wird, wenn der Rückgabewert von exec() nichtnull ist. Aufgrund dieser Äquivalenz verhält sich test() bei einemAufruf auf einem globalen regulären Ausdruck genauso wie exec().Die Suche im übergebenen String beginnt bei der Position, die vonlastIndex angegeben wird. Wird ein Treffer gefunden, wird last-Index auf die Position des Zeichens gesetzt, das unmittelbar auf denTreffer folgt. Sie können einen String mit test() also genausodurchlaufen wie mit exec().

168 | Kapitel 9: Reguläre Ausdrücke

Page 181: 3868993886_Script

KAPITEL 10

Clientseitiges JavaScript

Der erste Teil dieses Buchs hat den Kern der Sprache JavaScriptbeschrieben. In diesem Teil geht es nun um JavaScript innerhalbvon Webbrowsern, was oft als clientseitiges JavaScript bezeichnetwird. Die meisten der bisher vorgestellten Beispiele entsprechenzwar gültigem JavaScript-Code, haben aber keinen besonderenKontext – es handelt sich um JavaScript-Fragmente, denen ihreUmgebung relativ egal ist. In diesem Kapitel wird dieser Kontexthergestellt. In den folgenden Kapiteln beschäftigen wir uns dannmit den Details.

JavaScript in HTML einbettenJavaScript-Code kann in einer HTML-Datei inline zwischen denTags <script> und </script> erscheinen:

<script>// Hier steht Ihr JavaScript-Code</script>

Beispiel 10-1 ist eine HTML-Datei, in der sich ein einfaches Java-Script-Programm befindet. Die Kommentare beschreiben, was dasProgramm tut, aber entscheidend an diesem Beispiel ist, dass eszeigt, wie JavaScript-Code in einer HTML-Datei zusammen miteinem CSS-Stylesheet eingebettet wird.

Beispiel 10-1: Eine einfache Digitaluhr in JavaScript

<!DOCTYPE html> <!-- Dies ist eine HTML5-Datei --><html> <!-- Das Wurzelelement --><head> <!-- Titel, Skripten und Styles gehoren hierhin -->

| 169

Page 182: 3868993886_Script

<title>Digital ClockDigitaluhr</title><script> // Ein Skript mit JS-Code// Funktion zum Anzeigen der aktuellen Uhrzeit definierenfunction displayTime() {

var now = new Date(); // Aktuelle Uhrzeit holen// Element mit id="clock" findenvar elt = document.getElementById("clock");// Durch elt anzeigen lassenelt.innerHTML = now.toLocaleTimeString();// In 1 Sekunde erneut ausfuhrensetTimeout(displayTime, 1000);

}// Mit dem Anzeigen der Uhrzeit beim Laden des Dokuments// beginnen.window.onload = displayTime;</script><style> /* Ein CSS-Stylesheet fur die Uhrzeit */#clock {/* Style wird auf das Element mit id="clock" angewendet */

font: bold 24pt sans; /* große, fette Schrift nutzen */background: #ddf; /* hellgrauer Hintergrund */padding: 10px; /* etwas Platz drumherum lassen */border: solid black 2px; /* mit Rahmen */border-radius: 10px; /* Ecken abrunden */

}</style></head><body> <!-- Der Rumpf ist der Teil des Dokuments, der -->

<!-- angezeigt wird. --><h1>Digital Clock</h1> <!-- Uberschrift anzeigen --><span id="clock"></span> <!-- Die Uhrzeit wird hier eingefugt --></body></html>

Das <script>-Tag kann auch mit einem src-Attribut benutzt wer-den, das eine URL einer Datei angibt, die JavaScript-Code enthält.Es wird so verwendet:

<script src="../../scripts/util.js"></script>

Eine JavaScript-Datei enthält reines JavaScript ohne <script>-Tagsoder anderes HTML. Üblicherweise besitzen Dateien mit Java-Script-Code Namen, die auf .js enden.

Ein <script>-Tag, für das das Attribut src angegeben ist, verhältsich genau so, als stünde der Inhalt der angegebenen JavaScript-

170 | Kapitel 10: Clientseitiges JavaScript

Page 183: 3868993886_Script

Datei direkt zwischen den <script>- und </script>-Tags. BeachtenSie, dass das schließende </script>-Tag in HTML-Dokumentenauch erforderlich ist, wenn das Attribut src angegeben wird und eskeinen weiteren Inhalt zwischen den <script>- und </script>-Tagsgibt.

JavaScript war die erste Skriptsprache für das Web, und <script>-Elemente gehen standardmäßig davon aus, dass sie JavaScript-Codeenthalten oder darauf verweisen. <script>-Elemente besitzen eintype-Attribut, das den Standardwert »text/javascript« hat. Sie kön-nen diesen Typ auch explizit angeben, aber das ist nie notwendig.

Event-gesteuerte ProgrammierungClientseitige JavaScript-Programme sind normalerweise Event-ge-steuert. Üblicherweise werden beim Laden der Seite bestimmteVariablen initialisiert und verschiedene Event-Handler-Funktionenregistriert. Diese Funktionen werden dann vom Browser aufgeru-fen, wenn die Events auftreten, für die sie registriert wurden. EineWebanwendung, die Tastenkürzel für häufig benötigte Aktionenermöglichen möchte, würde zum Beispiel einen Event-Handler fürTastatur-Events registrieren. Selbst nicht-interaktive Programmenutzen Events. Stellen Sie sich vor, dass Sie ein Programm schreibenwollen, das die Struktur seines Dokuments analysiert und auto-matisch ein Inhaltsverzeichnis für das Dokument erstellt. Es sindkeine Event-Handler für Benutzereingaben notwendig, aber dasProgramm würde immer noch einen onload-Event-Handler regis-trieren, damit es weiß, wann das Dokument fertig geladen wurdeund dass nun ein Inhaltsverzeichnis dafür erzeugt werden kann.

Events und Event-Handler sind Thema von Kapitel 12.

Das Window-ObjektDas Window-Objekt ist der Haupteinstiegspunkt für alle clientsei-tigen JavaScript-Features und APIs. Es repräsentiert ein Fensteroder einen Rahmen im Webbrowser, und Sie können es über seinenBezeichner window ansprechen. Das Window-Objekt definiert Ei-genschaften wie location, die sich auf ein Location-Objekt mit der

Das Window-Objekt | 171

Page 184: 3868993886_Script

aktuell im Fenster angezeigten URL bezieht und mit der ein Skripteine neue URL laden kann:

// location setzen, um zu einer neuen// Webseite zu navigieren.window.location = "http://www.oreilly.de/";

Das Window-Objekt definiert Methoden wie alert(), die eineNachricht in einem Dialogfenster anzeigt, und setTimeout(), dieeine Funktion registriert, die nach einer bestimmten Zeit aufgerufenwird:

// 2 Sekunden warten und dann Hallo sagen.setTimeout(function() { alert("Hallo"); }, 2000);

Beachten Sie, dass dieser Code die Eigenschaft window gar nichtexplizit nutzt. In clientseitigem JavaScript ist das Window-Objektgleichzeitig auch das globale Objekt. Das bedeutet: Das Window-Objekt befindet sich ganz oben in der Geltungsbereichskette, undseine Eigenschaften und Methoden sind im Endeffekt globale Varia-blen und globale Funktionen. Das Window-Objekt besitzt eineEigenschaft namens window, die immer auf sich selbst verweist. Siekönnen diese Eigenschaft nutzen, wenn Sie auf das Window-Objektselbst verweisen müssen. Das ist im Allgemeinen aber nicht not-wendig, wenn Sie nur auf die Eigenschaften des globalen Window-Objekts zugreifen wollen.

Als globales Objekt definiert Window eine Reihe von Eigenschaftenund Methoden für die clientseitige JavaScript-Programmierung. Diewichtigste ist die document-Eigenschaft, mit der wir uns in Kapitel 11beschäftigen werden. Die übrigen Eigenschaften und Methodenbehandeln wir in den folgenden Abschnitten.

TimersetTimeout() und setInterval() ermöglichen es Ihnen, eine Funk-tion zu registrieren, damit sie nach einer gewissen Zeitspanneeinmalig oder wiederholt aufgerufen wird. Diese beiden Funktionensind für das clientseitige JavaScript wichtig und werden daher alsMethoden von Window definiert, aber es handelt sich um allgemeinnutzbare Funktionen, die eigentlich nichts mit dem Fenster zu tunhaben.

172 | Kapitel 10: Clientseitiges JavaScript

Page 185: 3868993886_Script

Die setTimeout()-Methode des Window-Objekts sorgt dafür, dasseine Funktion nach einer angegebenen Zahl von Millisekundenausgeführt wird. setTimeout() gibt einen Wert zurück, der anclearTimeout() übergeben werden kann, um die Ausführung derregistrierten Funktion abzubrechen.

Wenn Sie setTimeout() mit einer Zeitspanne von 0 ms aufrufen,wird die angegebene Funktion nicht sofort aufgerufen. Stattdessenlandet sie in einer Queue, um »sobald wie möglich« aufgerufen zuwerden, nachdem noch abzuarbeitende Event-Handler fertig sind.

setInterval() ist wie setTimeout(), nur dass die angegebene Funk-tion wiederholt nach der gewünschten Zahl von Millisekundenausgeführt wird:

// Rufe updateClock() alle 60 Sekunden auf.setInterval(updateClock, 60000);

Wie setTimeout() liefert auch setInterval() einen Wert zurück, deran clearInterval() übergeben werden kann, um weitere Aufrufeder registrierten Funktion zu verhindern.

Browser-Location und NavigationDie Eigenschaft location des Window-Objekts referenziert auf einLocation-Objekt, das wiederum für die aktuelle URL des im Fensterangezeigten Dokuments steht und das Methoden definiert, um imFenster ein neues Dokument zu laden.

Die location-Eigenschaft eines Fensters ist eine Referenz auf einLocation-Objekt. Es repräsentiert die aktuelle URL des Dokuments,das in diesem Fenster angezeigt wird. Die href-Eigenschaft desLocation-Objekts ist ein String, der den vollständigen Text derURL enthält. Die toString()-Methode des Location-Objekts liefertden Wert der href-Eigenschaft zurück, sodass Sie statt loca-tion.href auch einfach location nutzen können.

Andere Eigenschaften dieses Objekts – protocol, host, hostname,port, pathname, search und hash – geben die verschiedenen einzel-nen Bestandteile der URL an. Sie sind als »URL-Dekompositi-ons«-Eigenschaften bekannt und werden auch von Link-Objekten

Das Window-Objekt | 173

Page 186: 3868993886_Script

unterstützt (die in HTML-Dokumenten durch Elemente vom Typ<a> und <area> erzeugt werden).

Das Location-Objekt definiert auch noch reload(), die dafür sorgt,dass der Browser das Dokument neu lädt.

Mit dem Location-Objekt können Sie auch dafür Sorgen, dass derBrowser zu einer neuen Seite navigiert: Weisen Sie hierfür derEigenchaft location einfach einen neuen Wert zu:

location = "http://www.oreilly.de";

Sie können location auch relative URLs zuweisen. Diese werdenvon der aktuellen URL ausgehend ausgewertet:

location = "page2.html"; // Nachste Seite

Ein reiner Fragment-Text wird als besondere Art einer relativenURL interpretiert. Der Browser lädt kein neues Dokument in denBrowser, sondern scrollt an eine andere Stelle des aktuellen Doku-ments. Die Kennung #top hat eine besondere Bedeutung: Besitztkein Dokument-Element die ID »top«, springt der Browser an denAnfang:

location = "#top";

Man kann auf die URL-Dekompositions-Eigenschaften des Loca-tion-Objekts schreibend zugreifen. Dadurch wird die Location-URLgeändert, und der Browser lädt ein neues Dokument (oder navigiert– bei der hash-Eigenschaft – im aktuellen Dokument):

location.search = "?page=" + (pagenum+1);

Browser-VerlaufDie history-Eigenschaft des Window-Objekts referenziert auf dasHistory-Objekt des Fensters. Das History-Objekt modelliert denBrowser-Verlauf eines Fensters als Liste von Dokumenten undDokument-Status.

Das History-Objekt besitzt back()- und forward()-Methoden, diesich so wie die Zurück- und Vorwärts-Buttons des Browsers ver-halten: Sie sorgen dafür, dass der Browser in seiner Verlaufslisteeinen Schritt rückwärts oder vorwärts geht. Eine dritte Methode

174 | Kapitel 10: Clientseitiges JavaScript

Page 187: 3868993886_Script

go() erwartet ein ganzzahliges Argument und kann dann in derVerlaufsliste beliebig viele Seiten vorwärts (für positive Argumente)oder rückwärts (für negative Argumente) springen:

history.go(-2); // 2 zuruckgehen, so als ob der Zuruck-Button// zweimal angeklickt wurde.

Enthält ein Fenster Unterfenster (wie zum Beispiel <iframe>-Ele-mente – siehe den Abschnitt »Beziehungen zwischen Frames« aufSeite 179), ist der Browser-Verlauf der Unterfenster chronologischmit dem Verlauf des Hauptfensters verbunden. Ruft man also zumBeispiel history.back() für das Hauptfenster auf, navigiert eventu-ell erst eines der Unterfenster zurück zu einem der vorher ange-zeigten Dokumente, während das Hauptfenster seinen aktuellenStatus beibehält.

Moderne Webanwendungen können ihren eigenen Inhalt dyna-misch verändern, ohne ein neues Dokument zu laden. SolcheAnwendungen werden vielleicht dem Anwender erlauben wollen,die Zurück- und Vorwärts-Buttons zu nutzen, um zwischen diesendynamisch erzeugten Anwendungszuständen wechseln zu können.Das geht beispielsweise, indem Sie location.hash einen Stringzuweisen, der den aktuellen Zustand der Anwendung speichert.Obwohl hierbei kein neues Dokument geladen wird, wird ein neuerHistory-Eintrag angelegt. Benutzt der Benutzer später den Zurück-Button, löst der Browser ein »hashchange«-Event aus. Soll dieBenutzung der Vor- und Zurück-Buttons in der Anwendung mit-verfolgt werden, kann hierfür per window.onhashchange ein entspre-chender Handler angelegt werden.

Eine weitere, allerdings etwas komplizierte Möglichkeit zur Verwal-tung des Browserverlaufs besteht in der Verwendung der his-tory.pushState()-Methode und dem dazugehörigen Event-Handlerwindow.onpopstate. Für eine umfassende Behandlung dieser APIreicht der Platz in diesem Buch jedoch nicht aus.

Browser- und Bildschirm-InformationenSkripten müssen sich manchmal auch Informationen zum Web-browser verschaffen, in dem sie laufen, oder zum Desktop, auf demder Browser zu sehen ist. Dieser Abschnitt beschreibt die Eigen-

Das Window-Objekt | 175

Page 188: 3868993886_Script

schaften navigator und screen des Window-Objekts, die sich aufNavigator- und Screen-Objekte beziehen. Diese Informationen er-möglichen es einem Skript, sein Verhalten auf Basis seiner Umge-bung anzupassen.

Die navigator-Eigenschaft eines Window-Objekts verweist auf einNavigator-Objekt, das Informationen zum Browserhersteller undzur Versionsnummer enthält. Das Navigator-Objekt hat seinenNamen vom früheren Netscape Navigator, wird aber auch von allenanderen Browsern unterstützt.

Das Navigator-Objekt besitzt vier Eigenschaften, aus denen SieInformationen über den genutzten Browser ermitteln können:

appNameDer vollständige Name des Webbrowsers. Beim IE ist das»Microsoft Internet Explorer«. Beim Firefox steht hier »Net-scape«. Aus Kompatibilitätsgründen melden hier andere Brow-ser auch häufig »Netscape«.

appVersionDiese Eigenschaft beginnt üblicherweise mit einer Zahl, gefolgtvon einem Text, der den Browserhersteller und Versionsinfor-mationen enthält. Die Zahl am Beginn dieses Strings ist häufig4.0 oder 5.0, um die allgemeine Kompatibilität zu Browsern dervierten oder fünften Generation widerzuspiegeln. Es gibt keinStandardformat für den String appVersion, daher lässt er sichauch nicht unabhängig vom Browser parsen.

userAgentDer String, den der Browser in seinem USER-AGENT-HTTP-Hea-der sendet. Diese Eigenschaft enthält üblicherweise alle Infor-mationen in appName und appVersion und kann oft noch weitereInformationen enthalten. Wie bei appVersion gibt es allerdingskeine Standardformatierung.

platformEin String, der das Betriebssystem (und eventuell die Hardware)beschreibt, in dem (und auf der) der Browser läuft.

Neben den Eigenschaften zum Browserhersteller und zur Versions-nummer besitzt das Navigator-Objekt noch weitere Eigenschaften

176 | Kapitel 10: Clientseitiges JavaScript

Page 189: 3868993886_Script

und Methoden. Zu den standardisierten und nicht-standardisierten,aber im Allgemeinen implementierten Eigenschaften gehören:

onLineDie navigator.onLine-Eigenschaft gibt an (sofern vorhanden),ob der Browser aktuell mit dem Netz verbunden ist.

geolocationEin Geolocation-Objekt mit einer API, über die die geografischePosition des Anwenders ermittelt werden kann. Eine detaillierteBehandlung dieser API würde den Rahmen dieses Buchesjedoch sprengen.

Die screen-Eigenschaft eines Window-Objekts verweist auf einScreen-Objekt, das Informationen zur Größe der Anzeige des Bild-schirms enthält. Die Eigenschaften width und height geben dieGröße der Anzeige in Pixeln an. Die Eigenschaften availWidth undavailHeight geben die tatsächlich verfügbare Anzeigegröße an. Sieschließen den Raum aus, der von Elementen wie einer Desktop-Taskleiste eingenommen wird. Sie können das Screen-Objekt nut-zen, um herauszubekommen, ob Ihre Webapp auf einem Gerät mitwenig Platz auf dem Bildschirm läuft wie beispielsweise ein Tablet-Computer oder ein Mobiltelefon.

DialogfensterDas Window-Objekt stellt drei Methoden bereit, um einfache Dia-logfenster anzuzeigen. Mit alert() wird dem Benutzer eine Mel-dung angezeigt, und dann wird darauf gewartet, dass er das Fensterschließt. confirm() zeigt eine Meldung an, wartet darauf, dass derAnwender auf OK oder Abbrechen klickt, und gibt dann einenbooleschen Wert zurück. Und prompt() gibt eine Meldung aus, war-tet darauf, dass der Anwender einen Text eingibt, und gibt diesenString zurück. Der folgende Code nutzt alle drei Methoden:

do {// Einen String anfordern.var n = prompt("Wie heißen Sie?");// Nach Bestatigung fragen.var ok = confirm("Ist " + n + " okay?");

} while(!ok)alert("Hallo, " + n); // Eine Begrußung ausgeben.

Das Window-Objekt | 177

Page 190: 3868993886_Script

Auch wenn die Methoden alert(), confirm() und prompt() einfachzu nutzen sind, sollten sie (wenn überhaupt) nur zurückhaltend ein-gesetzt werden. Solche Dialogfenster sind im Web nicht sehr häufigim Einsatz, und die meisten Anwender werden das Gefühl haben,dass die Fenster nicht zum restlichen Erscheinungsbild der Websei-ten passen.

Document-Elemente als Window-EigenschaftenWenn Sie ein Element in Ihrem HTML-Dokument über sein id-Attribut benennen, erhält das Window-Objekt eine nicht enume-rierbare Eigenschaft, deren Name der Wert des id-Attributs undderen Wert das HTMLElement-Objekt ist, das dieses Document-Element repräsentiert.

Wie wir schon festgestellt haben, dient das Window-Objekt inclientseitigem JavaScript als globales Objekt. Das bedeutet hier also,dass die id-Attribute, die Sie in Ihren HTML-Dokumenten nutzen,zu globalen Variablen werden, die Sie in Ihren Skripten ansprechenkönnen (sofern es noch keine Variablen dieses Namens gibt). BesitztIhr Dokument zum Beispiel das Element <button id="okay"/>,können Sie es einfach über die globale Variable okay ansprechen.

Die implizite Verwendung von Element-IDs als globale Variablen isteine historische Marotte der Webbrowser-Entwicklung. Sie ist ausGründen der Abwärtskompatibilität für bestehende Webseiten not-wendig, aber ihre Nutzung wird nicht empfohlen. Stattdessen soll-ten Sie für die Lokalisierung bestimmter Elemente die in Kapitel 11besprochenen Techniken verwenden.

Mehrere Fenster und FramesEin einzelnes Webbrowser-Fenster kann auf Ihrem Desktop vieleTabs enthalten. Jeder Tab ist ein unabhängiger Browsing Contextmit seinem eigenen Window-Objekt, der von allen anderen Tabsgetrennt ist. Die Skripten, die in einem Tab laufen, haben normaler-weise nicht einmal die Chance, von den anderen Tabs zu wissen,und sie können erst recht nicht mit deren Window-Objekten inter-agieren oder deren Dokument-Inhalte verändern. Wenn Sie einen

178 | Kapitel 10: Clientseitiges JavaScript

Page 191: 3868993886_Script

Webbrowser nutzen, der keine Tabs unterstützt, oder wenn Sie dieTabs abgeschaltet haben, haben Sie vielleicht eine ganze Reihe vonWebbrowser-Fenstern gleichzeitig geöffnet. Wie bei Tabs besitztauch jedes Desktop-Fenster sein eigenes Window-Objekt, das imAllgemeinen unabhängig und isoliert von allen anderen ist.

HTML-Dokumente enthalten eventuell eingebettete Dokumente,die mithilfe eines <iframe>-Elements eingebaut wurden. Ein<iframe> erstellt einen eingebetteten Browsing Context, der durchein eigenes Window-Objekt repräsentiert wird. Die veralteten Ele-mente <frameset> und <frame> erstellen einen ebenfalls eingebette-ten Browsing Context, und jedes <frame> wird durch ein Windowrepräsentiert. Clientseitiges JavaScript unterscheidet nur sehr wenigzwischen Fenstern, Tabs, IFrames und Frames: Alle haben einenBrowsing Context, und für JavaScript handelt es sich immer umWindow-Objekte. Verschachtelte Browsing Contexts sind nicht sovoneinander isoliert, wie das bei unabhängigen Tabs im Allgemei-nen der Fall ist. Ein Skript, das in einem Frame läuft, kann immerseinen übergeordneten Frame und seine abhängigen Frames sehen,auch wenn die Same-Origin-Policy (die »Gleiche-Herkunft-Richt-linie«, die in Abschnitt »Die Same-Origin-Policy« auf Seite 183beschrieben ist) eventuell verhindert, dass es auf die Dokumente indiesen Frames zugreifen kann. Eingebettete Frames sind Thema desAbschnitts »Beziehungen zwischen Frames« auf Seite 179.

Da Window im clientseitigen JavaScript das globale Objekt ist,besitzt jedes Fenster und jeder Frame einen eigenen JavaScript-Aus-führungskontext. Trotzdem kann JavaScript-Code in einem Fenster– unter Berücksichtigung der Same-Origin-Policy – die Objekte,Eigenschaften und Methoden in anderen Fenstern nutzen. Dieswird detaillierter im Abschnitt »JavaScript in interagierenden Fens-tern« auf Seite 181 behandelt.

Beziehungen zwischen Frames

Sie wissen schon, dass der JavaScript-Code in einem beliebigenFenster oder Frame auf sein eigenes Window-Objekt über window(oder self) zugreifen kann. Ein Frame kann auf das Window-

Das Window-Objekt | 179

Page 192: 3868993886_Script

Objekt des übergeordneten Fensters oder Frames über die parent-Eigenschaft zugreifen:

parent.history.back();

Ein Window-Objekt, das ein oberstes Fenster oder einen Tabrepräsentiert, besitzt keinen Container, und seine parent-Eigen-schaft verweist einfach auf das Fenster selbst:

parent == self; // Fur jedes oberste Fenster

Wenn ein Frame in einem anderen Frame enthalten ist, der in einemobersten Fenster enthalten ist, kann dieser Frame auf das obersteFenster über parent.parent verweisen. Die Eigenschaft top ist aller-dings eine allgemeine Abkürzung: Egal wie tief ein Frame einge-bettet ist – seine top-Eigenschaft verweist auf das einschließendeFenster auf der obersten Ebene. Wenn ein Window-Objekt einoberstes Fenster repräsentiert, verweist top einfach auf dieses Fens-ter selbst. Bei Frames, die unmittelbare Kinder eines obersten Fens-ters sind, ist die Eigenschaft top mit der Eigenschaft parent iden-tisch.

Die Eigenschaften parent und top ermöglichen es einem Skript, aufseine Eltern-Container zuzugreifen. Auf die eingebetteten Framesoder Fenster kann man auf verschiedenen Wegen zugreifen. Frameswerden durch <iframe>-Elemente erstellt. Sie können ein Element-Objekt, das einen <iframe> repräsentiert, auf dem gleichen Weg wiebei jedem anderen Element erhalten. Gehen wir einmal davon aus,dass Ihr Dokument <iframe id="f1"> enthält. Dann ist das Element-Objekt, das diesen IFrame darstellt:

var e = document.getElementById("f1");

<iframe>-Elemente besitzen eine Eigenschaft contentWindow, die aufdas Window-Objekt des Frames verweist. Daher ist das Window-Objekt für diesen Frame:

var kid = document.getElementById("f1").contentWindow;

Sie können auch umgekehrt vorgehen – vom Window, das einenFrame repräsentiert, zum <iframe>-Element, das den Frame enthält.Das erreichen Sie über die frameElement-Eigenschaft des Window.

180 | Kapitel 10: Clientseitiges JavaScript

Page 193: 3868993886_Script

Bei Window-Objekten, die Fenster auf oberster Ebene repräsentie-ren, ist die Eigenschaft frameElement anders als bei Frames null:

var elt = document.getElementById("f1");var w = elt.contentWindow;w.frameElement === elt // Fur Frames immer truew.frameElement === null // Fur oberste Fenster

Normalerweise ist es allerdings nicht notwendig, die getElementById()-Methode und die contentWindow-Eigenschaft zu nutzen, umReferenzen auf die Kind-Frames eines Fensters zu erhalten. JedesWindow-Objekt besitzt eine Eigenschaft frames, die auf die Kind-Frames innerhalb des Fensters oder Frames verweist. Die frames-Eigenschaft ist ein Array-ähnliches Objekt, das numerisch odernach dem Frame-Namen indiziert werden kann. Um den erstenFrame in einem Fenster zu erreichen, können Sie frames[0] nutzen.Den dritten Kind-Frame des zweiten Kind-Frames erreichen Sieüber frames[1].frames[2]. Code, der in einem Geschwister-Frameläuft, kann auf einen Frame auf gleicher Ebene zum Beispiel überparent.frames[1] zugreifen. Beachten Sie, dass die Elemente desframes[]-Arrays Window-Objekte sind, keine <iframe>-Elemente.

Wenn Sie das name- oder id-Attribut eines <iframe>-Elements an-geben, kann dieser Frame auch über den Namen angesprochenwerden. Ein Frame mit dem Namen »f1« lässt sich zum Beispielüber frames["f1"] oder frames.f1 erreichen.

Sie können die Attribute name oder id eines <iframe>-Elementsnutzen, um dem Frame einen Namen zu geben, der in JavaScript-Code verwendet werden kann. Nutzen Sie das Attribut name, wirdder angegebene Name zugleich der Wert der name-Eigenschaft desWindow, das den Frame repräsentiert. Ein so angegebener Namekann auch als target-Attribut eines Links genutzt werden.

JavaScript in interagierenden Fenstern

Jedes Fenster und jeder Frame hat seinen eigenen JavaScript-Aus-führungskontext mit einem Window als globalem Objekt. Wennsich aber nun Code aus einem Fenster oder Frame auf ein anderesFenster oder einen Frame beziehen kann (und wenn die Same-Ori-

Das Window-Objekt | 181

Page 194: 3868993886_Script

gin-Policy das nicht verhindert), können die Skripten aus einemFenster oder Frame mit den Skripten im anderen interagieren.

Stellen Sie sich eine Webseite mit zwei <iframe>-Elementen namens»A« und »B« vor, und gehen Sie davon aus, dass diese FramesDokumente vom gleichen Server erhalten und dass sich in diesenDokumenten miteinander interagierende Skripten befinden. DasSkript in Frame A definiert zum Beispiel eine Variable i:

var i = 3;

Diese Variable ist nichts anderes als eine Eigenschaft des globalenObjekts – eine Eigenschaft des Window-Objekts. Code in Frame Akann auf die Variable mit der Bezeichnung i zugreifen oder sieexplizit über das Window-Objekt ansprechen:

window.i

Da das Skript in Frame B auf das Window-Objekt aus Frame Azugreifen kann, erreicht es auch die Eigenschaften dieses Window-Objekts:

parent.A.i = 4;

Denken Sie daran, dass das Schlüsselwort function, das Funktionendefiniert, genauso wie das Schlüsselwort var eine Variable erstellt.Deklariert ein Skript in Frame B eine (nicht eingebettete) Funktionf, ist diese Funktion eine globale Variable von Frame B, und Code inFrame B kann f als f() aufrufen. Code in Frame A muss f allerdingsals Eigenschaft des Window-Objekts von Frame B ansprechen:

parent.B.f();

Wenn der Code in Frame A diese Funktion häufig verwenden muss,könnte er die Funktion einer Variablen in Frame A zuweisen, damiter etwas bequemer auf sie verweisen kann:

var f = parent.B.f;

Jetzt kann Code in Frame A die Funktion als f() aufrufen, genauwie es der Code in Frame B macht.

Wenn Sie Funktionen auf diese Weise zwischen Frames oder Fens-tern teilen, ist es sehr wichtig, die Regeln der lexikalischen Geltungim Kopf zu behalten. Eine Funktion wird in dem Geltungsbereich

182 | Kapitel 10: Clientseitiges JavaScript

Page 195: 3868993886_Script

ausgeführt, in dem sie definiert wurde, nicht in dem, in dem sieaufgerufen wurde. Das heißt also, dass globale Variablen, wenn dieoben definierte Funktion f auf sie verweist, als Eigenschaften vonFrame B nachgeschlagen werden, selbst wenn die Funktion ausFrame A aufgerufen wird.

Die Same-Origin-Policy

Die Same-Origin-Policy (»Gleiche-Herkunft-Richtlinie«) ist einedurchschlagende Sicherheitseinschränkung für die Inhalte, mit de-nen JavaScript-Code interagieren kann. Üblicherweise kommt siezum Tragen, wenn eine Webseite <iframe>-Elemente enthält. Dannsteuert die Same-Origin-Policy die Interaktion von JavaScript-Codein einem Fenster oder Frame mit dem Inhalt anderer Fenster oderFrames. Sie bewirkt, dass ein Skript nur Eigenschaften von Fensternund Dokumenten lesen kann, die die gleiche Herkunft wie dasDokument haben, das das Skript enthält.

Die Herkunft eines Dokuments wird über das Protokoll, den Hostund den Port der URL definiert, von der das Dokument geladenwurde. Dokumente, die von unterschiedlichen Webservern geladenwerden, sind unterschiedlicher Herkunft. Dokumente, die überunterschiedliche Ports auf dem gleichen Host geladen wurden,sind unterschiedlicher Herkunft. Und ein Dokument, das mit demhttp:-Protokoll geladen wurde, hat eine andere Herkunft als einDokument, das mit dem https:-Protokoll geladen wurde, selbstwenn beide Dokumente vom gleichen Webserver stammen.

Es ist wichtig zu verstehen, dass die Herkunft des Skripts selbst fürdie Same-Origin-Policy irrelevant ist: Es geht nur um die Herkunftdes Dokuments, in das das Skript eingebettet ist. Nehmen wirbeispielsweise an, dass ein Skript von Host A (über das src-Attributeines <script>-Elements) in eine Webseite von Host B eingeschlos-sen ist. Die Herkunft dieses Skripts ist Host B, und es hat voll-ständigen Zugriff auf den Inhalt des Dokuments, das es enthält.Wenn das Skript einen iFrame erzeugt und ein zweites Dokumentvon Host B lädt, hat das Skript auch vollständigen Zugriff auf denInhalt des zweiten Dokuments. Wenn das Skript allerdings einenanderen iFrame öffnet und darin ein Dokument von Host C (oder

Das Window-Objekt | 183

Page 196: 3868993886_Script

sogar eines von Host A) lädt, tritt die Same-Origin-Policy auf denPlan und verhindert, dass das Skript auf dieses Dokument zugreift.

Die Same-Origin-Policy gilt eigentlich nicht für alle Eigenschaftenaller Objekte in einem Fenster von einer anderen Quelle. Aber siegilt für viele von ihnen und insbesondere für fast alle Eigenschaftendes Document-Objekts. Fenster oder Frames, die Dokumente voneinem anderen Server enthalten, sollten für Ihre Skripten also injeder Hinsicht tabu sein.

Die Same-Origin-Policy ist auch für geskriptete HTTP-Requestsgültig, die mit dem XMLHttpRequest (siehe Kapitel 13) abgeschicktwerden. Dieses Objekt ermöglicht es clientseitigem JavaScript-Code, beliebige HTTP-Requests an den Webserver zu schicken,von dem das enthaltende Dokument geladen wurde, aber Skriptenkönnen darüber nicht mit anderen Webservern kommunizieren.

184 | Kapitel 10: Clientseitiges JavaScript

Page 197: 3868993886_Script

KAPITEL 11

Dokumente skripten

Clientseitiges JavaScript dient dazu, statische HTML-Dokumente ininteraktive Webanwendungen zu verwandeln. Das Document-Ob-jekt steht für den Inhalt des Browserfensters und ist Thema diesesKapitels. Aber das Document-Objekt ist nicht allein. Es ist daszentrale Objekt in einer größeren API, die als das Document ObjectModel oder DOM bekannt ist. Mit ihr können Dokument-Inhalterepräsentiert und bearbeitet werden.

Übersicht über das DOMDas Document Object Model oder DOM ist die grundlegende APIfür das Arbeiten mit den Inhalten von HTML-Dokumenten. DieAPI ist nicht besonders kompliziert, aber es gibt eine Reihe archi-tektonischer Details, die Sie verstanden haben müssen. Als Erstessollten Sie verstehen, dass die verschachtelten Elemente einesHTML- oder XML-Dokuments im DOM als Baum aus Objektenrepräsentiert werden. Die Baum-Darstellung eines HTML-Doku-ments enthält Knoten für HTML-Tags oder -Elemente, wie zumBeispiel <body> und <p>, und Knoten, die Textstrings repräsentieren.Ein HTML-Dokument kann auch Knoten für HTML-Kommentareenthalten. Schauen Sie sich das folgende einfache HTML-Doku-ment an:

<html><head>

<title>Beispieldokument</title></head><body>

| 185

Page 198: 3868993886_Script

<h1>Ein HTML-Dokument</h1><p>Dies ist ein <i>einfaches</i> Dokument.

</html>

Die DOM-Repräsentation dieses Dokuments ist der Baum, den Siein Abbildung 11-1 sehen.

Document

<html>

<body>

<h1>

<head>

<title>

"Beispieldokument" <p>

"Ein HTML-Dokument"

"Dokument"<i>"Dies ist ein"

"einfaches"

Abbildung 11-1: Die Baum-Darstellung eines HTML-Dokuments

Wenn Sie mit der Baum-Struktur in der Computer-Programmie-rung noch nicht vertraut sind, ist es hilfreich zu wissen, dass sie ihreTerminologie von Stammbäumen ausleiht. Der Knoten direkt übereinem Knoten ist der Elternknoten dieses Knotens. Die Knoten aufeiner Stufe direkt unter einem anderen Knoten sind die Kindknotendieses Knotens. Knoten auf der gleichen Ebene und mit demgleichen Elternknoten sind Geschwister. Die Menge der Knoten,die sich auf irgendeiner Ebene unterhalb eines anderen Knotensbefinden, sind die Nachfahren dieses Knotens. Und der Elternkno-ten, die Großelternknoten und alle anderen Knoten über einemKnoten sind die Vorfahren dieses Knotens.

Jedes Kästchen in Abbildung 11-1 ist ein Knoten des Dokuments,der durch ein Node-Objekt repräsentiert wird. Wir werden über dieEigenschaften und Methoden von Node noch in den folgendenAbschnitten schreiben. Beachten Sie, dass die Abbildung drei ver-

186 | Kapitel 11: Dokumente skripten

Page 199: 3868993886_Script

schiedene Arten von Knoten enthält. Am obersten Punkt des Baums(der Wurzel) befindet sich der Document-Knoten, der das gesamteDokument repräsentiert. Die Knoten für die HTML-Elemente sindElement-Knoten und die für Text sind Text-Knoten. Document,Element und Text sind Unterklassen von Node. Document undElement sind die beiden wichtigsten DOM-Klassen, und ein Groß-teil dieses Kapitels ist ihren Eigenschaften und Methoden gewid-met.

Node und seine Untertypen bilden die Typ-Hierarchie, die inAbbildung 11-2 zu sehen ist. Beachten Sie, dass es einen formellenUnterschied zwischen den generischen Document- und Element-Typen einerseits und den HTMLDocument- und HTMLElement-Typen andererseits gibt. Der Document-Typ kann sowohl einHTML- wie auch ein XML-Dokument vertreten, und die Element-Klasse steht für ein Element solch eines Dokuments. Die Unter-klassen HTMLDocument und HTMLElement sind nur für HTML-Dokumente und -Elemente gedacht. In diesem Buch nutzen wirhäufig die generischen Klassennamen Document und Element,auch wenn wir uns auf HTML-Dokumente beziehen.

Document

CharacterData

Element

Attr

Node

HTMLDocument

Text

Comment

HTMLElement

HTMLTitleElement

HTMLBodyElement

HTMLHeadElement

HTMLParagraphElement

HTMLInputElement

HTMLTableElement

. . . usw.

Abbildung 11-2: Ein Teil der Klassen-Hierarchie der Dokument-Knoten

An Abbildung 11-2 erkennt man auch, dass es viele Untertypen vonHTMLElement gibt, die bestimmte Typen von HTML-Elementen

Übersicht über das DOM | 187

Page 200: 3868993886_Script

repräsentieren. Jeder definiert JavaScript-Eigenschaften, um dieHTML-Attribute eines bestimmten Elements oder einer Gruppevon Elementen widerzuspiegeln. Manche dieser elementspezi-fischen Klassen definieren zusätzliche Eigenschaften oder Metho-den, die über die einfache Wiedergabe der HTML-Syntax hinaus-gehen.

Dokument-Elemente auswählenDie meisten clientseitigen JavaScript-Programme werden eines odermehrere Dokument-Elemente verändern. Wenn diese Programmestarten, können sie die globale Variable document nutzen, um auf dasDocument-Objekt zuzugreifen. Um Elemente des Dokuments zubearbeiten, müssen sie aber irgendwie an die Element-Objekte gelan-gen, die für diese Elemente des Dokuments stehen. Das DOMdefiniert eine Reihe von Wegen, um Elemente auszuwählen. Sie kön-nen ein Dokument nach einem oder mehreren Elementen durch-suchen:

• mit einem bestimmten id-Attribut,

• mit einem bestimmten name-Attribut,

• mit einem bestimmten Tag-Namen,

• mit einer oder mehreren bestimmten CSS-Klassen oder

• mit einem bestimmten CSS-Selektor.

Die folgenden Unterabschnitte erläutern jede dieser Auswahltech-niken.

Elemente über ihre ID auswählenJedes HTML-Element kann ein id-Attribut besitzen. Der Wertdieses Attributs muss innerhalb des Dokuments eindeutig sein – esdarf keine zwei Elemente im gleichen Dokument mit der gleichen IDgeben. Sie können ein Element mit der Methode getElementById()des Document-Objekts auswählen:

var sect1 = document.getElementById("section1");

188 | Kapitel 11: Dokumente skripten

Page 201: 3868993886_Script

Dies ist der einfachste und am häufigsten genutzte Weg, um Ele-mente auszuwählen. Wird Ihr Skript eine bestimmte Gruppe vonDokument-Elementen bearbeiten, geben Sie diesen Elementen id-Attribute, und suchen Sie die Elemente anschließend anhand dieserIDs. Müssen Sie mehr als ein Element über die ID finden, hilft Ihnenvielleicht die Funktion getElements() aus Beispiel 11-1.

Beispiel 11-1: Mehrere Elemente anhand ihrer ID finden

/** Diese Funktion erwartet eine beliebige Zahl von* String-Argumenten.* Sie behandelt jedes Argument als Element-ID und ruft* dafur document.getElementById() auf. Es wird ein Objekt* zuruckgegeben, das die IDs auf das entsprechende* Element-Objekt abbildet.*/

function getElements(/*ids...*/) {var elements = {}; // Beginne mit einer leeren Map.for(var i = 0; i < arguments.length; i++) {

var id = arguments[i]; // Argument ist eine Element-ID.var elt = document.getElementById(id);if (elt == null)

throw new Error("No element with id: " + id);elements[id] = elt; // Bilde Element auf ID ab.

}return elements; // Gib die Map zuruck.

}

Elemente über ihren Namen auswählenDas HTML-Attribut name war ursprünglich dazu gedacht, Formu-lar-Elementen Namen zuzuweisen, und der Wert dieses Attributswird verwendet, wenn die Formular-Daten an einen Server gesendetwerden. Wie das id-Attribut weist name einem Element einen Na-men zu. Anders als id muss der Wert eines name-Attributs aber nichteindeutig sein: Mehrere Elemente können den gleichen Namenerhalten, was gerade bei Radiobuttons und Checkboxen in Formu-laren durchaus gebräuchlich ist. Und auch anders als bei id ist dasname-Attribut nur für eine Handvoll HTML-Elemente gültig. Dazugehören Formulare, Formular-Elemente, <iframe>- und <img>-Ele-mente.

Dokument-Elemente auswählen | 189

Page 202: 3868993886_Script

Um HTML-Elemente abhängig vom Wert ihres name-Attributs aus-zuwählen, nutzen Sie die Methode getElementsByName() des Docu-ment-Objekts:

var btns = document.getElementsByName("color");

getElementsByName() gibt ein NodeList-Objekt zurück, das sich wieein nicht veränderbares Array mit Element-Objekten verhält.

Setzt man das name-Attribut eines Elements vom Typ <form>, <img>oder <iframe>, wird eine Eigenschaft für das Document-Objekterzeugt, deren Name dem Wert des Attributs entspricht (sofernnatürlich das Dokument nicht schon eine Eigenschaft mit diesemNamen besitzt). Gibt es nur ein Element mit einem bestimmtenNamen, ist der Wert der automatischen Document-Eigenschaft dasElement selbst. Gibt es mehr als ein Element, handelt es sich beimWert der Eigenschaft um ein NodeList-Objekt, das sich wie einArray mit Elementen verhält. Die Document-Eigenschaften, die fürbenannte <iframe>-Elemente erzeugt werden, sind etwas spezieller:Anstatt auf das Element-Objekt zu verweisen, beziehen sie sich aufdas Window-Objekt des Frames.

Das bedeutet, dass ein Element anhand seines Namens einfach übereine Document-Eigenschaft des gleichen Namens angesprochenwerden kann:

// Element-Objekt fur das Element// <form name="shipping_address"> holen.var form = document.shipping_address;

Elemente über ihren Typ auswählenSie können alle Elemente eines angegebenen Typs (oder Tag-Na-mens) mit der Methode getElementsByTagName() des Document-Objekts auswählen. Um ein nicht veränderbares Array-ähnlichesObjekt mit den Element-Objekten für alle <span>-Elemente einesDokuments zu erhalten, schreiben Sie zum Beispiel:

var spans = document.getElementsByTagName("span");

Wie getElementsByName() liefert getElementsByTagName() ein Node-List-Objekt zurück. Die Elemente in der zurückgegebenen NodeListsind in derselben Reihenfolge wie im Dokument angeordnet, daher

190 | Kapitel 11: Dokumente skripten

Page 203: 3868993886_Script

können Sie das erste <p>-Element eines Dokuments wie folgt aus-lesen:

var firstpara = document.getElementsByTagName("p")[0];

HTML-Tags sind unabhängig von Groß- und Kleinschreibung, undwenn getElementsByTagName() für ein HTML-Dokument genutztwird, ist auch die Suche unabhängig von der Schreibweise. Diespans-Variable weiter oben wird zum Beispiel alle <span>-Elementefinden, auch wenn sie als <SPAN> geschrieben wurden.

Sie können eine NodeList mit allen Elementen eines Dokumentserhalten, indem Sie der Methode getElementsByTagName() den Wert»*« übergeben.

Die Element-Klasse definiert ebenfalls eine Methode getElements-ByTagName(). Sie funktioniert genauso wie die Document-Version,wählt aber nur Elemente aus, die Nachfahren des aufgerufenenElements sind. Um also alle <span>-Elemente im ersten <p>-Elementeines Dokuments zu finden, können Sie Folgendes schreiben:

var firstp= document.getElementsByTagName("p")[0];var firstpSpans = firstp.getElementsByTagName("span");

Aus historischen Gründen definiert die HTMLDocument-Klassezusätzliche Eigenschaften für den direkteren Zugriff auf bestimmteArten von Knoten. Die Eigenschaften images, forms und linksreferenzieren zum Beispiel auf Objekte, die sich wie nicht veränder-bare Arrays mit <img>-, <form>- und <a>-Elementen verhalten (beiden <a>-Tags gilt das allerdings nur für die, die auch ein href-At-tribut besitzen). Diese Eigenschaften verweisen auf HTMLCollec-tion-Objekte, die sich sehr ähnlich wie NodeList-Objekte verhalten,aber zusätzlich über die Element-ID oder den Namen angesprochenwerden können. Weiter oben haben wir gesehen, wie Sie auf einbenanntes <form>-Element mit einem Ausdruck wie dem folgendenzugreifen können:

document.shipping_address

Mit der document.forms-Eigenschaft können Sie auch spezifischerauf das benannte (oder mit einer ID versehene) Formular-Feldzugreifen:

document.forms.shipping_address;

Dokument-Elemente auswählen | 191

Page 204: 3868993886_Script

HTMLDocument definiert zudem zwei Eigenschaften, die sich aufspezielle einzelne Elemente und nicht auf Element-Collections be-ziehen. document.body ist das <body>-Element eines HTML-Do-kuments, document.head das <head>-Element. Die EigenschaftdocumentElement der Document-Klasse referenziert auf das Wur-zelelement des Dokuments. In HTML-Dokumenten ist dies immerein <html>-Element.

NodeLists und HTMLCollectionsgetElementsByName() und getElementsByTagName() liefern NodeList-Objekte zurück, während Eigenschaften wie document.images unddocument.forms HTMLCollection-Objekte sind.

Diese Objekte sind schreibgeschützte Array-ähnliche Objekte. Siehaben length-Eigenschaften und können wie echte Arrays (nur zumLesen, nicht zum Schreiben) indiziert werden. Sie können über denInhalt einer NodeList oder HTMLCollection mit einer Standard-schleife iterieren:

// Alle Bilder durchlaufen und verbergen.for(var i = 0; i < document.images.length; i++)

document.images[i].style.display = "none";

Eines der wichtigsten und überraschendsten Features von NodeListund HTMLCollection ist, dass es sich nicht um statische Abbildun-gen eines vergangenen Dokument-Status handelt, sondern dass sieim Allgemeinen live sind und sich die Liste ihrer Elemente durchBearbeiten des Dokuments verändern kann. Stellen Sie sich vor, Sierufen getElementsByTagName('div') für ein Dokument ohne <div>-Elemente auf. Der Rückgabewert ist eine NodeList mit einer lengthvon 0. Fügen Sie dann in das Dokument ein neues <div>-Elementein, wird dieses Element automatisch zu einem Mitglied der Node-List, und die length-Eigenschaft wechselt zu 1.

Elemente über die CSS-Klasse auswählenDas class-Attribut eines HTML-Elements ist eine durch Kommasgetrennte Liste mit null oder mehr Bezeichnern. Es beschreibt eineGruppe von zusammengehörigen Dokument-Elementen: Jedes Ele-ment mit dem gleichen Bezeichner in seinem class-Attribut ist Teil

192 | Kapitel 11: Dokumente skripten

Page 205: 3868993886_Script

der gleichen Gruppe. class ist in JavaScript ein reserviertes Wort,daher nutzt das clientseitige JavaScript die className-Eigenschaft,um den Wert des HTML-Attributs class zu verwalten. Das class-Attribut wird normalerweise zusammen mit einem CSS-Stylesheetgenutzt, um das gleiche Erscheinungsbild auf alle Mitglieder einerGruppe anzuwenden. Zusätzlich definiert HTML5 aber eine Me-thode getElementsByClassName(), mit der wir Gruppen von Doku-ment-Elementen aufgrund ihrer Bezeichner im class-Attribut aus-wählen können.

Wie getElementsByTagName() kann getElementsByClassName() so-wohl für HTML-Dokumente als auch für HTML-Elemente aufgeru-fen werden. Zurückgegeben wird eine Live-NodeList mit allenpassenden Nachfahren des Dokuments oder Elements. getEle-mentsByClassName() erwartet ein einzelnes String-Argument, indem aber mehrere, durch Leerzeichen getrennte Bezeichner stehenkönnen. Nur Elemente, die alle angegebenen Bezeichner in ihremclass-Attribut haben, werden gefunden. Die Reihenfolge der Be-zeichner ist dabei nicht wichtig. Hier ein paar Beispiele für getEle-mentsByClassName():

// Finde alle Elemente mit "warning" in ihrem// class-Attribut.var w = document.getElementsByClassName("warning");// Finde alle Kind-Elemente des Elements "log" mit den// Klassen "error" und "fatal".var log = document.getElementById("log");var fatal = log.getElementsByClassName("fatal error");

Elemente über CSS-Selektoren auswählenCSS-Stylesheets nutzen eine sehr mächtige Syntax – die Selektoren–, um Elemente oder Gruppen von Elementen in einem Dokumentauszuwählen. Die komplette Beschreibung der CSS-Selektor-Syntaxgeht über den Rahmen dieses Buches hinaus, aber ein paar Beispielesollen die Grundlagen deutlich machen. Elemente können über ihreID, den Tag-Namen oder die Klasse beschrieben werden:

#nav // Ein Element mit id="nav"div // Jedes <div>-Element.warning // Jedes Element mit "warning" in seinem

// class-Attribut.

Dokument-Elemente auswählen | 193

Page 206: 3868993886_Script

Allgemeiner können Elemente über Attribut-Werte selektiert wer-den:

p[lang="fr"] // Ein Absatz, der in Franzosisch// geschrieben wurde: <p lang="fr">

*[name="x"] // Jedes Element mit einem Attribut name="x"

Diese grundlegenden Selektoren können kombiniert werden:

span.fatal.error // Jedes <span> mit "error" und "fatal"// in seiner Klasse

span[lang="fr"].warning // Jede Warnung auf Franzosisch

Selektoren können auch die Dokumentenstruktur angeben:

#log span // Jeder <span>-Nachfahre des Elements mit// id="log"

#log>span // Jedes <span>-Kind des Elements mit id="log"body>h1:first-child // Das erste <h1>-Kind des <body>

Selektoren können kombiniert werden, um mehrere Elemente oderElement-Gruppen auszuwählen:

div, #log // Alle <div>-Elemente plus das Element mit// id="log"

Wie Sie sehen, ermöglichen es CSS-Selektoren, Elemente auf alleoben aufgeführten Weisen auszuwählen: nach ID, nach Name,nach Tag-Name und nach Klassen-Name. Elemente, auf die einCSS-Selektor passt, können Sie auch über die Document-MethodequerySelectorAll() auswählen. Sie erwartet ein einzelnes String-Argument mit einem CSS-Selektor und liefert dafür eine NodeListmit allen Elementen im Dokument zurück, die zum Selektor passen.Anders als die weiter oben beschriebenen Methoden zum Auswäh-len von Elementen ist die von querySelectorAll() zurückgegebeneNodeList nicht live, sondern ein Schnappschuss des Status beimAufruf der Methode. Danach vorgenommene Änderungen am Do-kument werden nicht automatisch widergespiegelt. Passen keineElemente, liefert querySelectorAll() eine leere NodeList zurück. Istder Selektor-String ungültig, löst querySelectorAll() eine Excep-tion aus.

Neben querySelectorAll() definiert das Document-Objekt auchnoch querySelector(), das sich wie querySelectorAll() verhält,

194 | Kapitel 11: Dokumente skripten

Page 207: 3868993886_Script

aber nur das erste (in der Reihenfolge im Dokument) passendeElement zurückliefert (oder null, wenn nichts gefunden wurde).

Diese beiden Methoden sind auch für Elemente definiert. Werdensie für ein Element aufgerufen, wird der angegebene Selektor gegendas gesamte Dokument ausgeführt und das Suchergebnis dann sogefiltert, dass nur die Nachfahren des angegebenen Elements ent-halten sind. Das mag etwas unintuitiv klingen, da der Selektor-String so Nachfahren des Elements enthalten kann, gegen das erüberprüft wird.

Dokumentenstruktur und -durchlaufHaben Sie ein Element aus einem Document ausgewählt, müssenSie manchmal andere, strukturell zugehörige Teile des Dokumentsfinden (Eltern-, Geschwister- oder Kind-Elemente). Das Document-Objekt, seine Element-Objekte und die Text-Objekte, die Text imDokument repräsentieren, sind alles Node-Objekte. Node definiertdie folgenden wichtigen Eigenschaften:

parentNodeDer Eltern-Node des aktuellen Knotens oder null für Knotenwie das Document-Objekt, das keinen Eltern-Knoten besitzt.

childNodesEin nicht veränderbares Array-ähnliches Objekt (eine NodeList)mit einer Live-Repräsentation der Kind-Knoten eines Node.

firstChild, lastChildDer erste und der letzte Kind-Knoten eines Knotens oder null,wenn der Knoten keine Kinder besitzt.

nextSibling, previousSiblingDer nächste und der vorherige Geschwister-Knoten eines Kno-tens. Zwei Knoten mit dem gleichen Eltern-Knoten sind Ge-schwister. Ihre Reihenfolge spiegelt die Reihenfolge wider, inder sie im Dokument auftreten. Diese Eigenschaften verbindendie Knoten in einer doppelt verketteten Liste miteinander.

Dokumentenstruktur und -durchlauf | 195

Page 208: 3868993886_Script

nodeTypeDie Art des Knotens. Document-Knoten haben den Wert 9.Element-Knoten haben den Wert 1, Text-Knoten den Wert 3und Comments-Knoten den Wert 8.

nodeValueDer Text-Inhalt eines Text- oder Comment-Knotens.

nodeNameDer Tag-Name eines Elements, in Großbuchstaben umgewan-delt.

Mit diesen Node-Eigenschaften erreicht man den zweiten Kind-Knoten des ersten Kinds von Document zum Beispiel so:

document.childNodes[0].childNodes[1]document.firstChild.firstChild.nextSibling

Stellen Sie sich vor, bei dem fraglichen Dokument handelt es sichum das folgende:

<html><head><title>Test</title></head><body>Hallo!</body></html>

Dann ist der zweite Kind-Knoten des ersten Kinds das <body>-Ele-ment. Er besitzt einen nodeType mit dem Wert 1 und den nodeName»BODY«.

Beachten Sie allerdings, dass diese API auf Änderungen im Doku-ment ausgesprochen empfindlich reagiert. Wird das Dokumentbeispielsweise durch das Einfügen eines einzigen Zeilenumbruchszwischen dem <html>- und dem <head>-Tag geändert, wird derText-Knoten, der den Zeilenumbruch repräsentiert, zum erstenKind des ersten Kindes, und das zweite Kind ist dann statt des<body>-Elements das <head>-Element.

Wenn wir mehr an den Elementen eines Dokuments als an derenText (und dem Whitespace zwischen ihnen) interessiert sind, ist esnützlich, eine API zu nutzen, die es uns ermöglicht, ein Dokumentals Baum aus Element-Objekten zu betrachten, bei denen Text- undKommentar-Knoten ignoriert werden.

196 | Kapitel 11: Dokumente skripten

Page 209: 3868993886_Script

Der erste Teil dieser API ist die children-Eigenschaft der Element-Objekte. Wie childNodes ist dies eine NodeList. Aber anders alschildNodes enthält die children-Liste nur Element-Objekte.

Der zweite Teil der elementbasierten API zum Durchlaufen vonDokumenten sind Element-Eigenschaften, die analog zu den child-und sibling-Eigenschaften des Node-Objekts genutzt werden kön-nen:

firstElementChild, lastElementChildWie firstChild und lastChild, aber nur für Element-Kinder.

nextElementSibling, previousElementSiblingWie nextSibling und previousSibling, aber nur für Element-Geschwister.

childElementCountDie Anzahl der Element-Kinder. Es wird der gleiche Wert wiebei children.length zurückgegeben.

AttributeHTML-Elemente bestehen aus einem Tag-Namen und einer Mengevon Name/Wert-Paaren, die als Attribute bekannt sind. Das<a>-Element, das einen Hyperlink definiert, nutzt zum Beispiel denWert seines href-Attributs als Ziel für den Link. Die Attributwertevon HTML-Elementen stehen als Eigenschaften der sie repräsentie-renden HTMLElement-Objekte zur Verfügung. Das HTMLElementdefiniert Eigenschaften für die universellen HTTP-Attribute wie id,title, lang und dir, dazu noch Event-Handler-Eigenschaften wieonclick. Elementspezifische Untertypen definieren Attribute, diefür die entsprechenden Elemente spezifisch sind. Um die URL einesBildes abzufragen, können Sie zum Beispiel die src-Eigenschaft desHTMLElements nutzen, das das <img>-Element repräsentiert:

var img = document.getElementById("myimage");var url = img.src; // Das src-Attribut ist die URL des

// Bildes.img.id = "myimg" // Wir haben das Bild uber die

// ID gefunden.

Attribute | 197

Page 210: 3868993886_Script

Genauso können Sie zum Beispiel die Submission-Attribute von<form>-Elementen mit Code wie dem folgenden bearbeiten:

var f = document.forms[0]; // Erste <form> im Dokumentf.method = "POST"; // HTTP-Request-Typf.action = "http://www.example.com/submit.php";

Die Namen der HTML-Attribute sind unabhängig von Groß- undKleinschreibung, nicht aber die Namen der JavaScript-Eigenschaften.Um einen Attributnamen in die JavaScript-Eigenschaft umzuwan-deln, schreiben Sie ihn in Kleinbuchstaben. Ist das Attribut allerdingslänger als ein Wort, müssen Sie den ersten Buchstaben jedes Worts(außer des ersten) großschreiben – zum Beispiel defaultChecked undtabIndex.

Manche HTML-Attributnamen sind in JavaScript reservierte Wör-ter. Für diese gilt die generelle Regel, dass dem Eigenschaftsnamen»html« vorangestellt wird. So wird das HTML-Attribut for (des<label>-Elements) zum Beispiel in JavaScript zur EigenschafthtmlFor. »class« ist in JavaScript reserviert (wenn auch ungenutzt),und das sehr wichtige HTML-Attribut class ist eine Ausnahme zureben erwähnten Regel: Es wird in JavaScript zu className.

Die Eigenschaften, die HTML-Attribute repräsentieren, haben imAllgemeinen einen String-Wert. Ist das Attribut ein boolescher odernumerischer Wert (zum Beispiel die Attribute defaultChecked undmaxLength eines <input>-Elements), sind die Eigenschaftswerte auchBooleans oder Zahlenwerte. Event-Handler-Attribute haben immerFunction-Objekte (oder null) als Wert. Die HTML5-Spezifikationdefiniert ein paar Attribute (unter anderem das form-Attribut von<input> und zugehörige Elemente), die Element-IDs in tatsächlicheElement-Objekte umwandeln.

Wie oben beschrieben definieren HTMLElement und seine Unter-typen Eigenschaften, die mit den Standard-Attributen von HTML-Elementen korrespondieren. Der Element-Typ definiert zudem dieMethoden getAttribute() und setAttribute(), mit denen Sie auchnicht dem Standard entsprechende HTML-Attribute lesen undschreiben können:

198 | Kapitel 11: Dokumente skripten

Page 211: 3868993886_Script

var image = document.images[0];var width = parseInt(image.getAttribute("WIDTH"));image.setAttribute("class", "thumbnail");

Der Code zeigt zwei wichtige Unterschiede zwischen diesen Metho-den und der weiter oben beschriebenen eigenschaftsbasierten API.Erstens werden Attributwerte immer als Strings behandelt. get-Attribute() liefert nie eine Zahl, einen booleschen Wert oder einObjekt zurück. Zweitens nutzen diese Methoden die Standard-Attributnamen, auch wenn diese in JavaScript reservierte Wörtersind. Für HTML-Elemente ist bei den Attributnamen Groß- undKleinschreibung irrelevant.

Element definiert auch zwei weitere Methoden, hasAttribute() undremoveAttribute(), die prüfen, ob ein angegebenes Attribut vor-handen ist, beziehungsweise die es vollständig entfernen können.Diese Methoden sind insbesondere für boolesche Attribute nütz-lich: Dabei handelt es sich um Attribute (wie zum Beispiel dasdisabled-Attribut von HTML-Formularelementen), deren An- oderAbwesenheit in einem Element entscheidend ist, nicht aber derenWert.

Element-InhaltSchauen Sie sich noch einmal Abbildung 11-1 an, und fragen Siesich, was der »Inhalt« des <p>-Elements ist. Es gibt drei Möglich-keiten, diese Frage zu beantworten:

• Der Inhalt ist der HTML-String »Dies ist ein <i>einfaches</i>Dokument«.

• Der Inhalt ist der einfache Textstring »Dies ist ein einfachesDokument«.

• Der Inhalt ist ein Text-Knoten, ein Element-Knoten mit einemText-Knoten als Kind und ein weiterer Text-Knoten.

Jede dieser Antworten ist korrekt, und jede Antwort ist auf ihreWeise nützlich. Die folgenden Abschnitte erläutern, wie man mitder HTML-Repräsentation, mit der Darstellung als einfacher Stringund mit der Baum-Darstellung des Element-Inhalts arbeitet.

Element-Inhalt | 199

Page 212: 3868993886_Script

Element-Inhalt als HTMLLiest man die innerHTML-Eigenschaft eines Elements aus, erhält manden Inhalt dieses Elements als mit Markup versehenen String. Setztman diese Eigenschaft für ein Element, wird der Parser des Web-browsers aufgerufen und der aktuelle Inhalt des Elements durcheine geparste Repräsentation des neuen Strings ersetzt.

Webbrowser können HTML sehr gut parsen, und das Setzen voninnerHTML ist meist ziemlich effizient, obwohl der angegebene Wertgeparst werden muss. Beachten Sie allerdings, dass ein wiederholtesAnhängen von Texthäppchen an die innerHTML-Eigenschaft mitdem +=-Operator meist nicht effizient ist, weil dabei jedes Mal einSerialisierungs- und ein Parsing-Schritt notwendig sind.

Mit der Methode insertAdjacentHTML() können Sie einen String mitbeliebigem HTML-Markup »neben« dem angegebenen Elementeinfügen. Das Markup wird als zweites Argument an die Methodeübergeben, und die genaue Bedeutung von »neben« hängt vomWert des ersten Arguments ab. Dieses sollte ein String mit einemder Werte »beforebegin«, »afterbegin«, »beforeend« oder »afterend«sein. Diese Werte entsprechen den Einfügepositionen, die in Ab-bildung 11-3 dargestellt sind.

<div id="target"> Dies ist der Elementinhalt</div>

beforebegin afterbegin beforeend afterend

Abbildung 11-3: Einfügepositionen für insertAdjacentHTML()

Element-Inhalt als einfacher TextManchmal wollen Sie den Inhalt eines Elements als einfachen Texterhalten oder solchen Text in ein Dokument einfügen (ohne diespitzen Klammern und Ampersands zu maskieren, die im HTML-Markup genutzt werden). Standardmäßig nutzt man dazu die text-Content-Eigenschaft von Node:

// Erstes <p> im Dokumentvar para = document.getElementsByTagName("p")[0];// Text ist "Dies ist ein einfaches Dokument."

200 | Kapitel 11: Dokumente skripten

Page 213: 3868993886_Script

var text = para.textContent;// Absatz-Inhalt verandernpara.textContent = "Hallo Welt!";

Element-Inhalt als Text-KnotenEine weitere Möglichkeit, mit dem Inhalt eines Elements zu arbei-ten, ist eine Liste mit Kind-Knoten, von denen jeder eventuell wei-tere Kinder besitzt. Wenn man sich für den Inhalt eines Elementsinteressiert, geht es meist um die Text-Knoten.

Beispiel 11-2 zeigt eine Funktion textContent(), die rekursiv dieKinder eines Elements durchläuft und den Text aller Text-Knoten-Nachfahren verkettet. Um den Code zu verstehen, sollten Sie sichins Gedächtnis rufen, dass die (vom Node-Typ definierte) node-Value-Eigenschaft den Inhalt eines Text-Knotens enthält.

Beispiel 11-2: Alle Text-Knoten-Nachfahren eines Elements finden

// Den einfachen Inhalt eines Elements e zuruckgeben,// rekursiv die Kind-Elemente durchlaufen. Diese Methode// arbeitet wie die textContent-Eigenschaft.function textContent(e) {

var c, type, s = "";for(c=e.firstChild; c!=null; c=c.nextSibling) {

type = c.nodeType;if (type === 3) // Text-Knoten:

s += c.nodeValue; // Textinhalt hinzufugenelse if (type === 1) // Element-Knoten:

s += textContent(c); // Rekursion}return s;

}

Beachten Sie, dass die nodeValue-Eigenschaft sich lesen und schrei-ben lässt, und Sie können sie setzen, um den von einem Text-Knoten angezeigten Inhalt zu ändern.

Knoten erstellen, einfügen und löschenWir haben gesehen, wie man den Inhalt von Dokumenten alsHTML- oder Text-Strings ausliest und verändert. Und wir habengesehen, dass wir ein Dokument durchlaufen können, um die

Knoten erstellen, einfügen und löschen | 201

Page 214: 3868993886_Script

einzelnen Element- und Text-Knoten, aus denen es aufgebaut ist, zunutzen. Es ist aber auch möglich, ein Dokument auf Ebene dereinzelnen Knoten zu bearbeiten. Der Document-Typ definiert Me-thoden, um Element- und Text-Objekte zu erstellen, und der Node-Typ definiert Methoden zum Einfügen, Löschen und Ersetzen vonKnoten im Baum. Die folgende Funktion zeigt, wie Sie ein Elementerstellen und in ein Dokument einfügen können:

// Asynchrones Laden und Ausfuhren eines Skriptsfunction loadasync(url) {

// <script>-Element erstellenvar s = document.createElement("script");// src-Attribut setzens.src = url;// <script> in head einfugendocument.head.appendChild(s);

}

Wie oben gezeigt wurde, können Sie neue Element-Knoten mit derMethode createElement() des Document-Objekts anlegen. Überge-ben Sie den Tag-Namen des Elements als Argument für die Metho-de.

Text-Knoten werden mit einer ähnlichen Methode erzeugt:

var t = document.createTextNode("text node");

Eine weitere Möglichkeit, neue Dokument-Knoten zu erstellen, istdas Kopieren bestehender Knoten. Jeder Knoten besitzt eine Me-thode cloneNode(), die eine neue Kopie des Knotens zurückgibt.Übergeben Sie true, werden auch rekursiv alle Nachfahren kopiert;mit false wird nur eine flache Kopie erzeugt.

Haben Sie einen neuen Knoten erstellt, können Sie ihn mit denNode-Methoden appendChild() oder insertBefore() in das Doku-ment einfügen. appendChild() wird für den Element-Knoten auf-gerufen, in den Sie einfügen wollen. Der einzufügende Knoten wirddann zum lastChild dieses Knotens.

insertBefore() ist wie appendChild(), nur werden zwei Argumenteerwartet. Das erste ist der einzufügende Knoten. Das zweite Argu-ment ist der Knoten, vor dem dieser neue Knoten eingefügt werdensoll. Diese Methode wird für den Knoten aufgerufen, der derEltern-Knoten des neuen Knotens sein wird, während das zweite

202 | Kapitel 11: Dokumente skripten

Page 215: 3868993886_Script

Argument ein Kind-Knoten des Eltern-Knotens sein muss. Überge-ben Sie als zweites Argument null, verhält sich insertBefore() wieappendChild() und fügt den Knoten am Ende ein.

Hier ist eine einfache Funktion, um einen Knoten mithilfe einesnumerischen Index einzufügen. Die Funktion demonstriert dieVerwendung von appendChild() und insertBefore():

// child-Knoten in parent einfugen, sodass er zum n-ten// Kind-Knoten wird.function insertAt(parent, child, n) {

if (n < 0 || n > parent.childNodes.length)throw new Error("invalid index");

else if (n == parent.childNodes.length)parent.appendChild(child);

elseparent.insertBefore(child,parent.childNodes[n]);

}

Wenn Sie appendChild() oder insertBefore() aufrufen, um einenKnoten einzufügen, der schon im Dokument vorhanden ist, wirddieser Knoten automatisch von seiner aktuellen Position entferntund an der neuen Position wieder eingefügt. Es gibt also keineNotwendigkeit, den Knoten explizit zu entfernen.

Die Methode removeChild() entfernt einen Knoten aus dem Doku-ment-Baum. Aber seien Sie vorsichtig: Diese Methode wird nicht fürden zu entfernenden Knoten, sondern (wie das »child« in seinemNamen schon andeutet) für das Eltern-Element aufgerufen. Über-geben Sie dabei den zu entfernenden Kind-Knoten als Argument.Um den Knoten n aus dem Dokument zu entfernen, können Sie alsoschreiben:

n.parentNode.removeChild(n);

replaceChild() entfernt einen Kind-Knoten und ersetzt ihn durcheinen neuen. Rufen Sie diese Methode für den Eltern-Knoten auf,und übergeben Sie den neuen Knoten als erstes und den zu erset-zenden Knoten als zweites Argument. Um den Knoten n zumBeispiel durch einen Textstring zu ersetzen, können Sie Folgendesschreiben:

var t = document.createTextNode("[ REDACTED ]");n.parentNode.replaceChild(t, n);

Knoten erstellen, einfügen und löschen | 203

Page 216: 3868993886_Script

Die folgende Funktion zeigt eine weitere Anwendung von replace-Child():

// Ersetze den Knoten n durch ein neues// Element <b>, und mache n zu einem Kind dieses Elements.function embolden(n) {

// Ist n ein String, soll es als Element-ID betrachtet// werden.if (typeof n == "string")

n = document.getElementById(n);// <b>-Element erstellenvar b = document.createElement("b");// n durch das <b>-Element ersetzenn.parentNode.replaceChild(b, n);// n zu einem Kind des <b>-Elements machenb.appendChild(n);

}

Element StyleCascading Style Sheets (CSS) ist ein Standard für die Definition dervisuellen Präsentation von HTML-Dokumenten. CSS ist dazu ge-dacht, von Grafikdesignern genutzt zu werden: CSS erlaubt eseinem Designer, die Schriften, Farben, Ränder, Einrückungen, Rah-men und sogar die Position von Elementen im Dokument fest-zulegen. CSS ist aber auch für clientseitige JavaScript-Programmie-rer interessant, da CSS-Styles geskriptet werden können. DieserAbschnitt demonstriert, wie Sie CSS-Regeln per JavaScript bear-beiten können. Wir gehen davon aus, dass Sie bereits CSS-Grund-kenntnisse besitzen.

Der einfachste Weg, CSS zu skripten, ist das Verändern des style-Attributs einzelner Dokument-Elemente. Wie die meisten HTML-Attribute ist style zudem eine Eigenschaft des Element-Objekts,und Sie können es per JavaScript beeinflussen. Die style-Eigen-schaft ist allerdings ein wenig ungewöhnlich: Ihr Wert ist keinString oder ein anderer elementarer Typ, sondern ein Objekt vomTyp CSSStyleDeclaration. Die JavaScript-Eigenschaften diesesStyle-Objekts repräsentieren die CSS-Eigenschaften, die durch dasHTML-Attribut style festgelegt wurden. Um den Text eines Ele-ments e groß, fett und blau zu machen, können Sie zum Beispiel denfolgenden Code nutzen, um die JavaScript-Eigenschaften zu setzen,

204 | Kapitel 11: Dokumente skripten

Page 217: 3868993886_Script

die zu den Style-Eigenschaften font-size, font-weight und colorgehören:

e.style.fontSize = "24pt";e.style.fontWeight = "bold";e.style.color = "blue";

Viele CSS-Style-Eigenschaften, wie zum Beispiel font-size, enthal-ten in ihren Namen Bindestriche. In JavaScript wird ein Bindestrichals Minuszeichen interpretiert, daher kann man nicht einfach fol-genden Ausdruck schreiben:

e.style.font-size = "24pt"; // Syntaxfehler!

Daher unterscheiden sich die Namen der Eigenschaften des CSS-StyleDeclaration-Objekts ein bisschen von den Namen der eigentli-chen CSS-Eigenschaften. Enthält der Name einer CSS-Eigenschafteinen oder mehrere Bindestriche, werden im Namen der CSSStyle-Declaration-Eigenschaft diese Bindestriche entfernt, und der direktfolgende Buchstabe wird großgeschrieben. Die CSS-Eigenschaftborder-left-width kann damit in JavaScript über borderLeftWidthangesprochen werden. Und wenn eine CSS-Eigenschaft einen inJavaScript reservierten Namen besitzt, wie zum Beispiel float,erhält dieser Name das Präfix »css«, um einen gültigen Namen inCSSStyleDeclaration zu erzeugen.

Vergessen Sie nicht, dass bei der Arbeit mit den Stil-Eigenschaften desCSSStyleDeclaration-Objekts alle Werte als Strings angegeben wer-den müssen. Denken Sie außerdem daran, dass alle Positionierungs-eigenschaften mit einer Längeneinheit versehen werden müssen. DenWert der Eigenschaft left können Sie daher nicht wie folgt setzen:

// falsch: Dies ist eine Zahl, kein String.e.style.left = 300;// falsch: Die Einheit fehlt.e.style.left = "300";

Einheiten werden benötigt, wenn Style-Eigenschaften in JavaScriptein Wert zugewiesen werden soll – genau wie bei der Angabe vonStyle-Eigenschaften in Stylesheets. Um der Eigenschaft left einesElements e den Wert 300 Pixel zuzuweisen, müssen Sie die folgendeAnweisung verwenden:

e.style.left = "300px";

Element Style | 205

Page 218: 3868993886_Script

Wenn Sie der Eigenschaft left einen berechneten Wert zuweisenmöchten, müssen Sie am Ende der Berechnung die Maßeinheit an-hängen:

e.style.left = (x0 + margin + border + padding) + "px";

Denken Sie daran, dass das numerische Ergebnis der Berechnung ineinen String umgewandelt wird, weil noch der Einheiten-Stringangefügt wird.

Das style-Attribut eines HTML-Elements ist sein Inline-Style, undes überschreibt Style-Spezifikationen aus einem Stylesheet. Inline-Styles sind im Allgemeinen nützlich, wenn man Style-Werte setzenmöchte – was auch all die Beispiele bisher getan haben. Sie könnendie Eigenschaften eines CSSStyleDeclaration-Objekts lesen, das dieInline-Styles repräsentiert, aber sie liefern nur dann sinnvolle Werte,wenn Ihr JavaScript-Code oder das HTML-Element, das Sie ver-wenden, eine eingebettete style-Eigenschaft besitzt, das den ge-wünschten Eigenschaften einen Wert zuweist. Beispielsweise kannIhr Dokument ein Stylesheet enthalten, das den linken Außen-abstand für alle Absätze auf 30 Pixel setzt. Wenn Sie die EigenschaftmarginLeft eines Ihrer Absatzelemente abfragen, erhalten Sie jedocheinen leeren String, sofern der Absatz nicht eine eigene style-Ei-genschaft besitzt, die die Einstellung des Stylesheets überschreibt.

Manchmal werden Sie es einfacher finden, den Inline-Style einesElements als einzelnen String statt als CSSStyleDeclaration-Objektauszulesen oder zu setzen. Dazu können Sie die Element-MethodengetAttribute() und setAttribute() nutzen, oder Sie greifen auf diecssText-Eigenschaft des CSSStyleDeclaration-Objekts zurück:

// Das style-Attribut von e auf den String s setzen.e.setAttribute("style", s);e.style.cssText = s; // Noch eine Moglichkeit, das

// Gleiche zu tun.

// Inline-Stile von Element e abfragen.s = e.getAttribute("style");s = e.style.cssText; // Eine weitere Moglichkeit

Eine Alternative zum Skripten einzelner CSS-Styles über die Inline-Eigenschaft style besteht darin, den Wert des HTML-Attributsclass zu skripten. Ändert man die class eines Elements, ändert

206 | Kapitel 11: Dokumente skripten

Page 219: 3868993886_Script

man auch die Menge an Stylesheet-Selektoren, die auf das Elementpassen, was dazu führen kann, dass sich mehrere CSS-Eigenschaf-ten gleichzeitig ändern. Stellen Sie sich zum Beispiel vor, Sie wolleneine Möglichkeit finden, die Aufmerksamkeit des Anwenders aufbestimmte Absätze (oder andere Elemente) eines Dokuments zulenken. So definieren Sie vielleicht Aufmerksamkeit heischendeStlyes für Elemente, die den Klassennamen »attention« besitzen:

.attention { /* Styles, die die Aufmerksamkeit auf sichziehen */

background-color: yellow; /* gelber Hintergrund */font-weight: bold; /* fetter Text */border: solid black 2px; /* schwarzer Rahmen */

}

Die Kennung class ist in JavaScript ein reserviertes Wort, daher istdas HTML-Attribut class im JavaScript-Code über den NamenclassName ansprechbar. Folgender Code setzt und löscht die class-Name-Eigenschaft eines Elements, um die »attention«-Klasse fürdieses Element hinzuzufügen oder zu entfernen:

function grabAttention(e) {e.className = "attention";

}function releaseAttention(e) {

e.className = "";}

HTML-Elemente können zu mehr als einer CSS-Klasse gehören,und das Attribut class besteht aus einer durch Leerzeichen getrenn-ten Liste mit Klassennamen. Die className-Eigenschaft besitzteinen irreführenden Namen: classNames wäre eine viel bessereWahl gewesen. Die beiden Funktionen oben gehen davon aus,dass die className-Eigenschaft null oder einen Klassennamen ent-hält – mit mehreren Klassen funktionieren sie nicht. Ist einemElement schon eine Klasse zugewiesen, wird ein Aufruf der Funk-tion grabAttention() die bestehende Klasse überschreiben.

HTML5 kümmert sich um dieses Problem durch die Definition einerclassList-Eigenschaft für jedes Element. Der Wert dieser Eigenschaftist eine DOMTokenList – ein nur lesbares, Array-ähnliches Objekt(siehe den Abschnitt »Array-artige Objekte« auf Seite 112), dessenElemente die einzelnen Klassennamen für das Element enthalten.

Element Style | 207

Page 220: 3868993886_Script

Wichtiger als seine Array-Elemente sind aber die Methoden, diedurch DOMTokenList definiert werden. add() und remove() fügeneinzelne Klassennamen zum Attribut class des Elements hinzu oderentfernen sie von dort. toggle() fügt einen Klassennamen hinzu,wenn nicht schon einer vorhanden ist, ansonsten wird er entfernt.Die Methode contains() prüft schließlich, ob das Attribut classeinen angegebenen Klassennamen enthält.

Wie andere DOM-Collection-Typen ist eine DOMTokenList eine»Live«-Repräsentation der Menge von Klassen eines Elements, keinstatischer Schnappschuss der Klassen beim Abfragen der classList-Eigenschaft. Wenn Sie sich eine DOMTokenList von der classList-Eigenschaft eines Elements holen und dann die className-Eigen-schaft dieses Elements ändern, sind die Änderungen sofort in derToken-Liste sichtbar. Genauso machen sich Änderungen an derToken-Liste auch direkt in der className-Eigenschaft bemerkbar.

Geometrie und ScrollingIn diesem Kapitel haben wir Dokumente bisher als abstrakte Bäumeaus Element- und Text-Knoten betrachtet. Aber wenn ein Browserein Dokument in einem Fenster rendert, erzeugt er eine visuelleDarstellung des Dokuments, in dem jedes Element eine Positionund eine Größe besitzt. Häufig können Webanwendungen Doku-mente als Element-Bäume ansehen, und sie müssen sich nie Ge-danken darum machen, wie diese Elemente auf dem Bildschirmgerendert werden. Aber manchmal ist es notwendig, die genaueGeometrie eines Elements zu bestimmen. CSS kann genutzt wer-den, um die Position eines Elements festzulegen. Wollen Sie CSSnutzen, um ein Element dynamisch neben einem normalen, vomBrowser positionierten Element zu platzieren (wie zum Beispieleinen Tooltip oder ein Callout), müssen Sie die Position diesesElements bestimmen können.

Die Position eines Elements wird in Pixeln gemessen, wobei diex-Koordinate nach rechts und die y-Koordinate nach unten ansteigt.Es gibt aber zwei verschiedene Punkte, die wir als Ursprung desKoordinatensystems ansehen können: Die x- und y-Koordinateneines Elements können relativ zur oberen linken Ecke des Doku-

208 | Kapitel 11: Dokumente skripten

Page 221: 3868993886_Script

ments oder relativ zur oberen linken Ecke des Viewport liegen, indem das Dokument angezeigt wird. In Toplevel-Fenstern und Tabsist der »Viewport« der Teil des Browsers, der tatsächlich Dokumen-teninhalte anzeigt: Der »Rahmen« des Browsers mit Menüs, Tool-bars und Tab-Reitern bleibt außen vor. Bei Dokumenten, die inFrames angezeigt werden, ist der Viewport das <iframe>-Element,das den Frame definiert. In beiden Fällen muss uns bezüglich derPosition eines Elements klar sein, ob wir Dokumenten- oderViewport-Koordinaten verwenden. (Beachten Sie, dass Viewport-Koordinaten manchmal auch als Fenster-Koordinaten bezeichnetwerden.)

Ist das Dokument kleiner als der Viewport oder wurde es nichtgescrollt, befindet sich die obere linke Ecke des Dokuments in deroberen linken Ecke des Viewports und die Dokument- und View-port-Koordinatensysteme sind identisch. Im Allgemeinen müssenwir aber zum Umwandeln zwischen den beiden Koordinatensyste-men die Scroll Offsets hinzufügen oder abziehen. Hat ein Element inDokument-Koordinaten eine y-Koordinate von 200 Pixeln und hatder Benutzer den Browser um 75 Pixel nach unten gescrollt, besitztdas Element in Viewport-Koordinaten eine y-Koordinate von 125Pixel. Oder: Hat ein Element im Viewport-Koordinatensystem einex-Koordinate von 400 Pixel und hat der Benutzer den Viewport um200 Pixel horizontal gescrollt, ist die x-Koordinate in Dokument-Koordinaten 600 Pixel.

Dokument-Koordinaten sind grundlegender als die Viewport-Koor-dinaten, und sie ändern sich nicht, wenn der Anwender scrollt.Trotzdem werden Viewport-Koordinaten in der clientseitigen Pro-grammierung häufig verwendet. Die Dokument-Koordinaten wer-den verwendet, um die Position eines Elements per CSS anzugeben.Wird die Position eines Elements dagegen abgefragt, erhalten wirdie Viewport-Koordinaten. Und registrieren wir Handler-Funktio-nen für Maus-Events, werden die Koordinaten des Mauszeigersebenfalls in Viewport-Koordinaten geliefert.

Um zwischen den Koordinatensystemen zu wechseln, müssen wirdie Scrollbar-Positionen des Browser-Fensters bestimmen können.Die Eigenschaften pageXOffset und pageYOffset des Window-Ob-jekts stellen diese Werte bereit.

Geometrie und Scrolling | 209

Page 222: 3868993886_Script

Manchmal ist es nützlich, die Größe des Viewports zu bestimmen –um zum Beispiel herauszufinden, welche Teile des Dokumentsaktuell sichtbar sind. Soll die Größe des Viewports abgefragt wer-den, können Sie hierfür die Eigenschaften innerWidth und inner-Height des Window-Objekts verwenden.

Um die Größe und Position eines Elements zu ermitteln, rufen Siedessen getBoundingClientRect()-Methode auf. Es werden keineArgumente erwartet, und der Rückgabewert liefert ein Objekt mitden Eigenschaften left, right, top und bottom. Die Eigenschaftenleft und top liefern die x- und y-Koordinaten der oberen linkenEcke des Elements, die Eigenschaften right und bottom die Koor-dinaten der unteren rechten Ecke.

Diese Methode liefert die Elementpositionen in Viewport-Koor-dinaten zurück. Um in Dokument-Koordinaten umzuwandeln, dieauch gültig bleiben, wenn der Anwender im Browser-Fenster scrollt,fügen Sie den Scroll-Offset hinzu:

// Position in Viewport-Koordinaten auslesenvar box = e.getBoundingClientRect();// Umwandlung in Dokument-Koordinatenvar x = box.left + window.pageXOffset;var y = box.top + window.pageYOffset;

Wird ein Element im Browser dargestellt, kann der Inhalt optionalvon Leerraum, dem so genannten Innenabstand (padding), umge-ben sein. Der Innenabstand ist wiederum von einem optionalenRahmen umgeben und der Rahmen von optionalem Außenabstand(Margin). Die von getBoundingClientRect()zurückgelieferten Werteenthalten den Rahmen und den Innenabstand des Elements, abernicht den Außenabstand.

Wir haben oben bereits gesehen, dass Sie die Position der Roll-balken eines Fensters mit pageXOffset und pageYOffset abfragenkönnen. Um die Position des Rollbalkens festzulegen, können Siehierfür die Methode scrollTo() benutzen. Diese Methode erwartetdie x- und y-Koordinaten eines Punktes (in Dokumenten-Koor-dinaten) und setzt diese als Scrollbar-Offset. Das Fenster wird alsoso gescrollt, dass sich der angegebene Punkt in der oberen linkenEcke des Viewports befindet. Geben Sie einen Punkt an, der zu naham unteren Ende oder am rechten Rand des Dokuments ist, wird

210 | Kapitel 11: Dokumente skripten

Page 223: 3868993886_Script

der Browser nur so weit wie möglich scrollen, aber nicht ganz zumPunkt gelangen.

Die Window-Methode scrollBy() entspricht scrollTo(), allerdingssind ihre Argumente relativ und werden zum aktuellen Scrollbar-Offset hinzuaddiert.

Häufig wollen wir nicht an eine numerische Position im Dokumentscrollen, sondern dafür sorgen, dass ein bestimmtes Element imDokument in den sichtbaren Bereich gelangt. Sie können die Po-sition des Elements mit getBoundingClientRect() ermitteln, diesePosition in Dokumenten-Koordinaten umwandeln und dann dieMethode scrollTo() nutzen, aber es ist einfacher, schlicht dieMethode scrollIntoView() für das gewünschte HTML-Elementaufzurufen. Diese Methode stellt sicher, dass das Element, für dassie aufgerufen wurde, im Viewport sichtbar ist. Standardmäßigversucht sie, den oberen Rand des Elements an den oberen Randdes Viewports oder zumindest in seine Nähe zu verschieben. Über-geben Sie als einziges Argument false, wird versucht, den unterenRand des Elements an den unteren Rand des Viewports zu ver-schieben. Der Browser wird den Viewport auch horizontal verschie-ben, wenn das notwendig ist, um das Element sichtbar werden zulassen.

Geometrie und Scrolling | 211

Page 224: 3868993886_Script
Page 225: 3868993886_Script

KAPITEL 12

Events

Clientseitige JavaScript-Programme nutzen ein asynchrones, Event-getriebenes Programmiermodell. Dabei erzeugt der Webbrowserimmer dann ein Event, wenn etwas Interessantes passiert – im Do-kument, im Browser oder bei einem Element oder Objekt, das damitverbunden ist. Beispielsweise erzeugt der Webbrowser ein Event,wenn ein Dokument vollständig geladen wurde, wenn der Benutzerdie Maus über einen Hyperlink bewegt oder auf der Tastatur eineTaste drückt. Wenn sich eine JavaScript-Anwendung für eine be-stimmte Art von Events interessiert, kann sie eine oder mehrereFunktionen registrieren, wenn Events von diesem Typ ausgelöstwerden.

Der Event-Typ ist ein String, der festlegt, was für ein Event auf-getreten ist. Der Typ »mousemove« zum Beispiel bedeutet, dass derAnwender die Maus bewegt hat. Der Typ »keydown« bedeutet, dasseine Taste auf der Tastatur gedrückt wurde. Und der Typ »load«bedeutet, dass ein Dokument (oder eine andere Ressource) fertigaus dem Netz geladen wurde. Da der Typ eines Events nur einString ist, wird er manchmal auch als Event-Name bezeichnet, undwir nutzen tatsächlich diesen Namen, um die Art von Event deut-lich zu machen, über die wir sprechen.

Das Event-Ziel (oder Event Target) ist das Objekt, bei dem dasEvent aufgetreten ist oder mit dem das Event verbunden ist. Redenwir von einem Event, müssen wir immer den Typ und das Ziel fest-legen, zum Beispiel ein load-Event für ein Window oder ein click-Event für ein <button>-Element. Window-, Document- und Ele-ment-Objekte sind die häufigsten Event-Ziele im clientseitigen

| 213

Page 226: 3868993886_Script

JavaScript, aber manche Events werden für andere Arten vonObjekten ausgelöst. In Kapitel 13 werden wir ein readystatechange-Event kennenlernen, das zum Beispiel für ein XMLHttpRequest-Objekt ausgelöst wird.

Ein Event-Handler oder Event-Listener ist eine Funktion, die sichum ein Event kümmert beziehungsweise darauf reagiert. Anwen-dungen registrieren ihre Event-Handler-Funktionen beim Web-browser, wobei sie einen Event-Typ und ein Event-Ziel angeben.Wird dann ein Event vom angegebenen Typ für das angegebene Zielausgelöst, ruft der Browser den Handler auf. Werden Event-Hand-ler für ein Objekt aufgerufen, sagen wir manchmal, dass derBrowser das Event »gefeuert«, »getriggert« oder »dispatcht« hat.

Ein Event-Objekt ist ein Objekt, das mit einem bestimmten Eventverbunden ist und die Details zu diesem Element enthält. Event-Objekte werden als Argumente an die Event-Handler-Funktionenübergeben. Alle Event-Objekte besitzen eine Eigenschaft type, dieden Event-Typ angibt, und eine Eigenschaft target, die das Event-Ziel definiert. Jeder Event-Typ definiert eine Reihe von Eigenschaf-ten für sein zugehöriges Event-Objekt. Das mit einem Maus-Eventverbundene Objekt enthält zum Beispiel die Koordinaten des Maus-zeigers, während das mit einem Tastatur-Event verbundene Objektweiß, welche Taste zusammen mit welchen Hilfstasten gedrücktwurde. Viele Event-Typen definieren nur ein paar Standardeigen-schaften – wie type und target – und besitzen nicht viel mehrnützliche Informationen. Bei solchen Events ist das reine Auftretendes Events das Entscheidende – nicht die Event-Details.

Die Event-Propagation ist der Prozess, bei dem der Browser ent-scheidet, für welche Objekte Event-Handler ausgelöst werden sol-len. Bei Events, die für ein einzelnes Objekt spezifisch sind (wie dasload-Event des Window-Objekts), ist keine Propagation notwendig.Aber wenn bestimmte Arten von Events für Dokument-Elementeauftreten, werden sie durch den Dokument-Baum hindurch nachoben propagiert (sie »steigen auf«, was auch als Bubbling bezeichnetwird). Bewegt der Anwender die Maus über einen Hyperlink, wirddas mousemove-Event zunächst für das <a>-Element ausgelöst, dasdiesen Link definiert. Dann wird es für die Container-Elementeausgelöst: vielleicht für ein <p>-Element, ein <div>-Element und

214 | Kapitel 12: Events

Page 227: 3868993886_Script

das Document-Objekt selbst. Manchmal ist es einfacher, eineneinzelnen Event-Handler für ein Dokument oder ein anderes Con-tainer-Element zu registrieren, anstatt die Handler für jedes einzelneElement zu registrieren, an dem Sie interessiert sind. Ein Event-Handler kann die Propagation eines Events stoppen, sodass es nichtweiter aufsteigt und weitere Handler von Container-Elementenauslöst.

In einer anderen Form der Event-Propagation – bekannt als Event-Capturing – werden die Handler für Container-Elemente registriert,die die Möglichkeit haben, Events abzufangen, bevor sie beimeigentlichen Ziel landen.

Manche Events besitzen Standardaktionen, die mit ihnen verbundensind. Wird zum Beispiel ein click-Event für einen Hyperlink aus-gelöst, besteht die Standardaktion des Browsers darin, dem Link zufolgen und eine neue Seite zu laden. Durch die Verwendung vonEvent-Handlern können Sie dieses Standardverhalten verhindern,indem Sie eine Methode am Event-Objekt aufrufen.

Mit den so definierten Begriffen können wir uns nun den Events unddem Event-Handling im Detail zuwenden. Der erste folgende Ab-schnitt ist ein Überblick über die vielen Event-Typen, die von Web-browsern unterstützt werden. Die nächsten beiden Abschnitte erläu-tern, wie Event-Handler registriert werden und wie der Browser dieseHandler aufruft.

Event-TypenDieser Abschnitt behandelt die verschiedenen Arten von Events:Formular-Events, Maus-Events, Tastatur-Events und so weiter. Je-der Abschnitt beschreibt die verschiedenen Event-Typen einer Ka-tegorie und erklärt die wichtigsten Eigenschaften der mit Eventsdieses Typs verbundenen Event-Objekte.

Formular-EventsFormulare und Hyperlinks waren die ersten skriptbaren Elementeauf einer Webseite – damals in den frühen Tagen des Webs und vonJavaScript. Das bedeutet, dass Formular-Events zu den stabilsten

Event-Typen | 215

Page 228: 3868993886_Script

und am besten unterstützten Event-Typen gehören. <form>-Elemen-te feuern submit-Events, wenn das Formular abgeschickt wird, undreset-Events, wenn das Formular zurückgesetzt wird. Button-ähn-liche Formular-Elemente (wie Radiobuttons und Checkboxen) lö-sen click-Events aus, wenn der Benutzer mit ihnen interagiert.Formular-Elemente, die eine Art von Statusänderung ermöglichen,feuern im Allgemeinen change-Events, wenn der Anwender denStatus durch die Eingabe von Text, das Auswählen eines Elementsoder das Markieren einer Checkbox ändert. Bei Texteingabefeldernwird das change-Event erst ausgelöst, wenn der Benutzer mit derEingabe fertig ist und durch Drücken der Tab-Taste oder das Ankli-cken eines anderen Elements das Textfeld verlässt. Formular-Ele-mente reagieren auf Änderungen am Tastatur-Fokus durch das Aus-lösen der focus- und blur-Elemente, wenn sie den Fokus erhaltenbeziehungsweise verlieren.

Die submit- und reset-Events besitzen ebenso wie manche click-Events Standardaktionen, die durch Event-Handler verhindert wer-den können. Die focus- und blur-Events steigen nicht per Bubblingnach oben, wie es die anderen Formular-Events tun.

Window-EventsWindow-Events stehen für Ereignisse, die mit dem Browser-Fensterselbst zu tun haben und nicht mit bestimmten Dokumentinhalten,die innerhalb des Fensters angezeigt werden. (Für manche dieserEvents kann allerdings auch ein Event mit dem gleichen Namen fürDokument-Elemente ausgelöst werden.)

Das load-Event ist das wichtigste dieser Elemente: Es wird aus-gelöst, wenn ein Dokument und all seine externen Ressourcen (wieBilder) vollständig geladen sind und dem Benutzer angezeigt wer-den. DOMContentLoaded und readystatechange sind Alternativenzum load-Event: Sie werden früher ausgelöst, wenn das Dokumentund seine Elemente bearbeitet werden können, aber bevor externeRessourcen vollständig geladen sind.

Das unload-Event ist das Gegenstück zu load: Es wird ausgelöst,wenn der Anwender von einem Dokument wegnavigiert. Ein unlo-ad-Event-Handler kann zum Beispiel genutzt werden, um den

216 | Kapitel 12: Events

Page 229: 3868993886_Script

Status eines Benutzers zu sichern, aber nicht, um die Navigationabzubrechen. Das beforeunload-Event entspricht unload, ermög-licht es aber, den Benutzer zu fragen, ob er Ihre Webseite wirklichverlassen will. Gibt ein Handler für beforeunload einen Stringzurück, wird dieser dem Anwender in einem Bestätigungsdialog an-gezeigt, bevor die neue Seite geladen wird. Dort hat er dann dieMöglichkeit, die Navigation abzubrechen und auf Ihrer Seite zubleiben.

Die weiter oben beschriebenen focus- und blur-Events für Formu-lar-Elemente werden auch als Window-Events genutzt: Sie werdenfür ein Fenster ausgelöst, wenn dieses Browserfenster den Tastatur-Fokus des Betriebssystems erhält beziehungsweise verliert.

Schließlich werden die resize- und scroll-Events für ein Windowausgelöst, wenn der Anwender das Browserfenster in der Größeanpasst oder scrollt. Scroll-Events können auch für beliebige scroll-bare Dokument-Elemente ausgelöst werden, wie zum Beispiel fürsolche, die mit der CSS-Eigenschaft overflow versehen sind.

Maus-EventsMaus-Events werden erzeugt, wenn der Anwender die Maus übereinem Dokument bewegt oder klickt. Diese Events werden für dasam tiefsten verschachtelte Element ausgelöst, über dem sich derBrowser befindet, aber sie steigen durch das Dokument auf. DasEvent-Objekt, das Maus-Event-Handlern übergeben wird, besitztEigenschaften für die Position und den Tasten-Status der Maus,zudem wird mitgeliefert, ob Hilfstasten auf der Tastatur gedrücktwurden, als das Ereignis eintrat. Die Eigenschaften clientX undclientY geben die Position der Maus in Fenster-Koordinaten an. DieEigenschaften altKey, ctrlKey, metaKey und shiftKey werden auftrue gesetzt, wenn die entsprechenden Hilfstasten auf der Tastaturgedrückt sind. Bei click-Events gibt zudem die Eigenschaft detailan, ob es sich um einen Einfach-, Doppel- oder Dreifach-Klickhandelt.

Das mousemove-Event wird immer dann ausgelöst, wenn der An-wender die Maus bewegt. Diese Events treten sehr häufig auf, daherdürfen mousemove-Handler keine rechenaufwendigen Aufgaben

Event-Typen | 217

Page 230: 3868993886_Script

erledigen. Die mousedown- und mouseup-Events werden ausgelöst,wenn der Benutzer eine Maustaste drückt oder loslässt. Durch dasRegistrieren eines mousedown-Handlers, der einen mousemove-Handler registriert, können Sie auf ein Ziehen mit der Maus reagie-ren. Um das richtig zu machen, muss man Maus-Events auch dannnoch erhalten können, wenn die Maus aus dem Start-Element he-rausbewegt wurde.

Nach einer mousedown- und mouseup-Event-Sequenz triggert derBrowser auch noch ein click-Event. Klickt der Benutzer eine Maus-taste zweimal hintereinander (innerhalb einer ausreichend kurzenZeitspanne), folgt auf das zweite click-Event ein dblclick-Event.Browser zeigen häufig ein Kontextmenü an, wenn die rechte Maus-taste gedrückt wurde. Vor dem Anzeigen solch eines Menüs feuernsie meist ein contextmenu-Event. Wenn Sie dieses Event abbrechen,können Sie das Anzeigen des Menüs verhindern. Das ist zudem eineeinfache Möglichkeit, um über das Anklicken mit der rechten Maus-taste informiert zu werden.

Bewegt der Anwender die Maus, sodass sie ein neues Elementerreicht, löst der Browser ein mouseover-Event für dieses Elementaus. Bewegt sich die Maus weiter, sodass sie das Element verlässt,löst der Browser dafür ein mouseout-Event aus. Für diese Eventsenthält das Event-Objekt eine relatedTarget-Eigenschaft, die dasandere Element in dieser Bewegung angibt. mouseover- und mouse-out-Events steigen wie alle Maus-Events nach oben auf. Das ist oftunpraktisch, weil Sie beim Auslösen eines mouseout-Triggers prü-fen müssen, ob die Maus tatsächlich das für Sie interessante Ele-ment verlassen hat oder ob der Zeiger nur von einem Kind desElements zum nächsten gewandert ist. Die Events mouseenter undmouseleave sind neue »non-bubbling«-Versionen von mouseoverund mouseout, die von vielen modernen Browser bereits unterstütztwerden.

Dreht der Anwender das Mausrad, lösen Browser ein mousewheel-Event aus. Das übergebene Event-Objekt enthält Eigenschaften, indenen steht, wie weit und in welche Richtung das Rad gedreht wur-de.

218 | Kapitel 12: Events

Page 231: 3868993886_Script

Tastatur-EventsHat der Webbrowser den Tastatur-Fokus, erzeugt er immer dannEvents, wenn der Anwender eine Taste auf der Tastatur drückt oderloslässt. Tastaturkürzel, die für das Betriebssystem oder denBrowser selbst eine Bedeutung haben, werden häufig schon abge-fangen und sind dann für JavaScript-Event-Handler nicht sichtbar.Tastatur-Events werden für das Dokument-Element ausgelöst, dasden Tastatur-Fokus besitzt, und sie steigen dann bis zum Doku-ment und Fenster auf. Besitzt kein Element den Fokus, werden dieEvents direkt für das Dokument ausgelöst. Tastatur-Event-Hand-lern wird ein Event-Objekt mit einem Feld keyCode mitgegeben, indem steht, welche Taste gedrückt oder losgelassen wurde. NebenkeyCode besitzt das Event-Objekt für Tasten-Events noch die FelderaltKey, ctrlKey, metaKey und shiftKey, mit denen der Status derHilfstasten auf der Tastatur dokumentiert wird.

Die keydown- und keyup-Events sind ganz schlichte Tastatur-Events:Sie werden immer dann ausgelöst, wenn eine Taste (selbst eine Hilfs-taste) gedrückt oder losgelassen wird. Erzeugt ein keydown-Event eindruckbares Zeichen, wird nach dem keydown-, aber vor dem keyup-Event ein zusätzliches keypress-Event ausgelöst. (Falls eine Tastegedrückt gehalten wird, bis die Wiederholung anspringt, kann dieszu vielen keypress-Events führen, bevor das keyup-Event kommt.)Das keypress-Event ist ein »fortgeschritteneres« Text-Event, und seinEvent-Objekt gibt das erzeugte Zeichen an, nicht die gedrückte Taste.In manchen Browsern (insbesondere Firefox) müssen Sie anstelle vonkeyCode die charCode-Eigenschaft eines keypress-Event-Objekts ver-wenden.

Die keydown-, keyup- und keypress-Events werden von allenBrowsern unterstützt, aber es gibt ein paar Probleme mit der Inter-operabilität, da die Werte der keyCode-Eigenschaft des Event-Ob-jekts nicht besonders gut standardisiert sind.

HTML5-EventsHTML5 und die zugehörigen Standards definieren eine ganze Reiheneuer APIs für Webanwendungen. Viele dieser APIs definierenEvents. Dieser Abschnitt führt diese HTML5- und Webanwendungs-

Event-Typen | 219

Page 232: 3868993886_Script

Events auf und beschreibt sie kurz. Manche dieser Events lassen sichjetzt schon verwenden; andere sind noch nicht sehr häufig imple-mentiert.

Eines der am häufigsten hervorgehobenen Features von HTML5 istdie Aufnahme von <audio>- und <video>-Elementen für das Abspie-len von Audio- und Videomaterial. Diese Elemente besitzen einelange Liste von Events, die sie auslösen, um über Netzwerk-Ereig-nisse, den Status der Datenzwischenspeicherung und das Abspielenzu informieren:

canplay loadeddata playing stalledcanplaythrough loadedmetadata progress suspenddurationchange loadstart ratechange timeupdateemptied pause seeked volumechangeended play seeking waiting

Diesen Medien-Events wird ein normales Event-Objekt ohne be-sondere Eigenschaften mitgegeben. Die target-Eigenschaft verweistaber auf das <audio>- oder <video>-Element, das dann viele wichtigeEigenschaften und Methoden besitzt.

Die Drag-and-Drop-API von HTML5 ermöglicht es JavaScript-An-wendungen, an betriebssystembasierten Drag-and-Drop-Operatio-nen teilzuhaben und Daten zwischen Webanwendungen und An-wendungen auszutauschen, die direkt im Betriebssystem laufen. DieAPI definiert die folgenden sieben Event-Typen:

dragstart drag dragenddragenter dragover dragleavedrop

Diese Drag-and-Drop-Events werden zusammen mit einem Event-Objekt ausgelöst, das dem der Maus-Events entspricht. Eine zusätz-liche Eigenschaft dataTransfer verweist auf ein DataTransfer-Ob-jekt mit Informationen über die zu übermittelnden Daten und dieverfügbaren Formate.

HTML5 definiert einen Verlaufsmanagement-Mechanismus, überden Webanwendungen mit den Zurück- und Vorwärts-Buttons desBrowsers interagieren können. Dazu gehören Events mit den Na-men hashchange und popstate. Diese Events sind wie load und

220 | Kapitel 12: Events

Page 233: 3868993886_Script

unload Statusänderungs-Events, die für das Window-Objekt undnicht für ein einzelnes Document-Objekt ausgelöst werden.

HTML5 definiert auch eine Reihe neuer Features für HTML-For-mulare. Neben dem weiter oben beschriebenen Standardisieren desForm-Input-Events definiert HTML5 noch einen Formular-Validie-rungsmechanismus mit einem invalid-Event, das für Formular-Ele-mente gefeuert wird, bei denen die Validierung fehlschlägt. DieBrowserhersteller sind bei der Implementierung allerdings ein wenigzögerlich (außer Opera), daher werden sie in diesem Buch nichtweiter behandelt.

HTML5 besitzt auch eine Unterstützung für Offline-Webanwen-dungen, die lokal in einem Anwendungs-Cache installiert werdenkönnen, sodass sie sich auch ausführen lassen, wenn der Browseroffline ist (weil zum Beispiel ein mobiles Gerät gerade keine Netz-werkverbindung hat). Die beiden wichtigsten Events sind in diesemUmfeld die offline- und online-Events: Sie werden für das Window-Objekt ausgelöst, wenn der Browser eine Netzwerkverbindungverliert oder wieder herstellen kann. Eine ganze Reihe zusätzlicherEvents ist definiert, um über den Download-Fortschritt oder dieUpdates der Anwendung zu informieren:

cached checking downloading errornoupdate obsolete progress updateready

Eine ganze Reihe neuer Webanwendungs-APIs nutzt ein message-Event zur asynchronen Kommunikation. Die Cross-DocumentMessaging API ermöglicht es Skripten in einem Dokument voneinem Server Nachrichten mit Skripten in einem Dokument voneinem anderen Server auszutauschen. Das umgeht die Einschrän-kungen der Same-Origin-Policy (siehe den Abschnitt »Die Same-Origin-Policy« auf Seite 183), ohne die Sicherheit zu gefährden. JedeNachricht, die geschickt wird, löst ein message-Event für dasWindow des empfangenden Dokuments aus. Das übergebeneEvent-Objekt besitzt eine data-Eigenschaft mit dem Inhalt derNachricht und mit source- und origin-Eigenschaften, die denAbsender der Nachricht identifizieren. Das message-Event wirdauch bei der Kommunikation mit Web Workers und für die Netz-werk-Kommunikation über Server-Sent-Events und WebSocketsgenutzt.

Event-Typen | 221

Page 234: 3868993886_Script

HTML5 und die zugehörigen Standards definieren ein paar Events,die für andere Objekte als Fenster, Dokumente und Dokument-Ele-mente ausgelöst werden. Version 2 der XMLHttpRequest-Spezifi-kation, aber auch die File API-Spezifikation definieren eine Reihevon Events, die den Verlauf des asynchronen I/O dokumentieren.Sie lösen Events für ein XMLHttpRequest- oder FileReader-Objektaus. Jede Lese-Operation beginnt mit einem loadstart-Event, gefolgtvon progress-Events und einem loadend-Event. Zusätzlich endetjede Operation mit einem load-, error- oder abort-Event, auf dasdann noch das abschließende loadend-Event folgt.

Schließlich definieren HTML5 und die zugehörigen Standards nochein paar weitere Event-Typen. Die Web Storage API definiert einstorage-Event (für das Window-Objekt), durch das man über Än-derungen an gespeicherten Daten informiert wird. HTML5 stan-dardisiert auch die beforeprint- und afterprint-Events, die ursprüng-lich von Microsoft im IE eingeführt wurden. Wie ihr Name schonvermuten lässt, werden diese Events direkt vor und nach demAusdrucken des Dokuments für das Window ausgelöst, sodassman zusätzliche Informationen bereitstellen kann, wie zum Beispieldas Druckdatum. (Diese Events sollten nicht genutzt werden, umdie Druck-Darstellung eines Dokuments zu verändern, da es dafürschon die CSS-Media-Typen gibt.)

Touchscreen- und Mobil-EventsDie Verbreitung mobiler Geräte mit Touchscreens erforderte eineneue Kategorie von Events. In vielen Fällen werden Touchscreen-Events auf klassische Event-Typen wie click und scroll abgebildet.Aber nicht jede Interaktion mit einem Touchscreen-UI emuliert eineMaus, und nicht alle Berührungen können als Maus-Events behan-delt werden. Dieser Abschnitt erklärt kurz die Gesten- und Touch-Events, die von Safari erzeugt werden, wenn es auf einem iPhoneoder iPad läuft. Zudem wird das orientationchange-Event behan-delt, das ausgelöst wird, wenn der Anwender das Gerät dreht.

Safari erzeugt Gesten-Events für Skalier- und Rotationsgesten mitzwei Fingern. Das gesturestart-Event wird ausgelöst, wenn mit derGeste begonnen wird, am Ende folgt dann ein gestureend. Zwischen

222 | Kapitel 12: Events

Page 235: 3868993886_Script

diesen beiden Events gibt es eine Folge von gesturechange-Events,die den Verlauf der Geste protokollieren. Das mit diesen Eventsmitgegebene Event-Objekt besitzt die numerischen Eigenschaftenscale und rotation. Die scale-Eigenschaft enthält das Verhältnisdes aktuellen Abstands der beiden Finger zum Abstand am Anfangder Geste. Eine »Zusammenzieh«-Geste besitzt einen scale-Wert,der kleiner als 1.0 ist, während eine »Aufzieh«-Geste einen scale-Wert größer als 1.0 hat. Die rotation-Eigenschaft ist der Winkel,um den die Finger seit dem Beginn des Events rotiert wurden. Siewird in Grad angegeben, wobei positive Werte eine Drehung imUhrzeigersinn angeben.

Gesten-Events sind High-Level-Events, die Sie über eine schoninterpretierte Geste informieren. Wollen Sie Ihre eigenen Gestenimplementieren, können Sie auf Low-Level-Touch-Events lauschen.Berührt ein Finger den Bildschirm, wird ein touchstart-Event aus-gelöst. Bewegt sich der Finger, erhält man ein touchmove-Event.Und wenn der Finger wieder angehoben wird, wird ein touchend-Event ausgelöst. Anders als bei Maus-Events geben Touch-Eventsnicht direkt die Koordinaten der Berührung wieder. Stattdessenbesitzt das Objekt, das mit einem Touch-Event mitgesendet wird,eine changedTouches-Eigenschaft. Diese Eigenschaft ist ein Array-ähnliches Objekt, dessen Elemente die Position einer jeden Berüh-rung beschreiben.

Das orientationchanged-Event wird für das Window-Objekt aufGeräten ausgelöst, die der Anwender aus dem Portrait- in denLandscape-Modus drehen kann. Das mit solch einem Event mit-gegebene Objekt ist für sich allein nicht nützlich. Aber im mobilenSafari gibt die orientation-Eigenschaft des Window-Objekts dieaktuelle Ausrichtung als einen der Werte 0, 90, 180 oder -90 an.

Event-Handler registrierenEs gibt zwei Vorgehensweisen, um Event-Handler zu registrieren.Die eine ist das Setzen einer Eigenschaft für das Objekt oderDokument-Element, das das Event-Ziel ist. Die andere ist dasÜbergeben eines Handlers an eine Methode des Objekts oder Ele-ments. Um das Ganze noch komplizierter zu machen, gibt es von

Event-Handler registrieren | 223

Page 236: 3868993886_Script

jeder Technik jeweils zwei Versionen. Sie können eine Event-Hand-ler-Eigenschaft in JavaScript-Code oder bei Dokument-Elementenüber das entsprechende Attribut direkt in HTML setzen.

Event-Handler-Eigenschaften setzenDie einfachste Möglichkeit, einen Event-Handler zu registrieren,besteht darin, eine Eigenschaft des Event-Ziels auf die gewünschteEvent-Handler-Funktion zu setzen. Die Konvention ist, dass dieEvent-Handler-Eigenschaften Namen haben, die aus »on«, gefolgtvom Event-Namen bestehen: onclick, onchange, onload, onmouse-over und so weiter. Achten Sie bei diesen Eigenschaftsnamen aufGroß- und Kleinschreibung: Alle Buchstaben werden kleingeschrie-ben, auch wenn der Event-Typ (wie bei »readystatechange«) ausmehreren Wörtern besteht:

// Setze die onload-Eigenschaft des Window-Objekts.// Die Funktion ist der Event-Handler:// Sie wird aufgerufen, wenn das Dokument// geladen wurde.window.onload = function() {

// Suche nach einem <form>-Element.var elt = document.getElementById("address");// Registriere eine Event-Handler-Funktion, die direkt// vor dem Abschicken des Formulars aufgerufen wird.elt.onsubmit = function() { return validate(this); }

}

Der Nachteil der Event-Handler-Eigenschaften ist, dass bei ihnendavon ausgegangen wird, dass Event-Ziele höchstens einen Handlerpro Event-Typ besitzen. Wenn Sie eine Bibliothek für die Benut-zung in beliebigen Dokumenten erstellen, können Sie sich auf dieseTechnik allerdings nicht verlassen.

Event-Handler-Attribute setzenDie Event-Handler-Eigenschaften eines Dokument-Elements kön-nen auch über die Attribute des entsprechenden HTML-Tags ge-setzt werden. Wenn Sie das tun, sollte der Attributswert ein Stringmit JavaScript-Code sein. Dabei handelt es sich um den Rumpf derEvent-Handler-Funktion, nicht um eine vollständige Funktions-

224 | Kapitel 12: Events

Page 237: 3868993886_Script

deklaration. Ihr HTML-Event-Handler-Code sollte also nicht vongeschweiften Klammern umschlossen und mit dem Schlüsselwortfunction versehen sein. Zum Beispiel:

<button onclick="alert('Danke sehr');">Hier klicken</button>

Enthält ein HTML-Event-Handler-Attribut mehrere JavaScript-An-weisungen, müssen Sie diese durch Semikolons trennen oder denAttributswert auf mehrere Zeilen umbrechen.

Manche Event-Typen gehen direkt an den Browser und nicht an einbestimmtes Dokument-Element. In JavaScript werden Handler fürdiese Events beim Window-Objekt registriert. In HTML bringenwir sie im <body>-Tag unter; der Browser registriert sie dann für dasWindow. Die folgende Liste enthält alle solche Event-Handler, diedurch den Entwurf der HTML5-Spezifikation definiert sind:

onafterprint onfocus ononline onresizeonbeforeprint onhashchange onpagehide onstorageonbeforeunload onload onpageshow onundoonblur onmessage onpopstate onunloadonerror onoffline onredo

Wenn Sie einen String mit JavaScript-Code als Wert eines HTML-Element-Handler-Attributs angeben, wandelt der Browser IhrenString in eine Funktion um, die in etwa wie folgt aussieht:

function(event) {with(document) {

with(this.form || {}) {with(this) {

/* hier steht Ihr Code */}

}}

}

Weitere Informationen über das oben gezeigte event-Argument unddie with-Anweisungen finden Sie im Abschnitt »Aufruf eines Event-Handlers« auf Seite 227.

addEventListener()Jedes Objekt, das ein Event-Ziel sein kann – einschließlich derWindow- und Document-Objekte und aller Dokument-Elemente –

Event-Handler registrieren | 225

Page 238: 3868993886_Script

definiert eine Methode namens addEventListener(), mit der Sieeinen Event-Handler für dieses Ziel registrieren können. addEvent-Listener() erwartet drei Argumente. Das erste ist der Event-Typ,für den der Handler registriert wird. Der Event-Typ (oder Event-Name) ist ein String ohne das »on«-Präfix, das genutzt wird, wennman Event-Handler-Eigenschaften setzt. Das zweite Argument vonaddEventListener() ist die Funktion, die aufgerufen werden soll,wenn die angegebene Art von Event ausgelöst wird. Das letzteArgument für addEventListener() ist ein optionaler boolescherWert. In der Regel wird entweder false oder einfach gar kein Wertübergeben. Bei true wird Ihre Funktion als fangender Event-Hand-ler (Capturing Event Handler) registriert und in einer anderen Phasedes Event-Dispatching aufgerufen. Wir werden das Event-Captu-ring im Abschnitt »Event-Propagation« auf Seite 230 behandeln.

Der folgende Code registriert zwei Handler für das click-Event eines<button>-Elements. Beachten Sie die Unterschiede zwischen denbeiden angewendeten Techniken:

<button id="mybutton">Hier klicken</button><script>var b = document.getElementById("mybutton");b.onclick = function() { alert("Danke!"); };b.addEventListener("click",

function() { alert("Nochmals Danke!"); });</script>

Der Aufruf von addEventListener() mit »click« als erstem Argu-ment hat keinen Einfluss auf den Wert der onclick-Eigenschaft. Imobigen Code erzeugt ein Klick auf den Button zwei alert()-Dialog-fenster. Wichtiger ist aber, dass Sie addEventListener() mehrfachaufrufen können, um mehr als eine Handler-Funktion für dengleichen Event-Typ ein und desselben Objekts zu registrieren.Wird ein Element für ein Objekt ausgelöst, werden alle für diesenEvent-Typ registrierten Handler aufgerufen – in der Reihenfolgeihrer Registrierung. Ruft man addEventListener() mehr als einmalfür das gleiche Objekt mit den gleichen Argumenten auf, hat dieskeinen Effekt – die Handler-Funktion bleibt nur einmal registriert,und der wiederholte Aufruf ändert auch nichts an der Aufrufreihen-folge der Handler.

226 | Kapitel 12: Events

Page 239: 3868993886_Script

Zu addEventListener() gehört auch eine Methode removeEventLis-tener() mit den gleichen drei Argumenten. Mit dieser Methodewird eine Event-Handler-Funktion von einem Objekt entfernt. Esist häufig nützlich, einen Event-Handler temporär zu registrierenund ihn nach seinem Aufruf wieder zu deregistrieren. Erhalten Siezum Beispiel ein mousedown-Event, können Sie Event-Handler fürmousemove und mouseup registrieren, um herauszufinden, ob derBenutzer mit der Maus zieht. Ist das mouseup-Event dann einge-troffen, deregistrieren Sie diese Handler wieder. In solch einerSituation kann der entsprechende Code wie folgt aussehen:

document.removeEventListener("mousemove",handleMove, true);

document.removeEventListener("mouseup",handleUp, true);

Aufruf eines Event-HandlersHaben Sie einen Event-Handler registriert, wird dieser vom Web-browser automatisch aufgerufen, wenn ein Event des angegebenenTyps für das gewünschte Objekt ausgelöst wird. Dieser Abschnittbeschreibt den Aufruf des Event-Handlers im Detail, geht auf seineArgumente und den Aufruf-Kontext ein (den Wert this) sowie aufden Geltungsbereich und die Bedeutung des Rückgabewerts.

Neben der Beschreibung des Aufrufs einzelner Handler wird indiesem Abschnitt auch die Event-Propagation erläutert: wie eineinzelnes Event den Aufruf vieler Handler für das ursprünglicheEvent-Ziel und auch für die übergeordneten Container-Elementeauslösen kann.

Argument eines Event-HandlersEvent-Handler werden mit einem Event-Objekt als einzigem Argu-ment aufgerufen. Die Eigenschaften des Event-Objekts (weitervorne in diesem Kapitel beschrieben) geben über weitere Detailsdes Events Auskunft.

Wenn Sie durch das Setzen eines HTML-Attributs einen Event-Handler einrichten, wie im Abschnitt »Event-Handler-Attribute set-

Aufruf eines Event-Handlers | 227

Page 240: 3868993886_Script

zen« auf Seite 224 beschrieben, konvertiert der Browser den Stringmit JavaScript-Code in eine Funktion mit einem einzelnen Argumentnamens event. Dadurch können HTML-Event-Handler das Event-Objekt als event ansprechen.

KontextRegistrieren Sie einen Event-Handler über das Setzen einer Eigen-schaft, sieht es so aus, als ob Sie eine neue Methode für einDokument definieren:

e.onclick = function() { /* Handler-Code */ };

Es ist nicht überraschend, dass Event-Handler als Methoden desObjekts aufgerufen werden, für das sie definiert sind. Im Rumpfeines Event-Handlers bezieht sich das Schlüsselwort this daher aufdas Event-Ziel.

Für Handler, die per addEventListener() registriert wurden, ist dasEvent-Ziel ebenfalls der Wert von this.

GeltungsbereichWie alle JavaScript-Funktionen besitzen Event-Handler einen Gel-tungsbereich. Sie werden in dem Geltungsbereich ausgeführt, indem sie definiert sind, nicht in dem, in dem sie aufgerufen werden.Zudem haben sie Zugriff auf beliebige lokale Variablen in diesemGeltungsbereich.

Event-Handler, die als HTML-Attribute registriert wurden, sindallerdings ein Sonderfall. Sie werden in Top-Level-Funktionen um-gewandelt, die Zugriff auf globale Variablen, nicht aber auf lokaleVariablen besitzen. Aber aus historischen Gründen laufen sie ineiner angepassten Geltungsbereichskette. Über HTML-Attributedefinierte Event-Handler können die Eigenschaften des Ziel-Ob-jekts, des Container-<form>-Objekts (sofern vorhanden) und desDocument-Objekts nutzen, als wären es lokale Variablen. DerAbschnitt »Event-Handler-Attribute setzen« auf Seite 224 zeigt,wie eine Event-Handler-Funktion aus einem HTML-Event-Hand-ler-Attribut erstellt wird, und der Code dort gibt mit with-Anwei-sungen die angepasste Geltungsbereichskette in etwa wieder.

228 | Kapitel 12: Events

Page 241: 3868993886_Script

HTML-Attribute sind kein guter Ort für lange Code-Texte, unddiese angepasste Geltungsbereichskette erlaubt hilfreiche Abkür-zungen. Sie können tagName statt this.tagName verwenden. Sie kön-nen getElementById statt document.getElementById nutzen. Und fürDokument-Elemente, die sich in einem <form> befinden, können Sieauf andere Formular-Elemente über die ID zugreifen, zum Beispielüber zipcode statt über this.form.zipcode.

Andererseits sorgt die angepasste Geltungsbereichskette von HTML-Event-Handlern für Fallstricke, da die Eigenschaften jedes Objekts inder Kette die globalen Eigenschaften gleichen Namens überdecken.Dieses Problem tritt besonders bei Formularen auf, da die Namenund IDs von Formular-Elementen Eigenschaften des enthaltendenFormular-Elements definieren. Besitzt also ein Formular ein Elementmit der ID »location«, müssen zum Beispiel alle HTML-Event-Hand-ler innerhalb dieses Formulars window.location statt location ver-wenden, wenn sie auf das Location-Objekt des Fensters zugreifenwollen.

Der Rückgabewert des HandlersDer Rückgabewert eines Event-Handlers, der über das Setzen einerObjekt-Eigenschaft oder über ein HTML-Attribut registriert wurde,ist manchmal wichtig. Im Allgemeinen teilt der Rückgabewert falsedem Browser mit, dass er die mit dem Event verbundene Standard-aktion nicht ausführen soll. Der onclick-Handler eines Submit-But-tons in einem Formular kann zum Beispiel false zurückgeben,damit der Browser das Formular nicht abschickt. (Das ist nützlich,wenn die Benutzereingaben schon auf Clientseite als nicht valideerkannt werden.) Genauso kann ein onkeypress-Handler eines Ein-gabefeldes Tastendrücke herausfiltern, indem false zurückgegebenwird, wenn der Anwender ein unpassendes Zeichen eintippt.

Der Rückgabewert des onbeforeunload-Handlers für das Window-Objekt ist ebenfalls wichtig. Dieses Event wird ausgelöst, wenn derBrowser zu einer neuen Seite navigieren möchte. Gibt dieser Event-Handler einen String zurück, wird er in einem modalen Dialogfens-ter angezeigt, und der Anwender wird gebeten, die Navigation zubestätigen.

Aufruf eines Event-Handlers | 229

Page 242: 3868993886_Script

Es ist wichtig, zu verstehen, dass die Rückgabewerte von Event-Handlern nur für Handler wichtig sind, die als Eigenschaften regis-triert wurden. Wir werden weiter unten sehen, dass Event-Handler,die mit addEventListener() registriert wurden, stattdessen die Me-thode preventDefault() des Event-Objekts aufrufen oder die re-turnValue-Eigenschaft des Event-Objekts setzen müssen.

Event-PropagationWenn das Ziel eines Events das Window-Objekt oder ein andereseinzelnes Objekt (wie ein XMLHttpRequest) ist, reagiert der Brow-ser auf ein Event, indem er einfach die passenden Handler für dieseseine Objekt aufruft. Ist das Event-Ziel aber ein Document oder einDokument-Element, ist die Angelegenheit komplizierter.

Nachdem die für das Ziel-Element registrierten Event-Handler auf-gerufen wurden, steigen die meisten Events im DOM-Baum nachoben (das sogenannte Bubbling). Es werden die Event-Handler desEltern-Elements des Ziels aufgerufen. Dann sind die für das Groß-eltern-Element registrierten Handler an der Reihe. Das geht soweiter bis zum Document-Objekt und dann zum Window-Objekt.Das Event-Bubbling ist eine Alternative zum Registrieren von Hand-lern für viele einzelne Dokument-Elemente. Stattdessen können Sieeinen einzelnen Handler für ein gemeinsames Vorfahr-Objekt regis-trieren und die Events dort verarbeiten. Sie können zum Beispieleinen »change«-Handler für ein <form>-Element registrieren, anstatteinen »change«-Handler für jedes einzelne Element im Formular zuregistrieren.

Die meisten Events, die für Dokument-Elemente ausgelöst werden,steigen auf. Wichtige Ausnahmen sind die focus-, blur- und scroll-Events. Das load-Event für Dokument-Elemente steigt auf, beendetseinen Weg nach oben aber beim Document-Objekt, sodass es nichtzum Window-Objekt weitersteigt. Das load-Event des Window-Objekts wird nur ausgelöst, wenn das gesamte Dokument geladenwurde.

Das Event-Bubbling ist die dritte »Phase« der Event-Propagation.Der Aufruf der Event-Handler für das Ziel-Objekt selbst ist diezweite Phase. Die erste Phase, die sogar noch vor dem Aufruf der

230 | Kapitel 12: Events

Page 243: 3868993886_Script

Ziel-Handler an der Reihe ist, wird als Capturing-Phase bezeichnet.Sie erinnern sich: addEventListener() nutzt einen booleschen Wertals drittes Argument. Ist dieses Argument true, wird der Event-Handler als abfangender Event-Handler registriert, um in dieserersten Phase der Event-Propagation aufgerufen zu werden.

Die Capturing-Phase der Event-Propagation entspricht der umge-kehrten Bubbling-Phase. Die abfangenden Handler des Window-Objekts werden zuerst aufgerufen, dann die des Document-Ob-jekts, dann die des Body-Objekts und so weiter – den DOM-Baumhinunter bis zu den abfangenden Event-Handlern der Eltern desEvent-Ziels. Die abfangenden Event-Handler des Event-Ziels selbstwerden nicht aufgerufen.

Das Event-Capturing bietet eine Möglichkeit, Events unter die Lupezu nehmen, bevor sie ihr eigentliches Ziel erreichen. Ein abfangen-der Event-Handler kann zum Debuggen genutzt werden oder zu-sammen mit der Event-Cancellation, die weiter unten beschriebenwird, um Events so zu filtern, dass die Ziel-Event-Handler nieaufgerufen werden. Ein häufiger Anwendungsfall für das Event-Capturing ist das Verarbeiten von Maus-Events, bei denen dieBewegungs-Events vom gezogenen Objekt behandelt werden müs-sen, nicht von Dokument-Elementen, über die es gezogen wird.

Event-CancellationIm Abschnitt »Der Rückgabewert des Handlers« auf Seite 229 habeich erläutert, dass der Rückgabewert von Event-Handlern, die alsEigenschaften registriert wurden, zum Abbrechen der Browser-Standardaktion für dieses Event genutzt werden kann. Sie könnendie Standardaktion für ein Event auch abbrechen, indem Sie dieMethode preventDefault() für das Event-Objekt aufrufen.

Das Abbrechen der Standardaktion, die mit einem Event verbundenist, ist nur eine Möglichkeit der Event-Cancellation. Wir könnenauch die Event-Propagation abbrechen. Event-Objekte besitzen einestopPropagation()-Methode, die Sie aufrufen können, um die wei-tere Event-Propagation abzubrechen. Sind andere Handler für dasgleiche Objekt definiert, wird der Rest dieser Handler trotzdemaufgerufen, nicht aber Event-Handler für andere Objekte. Die Me-

Aufruf eines Event-Handlers | 231

Page 244: 3868993886_Script

thode stopPropagation() kann während der Event-Propagationjederzeit aufgerufen werden. Sie funktioniert während der Captu-ring-Phase, für das Event-Ziel selbst und während der Bubbling-Phase. Eine weitere Methode des Event-Objekts mit dem NamenstopImmediatePropagation(), unterbindet die Propagation des Ob-jekts in andere Objekte. Gleichzeitg wird verhindert, dass weitereEvent-Handler aufgerufen werden, die möglicherweise für das glei-che Objekt registriert sind.

232 | Kapitel 12: Events

Page 245: 3868993886_Script

KAPITEL 13

Netzwerkverbindungen

In diesem Kapitel zeigen wir Ihnen vier Techniken für die clientsei-tige Verwendung von JavaScript für Netzwerkverbindungen. Dieerste Methode, bekannt als XMLHttpRequest, ist besonders im Um-feld der »Ajax«-Applikationsarchitektur verbreitet. Diese API ist beiWeitem die wichtigste und nimmt entsprechend den größten Teildieses Kapitels ein. Außerdem behandeln wir hier die JSONP-Technik für die Arbeit mit Ajax-Netzwerkverbindungen mit dem<script>-Tag. Daneben gehen wir auch aus »Server Push«- und»Comet«-Netzwerkverbindungen über die neue EventSource-APIsowie auf bidirektionale Socket-Verbindungen mit WebSockets ein.

XMLHttpRequest verwendenBrowser definieren ihre HTTP-API über eine XMLHttpRequest-Klas-se. Jede Instanz dieser Klasse steht für ein einzelnes HTTP-Request/Response-Paar, und die Eigenschaften und Methoden des Objektsermöglichen es Ihnen, Request-Details festzulegen und Response-Daten auszulesen. XMLHttpRequest wird oft als XHR abgekürzt. Indiesem Kapitel verwenden wir außerdem den Begriff XHR2. Hiermitbezeichnen wir Merkmale, die im Entwurf zur Version 2 der XHR-Spezifikation neu hinzugekommen sind. Die XMLHttpRequest-APIhat übrigens nichts mit XML zu tun: Der Name ist schlicht ein Fehlerder Geschichte, mit dem wir jetzt leben müssen.

Der erste Schritt bei der Verwendung der XHR-API besteht natürlichdarin, eine neue Instanz des XMLHttpRequest-Objekts anzulegen:

var request = new XMLHttpRequest();

| 233

Page 246: 3868993886_Script

Sie können auch ein bestehendes XMLHttpRequest-Objekt wieder-verwenden. Beachten Sie allerdings, dass damit jeglicher Request,der für dieses Objekt noch aussteht, abgebrochen wird.

Jeder HTTP-Request besteht aus vier Teilen:

• aus der HTTP-Request-Methode oder dem »Verb«,

• aus der angefragten URL,

• aus einer optionalen Menge an Request-Headern, zu der auchAuthentifizierungsinformationen gehören können, und

• aus einem optionalen Request-Body.

Die von einem Server geschickte HTTP-Response besteht aus dreiTeilen:

• aus einem Statuscode als Zahl und als Text, der den Erfolgoder Misserfolg des Requests anzeigt,

• aus einer Menge von Response-Headern und

• aus dem Response-Body.

Die ersten beiden folgenden Unterabschnitte zeigen, wie man dieTeile eines HTTP-Requests setzt und wie man die Elemente einerHTTP-Response mit der XHR-API ausliest. Auf diese beiden wich-tigen Abschnitte folgen dann weitere zu spezielleren Themen.

XMLHttpRequest und lokale DateienDie Möglichkeit, auf Webseiten relative URLs zu verwenden, be-deutet im Allgemeinen, dass wir unseren HTML-Code in einemlokalen Dateisystem entwickeln und testen können, bevor wir ihnunverändert auf einem Webserver bereitstellen. Bei der Ajax-Pro-grammierung mit XMLHttpRequest ist das allerdings nicht möglich.XMLHttpRequest ist dazu gedacht, mit den HTTP- und HTTPS-Protokollen zusamenzuarbeiten, allerdings nicht mit dem file://-Protokoll! Das bedeutet: Wenn Sie mit XMLHttpRequest arbeiten,müssen Sie Ihre Daten zuerst auf einen Webserver hochladen (odereinen lokal laufen lassen), um sie zu testen.

Die grundlegende Request/Response-Architektur von HTTP istziemlich einfach, und man kann leicht mit ihr arbeiten. In der Praxis

234 | Kapitel 13: Netzwerkverbindungen

Page 247: 3868993886_Script

kommt es allerdings zu allen möglichen Komplikationen: Clientsund Server tauschen Cookies miteinander aus; Server leiten Browserauf andere Server weiter; manche Ressourcen werden gecacht,andere hingegen nicht; manche Clients schicken alle Requests überProxy-Server und so weiter. XMLHttpRequest ist keine HTTP-APIauf Protokollebene, sondern eine API auf Browserebene. DerBrowser kümmert sich um Cookies, Redirects, Caching und Pro-xies, während sich Ihr Code nur um Requests und Responseskümmern muss.

Den Request definierenNach dem Erstellen des XMLHttpRequest-Objekts besteht dernächste Schritt beim Aufbauen eines HTTP-Requests darin, dieopen()-Methode Ihres XMLHttpRequest-Objekts aufzurufen, umdie beiden notwendigen Teile des Requests festzulegen: die Me-thode und die URL.

request.open("GET", // HTTP-GET-Request eroffnen"data.csv"); // Fur den Inhalt dieser URL

Das erste Argument von open() legt die HTTP-Methode oder dasVerb fest. Die GET- und POST-Methoden werden überall unter-stützt. GET wird für »normalere« Requests genutzt und ist passend,wenn die URL die angefragte Ressource vollständig beschreibt,keine Nebenwirkungen auf dem Server auslöst und die Responsevom Server gecacht werden kann. Die POST-Methode enthältzusätzliche Daten im Request-Body, die oft in einer Datenbank aufdem Server gespeichert werden (eine Nebenwirkung).

Neben GET und POST erlaubt die XMLHttpRequest-Spezifikationals erstes Argument von open() auch noch DELETE, HEAD, OPTI-ONS und PUT.

Das zweite Argument von open() ist die URL für diesen Request.Diese ist relativ zur URL des Dokuments zu sehen, in dem das Skriptmit dem Aufruf von open() zu finden ist. Geben Sie eine absoluteURL an, müssen Protokoll, Host und Port im Allgemeinen mit denentsprechenden Daten des enthaltenden Dokuments übereinstim-men. Cross-Origin-HTTP-Requests führen normalerweise zu einemFehler. (Aber XHR2 erlaubt Cross-Origin-Requests, wenn der Server

XMLHttpRequest verwenden | 235

Page 248: 3868993886_Script

dies explizit zulässt – siehe den Abschnitt »Cross-Origin-HTTP-Re-quests« auf Seite 240.)

Der nächste Schritt beim Request-Prozess ist das Setzen der Re-quest-Header, wenn denn welche benötigt werden. POST-Requestsbenötigen zum Beispiel einen »Content-Type«-Header, um denMIME-Typ des Request-Bodys festzulegen:

request.setRequestHeader("Content-Type", "text/plain");

Wenn Sie setRequestHeader() mehrfach für den gleichen Headeraufrufen, ersetzt der neue Wert nicht den vorher angegebenen.Stattdessen enthält der HTTP-Request danach mehrere Kopien desHeaders, oder für den Header werden mehrere Werte angegeben.

Sie können die Header »Content-Length«, »Date«, »Referer« oder»User-Agent« nicht selbst festlegen – XMLHttpRequest fügt dieseautomatisch für Sie hinzu und erlaubt auch kein Tricksen. Genausokümmert sich das XMLHttpRequest-Objekt automatisch um Coo-kies und die Laufzeit der Verbindung, um den Zeichensatz und diemöglichen Kodierungen. Diese Header dürfen Sie daher ebenfallsnicht selbst setzen.

Der letzte Schritt beim Erstellen eines HTTP-Requests mitXMLHttpRequest ist das Festlegen des optionalen Request-Bodysund das Abschicken an den Server. Dazu nutzen Sie die send()-Methode:

request.send(null);

GET-Requests besitzen niemals einen Body, daher sollten Sie nullübergeben oder das Argument weglassen. POST-Requests enthaltenim Allgemeinen einen Body, der dann dem »Content-Type«-Headerentsprechen sollte, den Sie mit setRequestHeader() gesetzt haben.

Beispiel 13-1 nutzt alle bisher beschriebenen XMLHttpRequest-Me-thoden. Es wird ein Textstring per POST an einen Server geschickt,aber jede Response ignoriert, die der Server schickt. Beachten Sie,dass der im Request-Body verschickte Text ziemlich komplex seinkann, zum Beispiel ein mit JSON.stringify() kodiertes JavaScript-Objekt oder eine formularkodierte Folge von Schlüssel/Wert-Paa-ren.

236 | Kapitel 13: Netzwerkverbindungen

Page 249: 3868993886_Script

Beispiel 13-1: Reinen Text an einen Server schicken

function postMessage(msg) {var r = new XMLHttpRequest(); // Neuer Requestr.open("POST", "/log.php"); // Daten per POST-Request

// an diesen URL schicken.// UTF-8 als Zeichensatz fur den Request-Body verwenden.r.setRequestHeader("Content-Type",

"text/plain;charset=UTF-8");// msg als Request-Body senden.r.send(msg);// Wir ignorieren jegliche Response oder Fehler.

}

Beachten Sie, dass die send()-Methode in Beispiel 13-1 den Requestaufsetzt und dann zurückkehrt – sie wartet nicht auf die Server-Response. HTTP-Responses werden asynchron verarbeitet, wie wirim nächsten Abschnitt zeigen werden.

Die Response erhaltenEine vollständige HTTP-Response besteht aus einem Status-Code,einer Menge von Response-Headern und einem Response-Body. Alldiese Elemente lassen sich über Eigenschaften und Methoden desXMLHttpRequest-Objekts auslesen:

• Die Eigenschaften status und statusText geben den HTTP-Status in numerischer Form und in Textform zurück. DieseEigenschaften enthalten Standard-HTTP-Werte wie 200 und»OK« für erfolgreiche Requests und 404 und »Not Found« fürURLs, die zu keiner Ressource auf dem Server passen.

• Die Response-Header können mit getResponseHeader() undgetAllResponseHeaders() ausgelesen werden.

• Der Response-Body kann in Textform über die EigenschaftresponseText ausgelesen werden.

Das XMLHttpRequest-Objekt wird asynchron genutzt: Die Me-thode send() kehrt direkt nach dem Absenden des Requests zurück,und die oben aufgeführten Response-Methoden und Eigenschaftensind erst dann gültig, wenn die Response erhalten wurde. Um da-rüber informiert zu werden, wenn die Response bereitsteht, müssenSie für das XMLHttpRequest-Objekt auf readystatechange-Events

XMLHttpRequest verwenden | 237

Page 250: 3868993886_Script

(oder die im Abschnitt »HTTP-Progress-Events« auf Seite 239 be-schriebenen neuen XHR2-progress-Events) lauschen. Aber um die-sen Event-Typ zu verstehen, müssen Sie zunächst die EigenschaftreadyState kennen.

readyState enthält einen Integer-Wert, der den Status eines HTTP-Requests angibt. Seine möglichen Werte sind die folgenden:

Wert Bedeutung

0 open() wurde noch nicht aufgerufen.

1 open() wurde aufgerufen.

2 Header wurden empfangen.

3 Der Response-Body wird empfangen.

4 Die Response ist vollständig.

Um auf Events vom Typ readystatechange zu lauschen, weisen Sieder Eigenchaft onreadystatechange des XMLHttpRequest-ObjektsIhre Event-Handler zu. (Alternativ können Sie auch addEventListe-ner() aufrufen.) Beispiel 13-2 definiert eine Funktion getText(), diezeigt, wie man auf readystatechange-Events lauscht. Der Event-Handler stellt zunächst sicher, dass der Request vollständig ist.Wenn das der Fall ist, prüft er den Response-Statuscode daraufhin,ob der Request erfolgreich war. Dann schaut er nach dem »Content-Type«-Header, um sicherzugehen, dass die Response den erwarte-ten Typ besitzt. Sind alle drei Bedingungen erfüllt, übergibt er denResponse-Body (als Text) an eine angegebene Callback-Funktion.Diese Callback-Funktion könnte die Antwort dann weiterverarbei-ten, zum Beispiel, indem sie die Antwort an JSON.parse() weiter-reicht.

Beispiel 13-2: Eine HTTP-Response über onreadystatechange erhalten

// HTTP-GET-Request fur den Inhalt der angegebenen URL// auslosen. Ist die Response erfolgreich geladen, prufe// auf reinen Text. Wenn alles stimmt,// ubergib ihn an die angegebene Callback-Funktion.function getText(url, callback) {

var r = new XMLHttpRequest(); // Neuen Request erzeugenr.open("GET", url); // URL angebenr.onreadystatechange = function() {

238 | Kapitel 13: Netzwerkverbindungen

Page 251: 3868993886_Script

// Ist der Request vollstandig und erfolgreich?if (r.readyState === 4 && r.status === 200) {

var type = r.getResponseHeader("Content-Type");// Enthalt die Response Text? Ubergabe an Callbackif (type.match(/^text/))

callback(r.responseText);}

};r.send(null); // Request abschicken

}

HTTP-Progress-EventsIn den oben gezeigten Beispielen haben wir das readystatechange-Event genutzt, um herauszufinden, ob ein HTTP-Request abge-schlossen ist. Der XHR2-Spezifikationsentwurf definiert eine nütz-lichere Gruppe an Events. In diesem neuen Event-Modell löst dasXMLHttpRequest-Objekt in den unterschiedlichen Request-Phasenverschiedene Event-Typen aus, sodass man die Eigenschaft ready-State nicht länger prüfen muss.

In Browsern, die diese Events unterstützen, werden diese folgender-maßen ausgelöst: Wird die Methode send() aufgerufen, sorgt dasfür ein einzelnes loadstart-Event. Während die Response des Serversheruntergeladen wird, löst das XMLHttpRequest-Objekt progress-Events aus (meist etwa alle 50 Millisekunden). Diese Events könnenSie nutzen, um den Anwender über den Fortschritt des Requests zuinformieren. Ist ein Request sehr schnell abgeschlossen, wird even-tuell nie ein progress-Event ausgelöst. Am Ende eines Requests wirdein load-Event ausgelöst.

Ein vollständiger Request muss nicht notwendigerweise erfolgreichsein, und Ihr Handler für das load-Event sollte den status-Code desXMLHttpRequest-Objekts prüfen, um zum Beispiel sicherzustellen,dass Sie eine HTTP-Response »200 OK« statt »404 Not Found«erhalten haben.

Es gibt drei Möglichkeiten, warum ein HTTP-Request nicht abge-schlossen werden kann, und dementsprechend gibt es auch dreidazugehörige Events. Gibt es einen Zeitabbruch, wird das timeout-Event ausgelöst. Wird ein Request abgebrochen, wird das abort-Event ausgelöst. Schließlich können andere Netzwerkfehler, wie

XMLHttpRequest verwenden | 239

Page 252: 3868993886_Script

zum Beispiel zu viele Redirects, das Abschließen eines Requestsverhindern. In dem Fall wird das error-Event ausgelöst.

Das mit diesen Progress-Events verbundene Event-Objekt besitztneben den normalen Event-Objekt-Eigenschaften wie type undtimestamp drei nützliche Eigenschaften. Die Eigenschaft loaded ent-hält die Anzahl an Bytes, die bisher übertragen wurden. In totalsteht, wie viele Daten (in Bytes) überhaupt zu übertragen sind. Daswird aus dem »Content-Length«-Header übernommen. Ist die Längeunbekannt, steht hier 0. Die Eigenschaft lengthComputable ist schließ-lich true, wenn die Content-Length bekannt ist, ansonsten steht hierfalse. Die Eigenschaften total und loaded sind ganz offensichtlichinsbesondere für die progress-Event-Handler nützlich:

request.onprogress = function(e) {if (e.lengthComputable) {

var p = Math.round(100*e.loaded/e.total);progress.innerHTML = p + "% Complete";

}}

Neben der Definition dieser nützlichen Events für das Verfolgen desDownloads einer HTTP-Response erlaubt es XHR2 auch, dieEvents zum Verfolgen des Uploads eines HTTP-Requests zu nutzen.In Browsern, die dieses Feature implementiert haben, besitzt dasXMLHttpRequest-Objekt eine Eigenschaft upload. Der Wert derupload-Eigenschaft ist ein Objekt, das eine addEventListener()-Me-thode und einen kompletten Satz an Progress-Event-Eigenschaftenwie onprogress und onload definiert.

Sie können die Upload-Event-Handler so nutzen, wie Sie es mitden normalen Progress-Event-Handlern tun. Setzen Sie für einXMLHttpRequest-Objekt x die Eigenschaft x.onprogress, um denDownload-Fortschritt der Response zu verfolgen. Setzen Sie da-gegen x.upload.onprogress, um den Upload-Fortschritt des Re-quests überwachen zu können.

Cross-Origin-HTTP-RequestsAls Teil der Same-Origin-Policy (siehe den Abschnitt »Die Same-Origin-Policy« auf Seite 183) kann das XMLHttpRequest-Objekt

240 | Kapitel 13: Netzwerkverbindungen

Page 253: 3868993886_Script

HTTP-Requests normalerweise nur an den Server schicken, vondem das Dokument heruntergeladen wurde, in dem das Skript läuft.Diese Einschränkung schließt Sicherheitslücken, aber sie geht oftetwas zu weit und verhindert auch eine Reihe zulässiger Anwen-dungsfälle für Cross-Origin-Requests. Sie können Cross-Origin-URLs mit <form>- und <iframe>-Elementen nutzen, und derBrowser wird das entsprechende Dokument anzeigen. Aber auf-grund der Same-Origin-Policy erlaubt der Browser es dem Skriptnicht, den Inhalt solcher herkunftsgleicher Dokumente zu nutzen.Bei XMLHttpRequests steht der Inhalt eines Dokuments immerüber die Eigenschaft responseText zur Verfügung, daher darf es dieSame-Origin-Policy nicht erlauben, dass XMLHttpRequest Cross-Origin-Requests startet. (Beachten Sie, dass das <script>-Elementnie Thema der Same-Origin-Policy war: Es lädt jedes Skript herun-ter und führt es aus – unabhängig von seiner Herkunft. Wie Sie imAbschnitt »HTTP per <script>: JSONP« auf Seite 241 sehen wer-den, macht diese Freiheit das <script>-Element zu einer attraktivenAjax-Transportalternative zu XMLHttpRequest.)

XHR2 erlaubt Cross-Origin-Requests für Websites, die dem durchdas Mitsenden eines passenden CORS-Headers (Cross-Origin Re-source Sharing Header) in der HTTP-Response zustimmen. AlsWebentwickler gibt es nichts Besonderes, das Sie tun müssen, damitCross-Origin-Requests funktionieren: Berücksichtigt der BrowserCORS für XMLHttpRequest und erlaubt die Website, auf der Sieeinen Cross-Origin-Request starten wollen, CORS, wird die Same-Origin-Policy gelockert und Ihr Request funktioniert.

HTTP per <script>: JSONPFür bestimmte Inhaltsarten kann die Verwendung eines <script>-Elements eine nützliche Alternative zu XMLHttpRequest darstellen.Man setzt einfach das src-Attribut eines <script>-Elements (undfügt dieses in das Dokument ein, wenn es sich nicht schon darinbefindet), und der Browser erzeugt einen HTTP-Request, um dieangegebene URL herunterzuladen. <script>-Elemente sind vor al-lem aus einem Grund nützliche Ajax-Transportmedien: Sie sindnicht von der Same-Origin-Policy betroffen, daher können Sie sie

HTTP per <script>: JSONP | 241

Page 254: 3868993886_Script

nutzen, um Daten von anderen Servern als nur Ihren eigenen an-zufordern.

Das Verwenden eines <script>-Elements als Ajax-Transportme-dium ist als JSONP bekannt geworden. Es funktioniert dann,wenn der Response-Body des HTTP-Requests JSON-kodiert ist.Das »P« steht für »Padding« oder »Präfix«; wir werden das gleicherklären.

Stellen Sie sich vor, Sie haben einen Service geschrieben, der GET-Requests verarbeitet und JSON-kodierte Daten zurückgibt. Doku-mente mit gleicher Herkunft können diesen Service mitXMLHttpRequest und JSON.parse() nutzen. Wenn Sie CORS aufIhrem Server aktivieren, können herkunftsfremde Dokumente inneuen Browsern ebenfalls Ihren Service per XMLHttpRequest ver-wenden. Cross-Origin-Dokumente in älteren Browsern, die keinCORS unterstützen, können auf Ihren Service aber nur per<script>-Element zugreifen. Bei Ihrem JSON-Response-Body han-delt es sich (per Definition) um gültigen JavaScript-Code, und derBrowser wird ihn ausführen, wenn die Response vollständig gela-den wurde. Das Ausführen von JSON-kodierten Daten dekodiertsie, aber das Ergebnis sind immer noch nur Daten, die nicht selbstetwas tun.

Hier kommt das »P« von JSONP ins Spiel. Wird Ihr Service über ein<script>-Element aufgerufen, muss er seine Response per Padding»aufpolstern«, indem er sie mit Klammern umschließt und denNamen einer JavaScript-Funktion vornean stellt. Anstatt also ein-fach folgende JSON-Daten zu schicken:

[1, 2, {"buckle": "my shoe"}]

sendet er eine Padded-JSON-Response, wie zum Beispiel:

handleResponse([1, 2, {"buckle": "my shoe"}])

Als Rumpf eines <script>-Elements kann diese »gepolsterte« Res-ponse etwas tun: Die JSON-kodierten Daten werden evaluiert (eshandelt sich schließlich um einen großen JavaScript-Ausdruck) unddann an die Funktion handleResponse() weitergegeben, die (so

242 | Kapitel 13: Netzwerkverbindungen

Page 255: 3868993886_Script

unsere Annahme) das enthaltende Dokument definiert hat, um mitden Daten etwas Nützliches anzustellen.

Damit das funktioniert, müssen wir dem Service irgendwie mittei-len, dass er über ein <script>-Element aufgerufen wurde und daherbitte JSONP statt JSON zurückschicken soll. Das erreicht manmeist über einen Query-Parameter für die URL, zum Beispiel über?json (oder &json).

In der Praxis diktieren Services, die JSONP unterstützen, gar nicht,wie der Funktionsname (hier ist er »handleResponse«) lautet, denalle Clients zu implementieren haben. Stattdessen nutzen sie denWert eines Query-Parameters, um es dem Client zu ermöglichen,einen Funktionsnamen anzugeben, der dann für das Padding ge-nutzt wird. Beispiel 13-3 nutzt einen Query-Parameter namens»jsonp«, um den Namen der Callback-Funktion anzugeben.

Beispiel 13-3 definiert eine Funktion getJSONP(), die einen JSONP-Request abschickt. Dieses Beispiel ist ein bisschen knifflig, und esgibt ein paar Dinge, die Sie wissen sollten. Beachten Sie zum Beispielbitte, wie das neue <script>-Element erstellt, seine URL gesetzt undes in das Dokument eingefügt wird. Durch das Einfügen wird dannder HTTP-Request ausgelöst. Dann schauen Sie sich einmal an, wiedas Beispiel eine neue, interne Callback-Funktion für jeden Requesterstellt und die Funktion als Eigenschaft von getJSONP() selbstspeichert. Schließlich achten Sie noch darauf, dass nach dem Aufrufder Callback-Funktion aufgeräumt wird: Das script-Element wirdentfernt und die Funktion selbst gelöscht.

Beispiel 13-3: Einen JSONP-Request mit einem script-Element erstellen

// Schicke einen JSONP-Request an die angegebene URL, und// ubergib die geparsten Response-Daten an den ubergebenen// Callback. Fuge einen Query-Parameter namens// "jsonp" an die URL an, um den Namen der Callback-// Funktion fur den Request festzulegen.function getJSONP(url, callback) {

// Eindeutigen Callback-Namen nur fur diesen Request// erstellen. Der Name wird als Eigenschaft der Funktion// definiert.var cbnum = "cb" + getJSONP.counter++;var cbname = "getJSONP." + cbnum;

HTTP per <script>: JSONP | 243

Page 256: 3868993886_Script

// Fuge den Callback-Namen an den URL-Query-String an.if (url.indexOf("?") === -1)

url += "?jsonp=" + cbname;else

url += "&jsonp=" + cbname;

// script-Element erstellen, das diesen Request abschickt.var script = document.createElement("script");

// Callback-Funktion definieren, die durch das Script// aufgerufen wird.getJSONP[cbnum] = function(response) {

try {callback(response); // Response-Daten verarbeiten

}finally { // Auch wenn callback oder Response Fehler

// auslosen.delete getJSONP[cbnum];script.parentNode.removeChild(script);

}};

// Jetzt den HTTP-Request auslosenscript.src = url;document.body.appendChild(script);

}

// Zahler zum Erstellen eindeutiger Callback-NamengetJSONP.counter = 0;

Skripten und SicherheitUm ein <script>-Element als Ajax-Transportmedium zu nutzen,müssen Sie Ihrer Webseite erlauben, den JavaScript-Code auszufüh-ren, den der fremde Server Ihnen schickt. Sie dürfen also auf keinenFall die hier beschriebenen Techniken für Server nutzen, denen Sienicht vertrauen. Und auch bei vertrauenswürdigen Servern solltenSie im Hinterkopf behalten, dass sich ein Angreifer ins Systemhacken kann. Dieser hat dann die Möglichkeit, Ihre Webseite zuübernehmen, indem er beliebigen Code ausführt und beliebigeInhalte anzeigt. Für den Anwender sieht es aber trotzdem so aus,als ob der Inhalt von Ihrer Website stammt.

244 | Kapitel 13: Netzwerkverbindungen

Page 257: 3868993886_Script

Vergessen Sie das nicht, auch wenn es durchaus üblich ist, Skriptenvon dritter Seite zu vertrauen, insbesondere wenn es um das Ein-binden von Werbung oder »Widgets« auf einer Seite geht. DasVerwenden von <script> als Ajax-Transportmedium für eine Kom-munikation mit einem vertrauenswürdigen Webservice ist auchnicht gefährlicher als dies.

Server-Sent EventsBei normalen HTTP-Netzwerkverbindungen mit XHR oder dem<script> -Tag fordert der Client die Daten bei Bedarf vom Serveran. Man spricht hier auch von Client-Pull. Manche Web-Applika-tionen verwenden dagegen eine andere Form HTTP-basierter Netz-werkverbindungen. Beim Server-Push- oder Comet-Verfahren wirdzwischen und Client und Server eine HTTP-Verbindung aufgebaut,aber auf unbestimmte Zeit offen gehalten. Dadurch ist es möglich,dass der Server die Daten durch diese offene Verbindung zum Client»schiebt« (engl. to push).

Zwar ist es auch möglich, diese Art von Netzwerkverbindung mitXHR zu realisieren, allerdings ist das nicht gerade trivial. Durch eineeinfache EventSource-API (die aus einem neuen Standard aus demHTML5-Umfeld mit dem Namen Server-Sent Events stammt) wirddas Empfangen und Reagieren auf vom Server gepushte Nachrich-ten deutlich erleichtert.

Um Server-Sent Events zu nutzen, übergeben Sie einfach eine URLan den EventSource()-Konstruktor und lauschen dann auf Message-Events für das zurückgegebene Objekt:

var ticker = new EventSource("stockprices.php");ticker.onmessage = function(e) {

var type = e.type;var data = e.data;

// Verarbeite die Event-Typ- und -Daten-Strings.}

Das mit einer Message verbundene Event-Objekt besitzt eine Eigen-schaft data, in der der String steckt, den der Server als Nutzlast fürdieses Event mitgeschickt hat. Das Event-Objekt besitzt wie alle

Server-Sent Events | 245

Page 258: 3868993886_Script

Event-Objekte auch eine Eigenschaft type. Der Standardwert dafürist »message«, aber die Event-Quelle kann auch einen anderenString für die Eigenschaft mitgeben. Ein einzelner onmessage-Event-Handler erhält alle Events von einer angegebenen Server-Event-Quelle und kann sie dann bei Bedarf anhand ihrer Eigenschaft typeverteilen.

Das Server-Sent Event-Protokoll ist recht übersichtlich. Der Clientrichtet eine Verbindung zum Server ein (durch das Erstellen desEventSource-Objekts), und der Server hält diese Verbindung offen.Wird ein Event ausgelöst, schreibt der Server Textzeilen in dieseVerbindung. Ein so übermitteltes Event kann zum Beispiel wie folgtaussehen:

event: bid Art des Eventsdata: GOOG Setzt die data-Eigenschaft.data: 999 Hangt ein Newline-Zeichen und weitere Daten an.

Leerzeichen lost ein neues Event aus.

WebSocketsSämtliche bisher in diesem Kapitel behandelten APIs sind HTTP-basiert. Das bedeutet, dass sie alle von der grundlegenden Naturvon HTTP eingeschränkt werden: HTTP ist ein zustandsloses Pro-tokoll, das aus Clientanfragen und Serverantworten besteht. HTTPist eigentlich ein spezialisiertes Netzwerkprotokoll. AllgemeinereNetzwerkprotokolle nutzen häufig langlebigere Verbindungen undeinen bidirektionalen Nachrichtenaustausch über TCP-Sockets. Esist nicht sicher, nicht-vertrauenswürdigem clientseitigen JavaScript-Code Zugriff auf elementare TCP-Sockets zu geben, aber die Web-Socket-API definiert eine sichere Alternative: Sie ermöglicht esclientseitigem Code, eine bidirektionale Socket-artige Verbindungmit Servern herzustellen, die das WebSocket-Protokoll unterstüt-zen. Das vereinfacht die Durchführung bestimmter Arten von Netz-werkoperationen erheblich.

Die WebSocket-API lässt sich überraschend leicht nutzen. Zunächstmüssen Sie mit dem WebSocket()-Konstruktor ein Socket erstellen:

var s = new WebSocket("ws://ws.example.com/resource");

246 | Kapitel 13: Netzwerkverbindungen

Page 259: 3868993886_Script

Das Argument für den WebSocket()-Konstruktor ist eine URL, diedas ws://-Protokoll (oder wss:// für eine sichere Verbindung wiedie von https://) nutzt. Die URL gibt den Host an, mit dem dieVerbindung hergestellt werden soll, und kann auch einen Port(WebSockets nutzen den gleichen Standard-Port wie HTTP undHTTPS) und einen Pfad oder eine Ressource angeben.

Haben Sie das Socket erstellt, registrieren Sie in der Regel daraufEvent-Handler:

s.onopen = function(e) { /* Das Socket ist offen */};s.onclose = function(e) { /* Socket geschlossen. */ };s.onerror = function(e) { /* Etwas ist schiefgelaufen! */ };s.onmessage = function(e) {

var m = e.data; /* Der Server hat eine Nachricht gesendet */};

Wollen Sie über das Socket Daten an den Server senden, rufen Siedie send()-Methode des Sockets auf:

s.send("Hallo, Server!");

Wenn Ihr Code die Kommunikation mit dem Server abgeschlossenhat, können Sie ein WebSocket schließen, indem Sie seine close()-Methode aufrufen.

WebSocket-Kommunikation ist vollständig bidirektional. HabenSie einmal eine WebSocket-Verbindung eingerichtet, können Clientund Server einander jederzeit Nachrichten senden, und diese Kom-munikation muss nicht in der Anfrage/Antwort-Form ablaufen.

WebSockets | 247

Page 260: 3868993886_Script
Page 261: 3868993886_Script

KAPITEL 14

Clientseitiger Speicher

Webanwendungen können Browser-APIs nutzen, um Daten lokalauf dem Computer des Anwenders abzulegen. Dieser clientseitigeSpeicher dient als Gedächtnis des Webbrowsers. Webapps könnenzum Beispiel Benutzereinstellungen oder gleich den gesamten Statussichern, um so dort weiterzumachen, wo der Benutzer beim letztenBesuch aufgehört hat. Der clientseitige Speicher wird nach der»Herkunft« getrennt, sodass die Seiten der einen Site nicht dieDaten der Seiten von anderen Sites lesen können. Aber zwei Seitender gleichen Site können den Speicher gemeinsam nutzen und sie alsKommunikationsmechanismus verwenden. Daten, die in einemFormular auf einer Seite eingegeben wurden, können zum Beispielin einer Tabelle auf einer anderen Seite angezeigt werden.

Webanwendungen können die Lebensdauer der Daten bestimmen,die sie abspeichern: Daten können temporär gesichert werden,sodass sie nur bestehen bleiben, bis das Fenster geschlossen oderder Browser beendet wird; oder sie können permanent auf derFestplatte gesichert werden, sodass sie auch noch Monate späterzur Verfügung stehen.

Dieses Kapitel behandelt zwei Arten der clientseitigen Speicherung:die moderne Web Storage-API und die bereits etwas betagte Coo-kies-API.

| 249

Page 262: 3868993886_Script

Speichern, Sicherheit und PrivatsphäreWebbrowser bieten häufig an, sich Kennwörter für Sie zu merkenund sie verschlüsselt auf der Festplatte zu sichern. Aber zu keiner derin diesem Kapitel beschriebenen Formen des clientseitigen Speichersgibt es Verschlüsselungsmöglichkeiten. Alles, was Sie sichern, findetsich unverschlüsselt auf der lokalen Festplatte. Gesicherte Datenstehen daher neugierigen Anwendern, die Zugriff auf Ihren Rechnerhaben, ebenso zur Verfügung wie böswilliger Software (wie zumBeispiel Spyware). Aus diesem Grund sollte man clientseitige Daten-sicherung nie für Kennwörter, Kontonummern und Ähnliches oderandere private Informationen verwenden.

Vergessen Sie auch nicht, dass viele Anwender Websites misstrauen,die Cookies oder andere clientseitige Speichermechanismen nutzen,weil sie Angst haben, »überwacht« zu werden. Versuchen Sie, mitden in diesem Kapitel vorgestellten Mechanismen die Benutzerober-fläche Ihrer Site zu verbessern; probieren Sie aber nicht, damitprivate Daten zu sammeln. Gibt es zu viele Sites, die den clientseiti-gen Speicher missbrauchen, wird er von den Anwendern eher abge-schaltet oder regelmäßig geleert, was dem eigentlichen Zweck dieserMechanismen zuwiderläuft.

localStorage und sessionStorageBrowser, die den Web Storage-Spezifikationsentwurf implementie-ren, definieren zwei Eigenschaften für das Window-Objekt: local-Storage und sessionStorage. Beide Eigenschaften beziehen sich aufein Storage-Objekt – ein persistentes, assoziatives Array, das String-schlüssel auf Stringwerte abbildet. Storage-Objekte funktionieren soähnlich wie normale JavaScript-Objekte. Wenn Sie einfach eineEigenschaft des Objekts auf einen String setzen, sichert der Browserdiesen String für Sie. Der Unterschied zwischen localStorage undsessionStorage liegt in der Lebensdauer und im Geltungsbereich:Wie lange werden die Daten gesichert, und wer kann auf siezugreifen?

Die Lebensdauer und der Geltungsbereich des Speichers werdenweiter unten detailliert behandelt. Als Erstes wollen wir uns aber ein

250 | Kapitel 14: Clientseitiger Speicher

Page 263: 3868993886_Script

paar Beispiele anschauen. Der folgende Code nutzt localStorage,funktioniert aber auch mit sessionStorage:

// Einen gespeicherten Wert abfragenvar name = localStorage.username;// Aquivalent in Array-Notationname = localStorage["username"];if (!name) { // Name abfragen und speichern, sofern es

// noch keinen Namen gibt.name = prompt("Wie heißen Sie?");localStorage.username = name;

}

// Uber alle gespeicherten Name/Wert-Paare iterierenfor(var key in localStorage) {

var value = localStorage[key];}

Storage-Objekte definieren zudem Methoden für das Speichern,Auslesen, Iterieren über und Löschen von Daten. Diese Methodenwerden im Abschnitt »Storage-API« auf Seite 253 behandelt.

Der Web Storage-Spezifikationsentwurf besagt, dass wir struktu-rierte Daten (Objekte und Arrays), einfache Werte und eingebauteDatentypen wie Datumswerte, reguläre Ausdrücke und sogar File-Objekte speichern können sollen. Aktuell lassen sich mit Browsernaber nur Strings speichern. Wollen Sie andere Datenformen ablegenund auslesen, müssen Sie sie selbst kodieren und dekodieren. ZumBeispiel:

// Speichern Sie eine Zahl, wird sie automatisch in einen// String umgewandelt. Vergessen Sie nicht, sie beim// Auslesen wieder umzuwandeln!localStorage.x = 10;var x = parseInt(localStorage.x);

// Ein Datum beim Schreiben in einen String umwandeln.localStorage.lastRead = (new Date()).toUTCString();// Und beim Lesen wieder zuruckwandeln.var last = new Date(Date.parse(localStorage.lastRead));

// JSON fur die String-Umwandlung und// das Parsen von Objekten und Arrays verwenden.localStorage.data = JSON.stringify(data);var data = JSON.parse(localStorage.data);

localStorage und sessionStorage | 251

Page 264: 3868993886_Script

Lebensdauer und Geltungsbereich des SpeichersDer Unterschied zwischen localStorage und sessionStorage be-steht in der Lebensdauer und im Geltungsbereich des Speichers.Speichert man Daten über localStorage, sind sie permanent gespei-chert: Sie verfallen nicht und bleiben so lange auf dem Rechnereines Anwenders abgelegt, bis sie von einer Webapp gelöschtwerden oder der Anwender den Browser (über eine browserspezi-fische UI) anweist, sie zu löschen.

localStorage hat als Geltungsbereich die Dokumenten-Herkunft.Wie im Abschnitt »Die Same-Origin-Policy« auf Seite 183 erläutertwird, ist die Herkunft eines Dokuments durch sein Protokoll, denHostnamen und den Port definiert, sodass alle folgenden URLs eineunterschiedliche Herkunft aufweisen:

http://www.example.comhttps://www.example.com // Anderes Protokollhttp://static.example.com // Anderer Hostnamehttp://www.example.com:8000 // Anderer Port

Alle Dokumente mit der gleichen Herkunft nutzen die gleichenlocalStorage-Daten (unabhängig von der Herkunft der Skripten,die dann auf localStorage zugreifen). Sie können die Daten gemein-sam lesen. Und sie können die anderen Daten auch überschreiben.Aber Dokumente mit unterschiedlicher Herkunft können andereDaten nie lesen oder überschreiben (selbst wenn sie beide ein Skriptvom gleichen Fremdserver ausführen).

Beachten Sie, dass localStorage auch auf den Browser einge-schränkt ist. Besuchen Sie eine Site mit dem Firefox und danach(zum Beispiel) erneut mit Chrome, werden die beim ersten Besuchgespeicherten Daten nicht beim zweiten Besuch erreichbar sein.

Daten, die über sessionStorage gespeichert wurden, haben eineandere Lebensdauer als Daten, die über localStorage abgelegt sind– sie haben die Lebensdauer des obersten Fensters oder desBrowser-Tabs, in dem das Skript läuft, das die Daten sichert. Wirddas Fenster oder der Tab dauerhaft geschlossen, werden alle Daten,die über sessionStorage abgelegt sind, gelöscht. (Beachten Sieallerdings, dass moderne Browser die Möglichkeit haben, kürzlichgeschlossene Tabs wieder zu öffnen und die letzte Browser-Session

252 | Kapitel 14: Clientseitiger Speicher

Page 265: 3868993886_Script

wiederherzustellen. Daher kann die Lebensdauer dieser Tabs undihres zugehörigen sessionStorage länger sein, als man denkt.)

Wie localStorage ist sessionStorage mit seinem Geltungsbereichauf die Dokument-Herkunft beschränkt, sodass Dokumente mitunterschiedlicher Herkunft nie die gleiche sessionStorage nutzen.Aber sessionStorage ist auch noch auf das jeweilige Fenster be-schränkt. Hat ein Anwender zwei Browser-Tabs geöffnet, die Do-kumente der gleichen Herkunft anzeigen, besitzen diese beidenTabs unterschiedliche sessionStorage-Daten: Die Skripten, die indem einen Tab laufen, können Daten aus dem anderen Tab nichtlesen oder überschreiben, selbst wenn beide Tabs genau die gleicheSeite anzeigen und die gleichen Skripten laufen lassen.

Beachten Sie, dass dieser fensterbasierte Geltungsbereich von ses-sionStorage nur für Fenster auf oberster Ebene gilt. Enthält einBrowser-Tab zwei <iframe>-Elemente und zeigen diese Frames zweiDokumente der gleichen Herkunft an, nutzen sie auch eine gemein-same sessionStorage.

Storage-APIlocalStorage und sessionStorage werden häufig wie ganz normaleJavaScript-Objekte verwendet. Man setzt eine Eigenschaft, umeinen String zu speichern, und fragt die Eigenschaft ab, um ihnwieder auszulesen. Aber diese Objekte definieren auch noch eineformalere methodenbasierte API. Um einen Wert zu speichern,übergeben Sie den Namen und Wert an setItem(). Um ihn aus-zulesen, übergeben Sie den Namen an getItem(). Um einen Wert zulöschen, übergeben Sie den Namen an removeItem(). (In den meis-ten Browsern können Sie auch den delete-Operator nutzen, umeinen Wert zu löschen, so wie Sie es für ein normales Objektmachen würden, aber das funktioniert nicht im IE8.) Um allegespeicherten Werte zu löschen, rufen Sie clear() (ohne Argumen-te) auf. Schließlich können Sie die Namen aller gespeicherten Wertenoch enumerieren, die length-Eigenschaft verwenden und Zahlenvon 0 bis length-1 an die Methode key() übergeben. Hier sehen Sieein paar Beispiele mit localStorage. Der gleiche Code funktioniertauch mit sessionStorage:

localStorage und sessionStorage | 253

Page 266: 3868993886_Script

localStorage.setItem("x", 1); // Eine Zahl mit dem Namen// "x" speichern.

localStorage.getItem("x"); // Einen Wert auslesen.

// Alle gespeicherten Name/Wert-Paare enumerieren.// length gibt die Anzahl der Paare an.for(var i = 0; i < localStorage.length; i++) {

// Den Namen von Paar i erhaltenvar name = localStorage.key(i);// Den Wert dieses Paares erhaltenvar value = localStorage.getItem(name);

}

localStorage.removeItem("x"); // Element "x" loschenlocalStorage.clear(); // Auch alle anderen Elemente loschen

Storage-EventsImmer, wenn sich Daten ändern, die in localStorage oder ses-sionStorage gesichert wurden, löst der Browser ein storage-Eventfür alle anderen Window-Objekte aus, für die sie sichtbar sind (abereben nicht für das Fenster, das die Änderung verursacht hat). Sindin einem Browser zwei Tabs zu Seiten mit der gleichen Herkunftoffen und speichert eine dieser Seiten einen Wert in localStorage,erhält der andere Tab ein storage-Event. Denken Sie daran, dasssessionStorage mit seiner Gültigkeit auf das oberste Fenster einge-schränkt ist, daher werden storage-Events für sessionStorage-Än-derungen nur ausgelöst, wenn Frames im Spiel sind. Setzt man einbestehendes, gespeichertes Element auf seinen aktuellen Wert, wirdkein Event ausgelöst, auch das Entfernen eines Elements, das garnicht vorhanden ist, löst dies nicht aus.

Registrieren Sie einen Handler für storage-Events mit addEventLis-tener() (oder attachEvent() im IE). In den meisten Browsernkönnen Sie auch die onstorage-Eigenschaft des Window-Objektssetzen, aber aktuell unterstützt der Firefox diese Eigenschaft nicht.

Das mit einem storage-Event verbundene Event-Objekt besitzt fünfwichtige Eigenschaften (die leider nicht vom IE8 unterstützt wer-den):

254 | Kapitel 14: Clientseitiger Speicher

Page 267: 3868993886_Script

keyDer Name oder Schlüssel des Elements, das gesetzt oder ent-fernt wurde. Wurde die Methode clear() aufgerufen, hat dieseEigenschaft den Wert null.

newValueEnthält den neuen Wert des Elements oder null, wenn remove-Item() aufgerufen wurde.

oldValueEnthält den alten Wert eines bestehenden Elements, das geän-dert oder gelöscht wurde, oder null, wenn ein neues Elementeingefügt wurde.

storageAreaDiese Eigenschaft entspricht entweder der localStorage- oderder sessionStorage-Eigenschaft des Ziel-Window-Objekts.

urlDie URL (als String) des Dokuments, dessen Skript diese Än-derung ausgelöst hat.

Beachten Sie schließlich noch, dass localStorage und das storage-Event als Broadcast-Mechanismus genutzt werden kann, über denein Browser eine Nachricht an alle Fenster schickt, die aktuell diegleiche Website anzeigen. Möchte zum Beispiel ein Anwender, dasseine Website keine Animationen mehr ausführt, kann die Site dieseEinstellung in localStorage speichern, um sie auch bei zukünftigenBesuchen zu berücksichtigen. Durch das Sichern dieser Einstellungwird zugleich ein Event ausgelöst, sodass andere Fenster, die diegleiche Site anzeigen, diese Anforderung ebenso berücksichtigenkönnen. Ein anderes Beispiel mag eine webbasierte Bildbearbeitungsein, mit der der Anwender Tool-Paletten in eigenen Fenstern an-zeigen lassen kann. Wählt der Anwender nun ein Werkzeug aus,nutzt die Anwendung localStorage, um den aktuellen Status zusichern und eine Benachrichtigung an andere Fenster zu schicken,dass ein neues Werkzeug ausgewählt wurde.

localStorage und sessionStorage | 255

Page 268: 3868993886_Script

CookiesEin Cookie ist ein kleiner, benannter Datenschnipsel, der vom Web-browser gespeichert und mit einer bestimmten Webseite oder Web-site verbunden wird. Cookies waren ursprünglich für die serverseitigeProgrammierung gedacht, und auf unterster Ebene sind sie als Er-weiterung zum HTTP-Protokoll implementiert. Cookie-Daten wer-den automatisch zwischen Webbrowser und Webserver übertragen,sodass serverseitige Skripten die Werte lesen und schreiben können,die auf dem Client gespeichert sind. Dieser Abschnitt zeigt, wieclientseitige Skripten ebenfalls Cookies beeinflussen können, indemsie die cookie-Eigenschaft des Document-Objekts nutzen.

Die API für den Umgang mit Cookies ist alt – das heißt, sie wirdüberall unterstützt, aber sie ist auch ausgesprochen kryptisch. Esgibt keine Methoden: Cookies werden über das Lesen und Schrei-ben der cookie-Eigenschaft des Document-Objekts abgefragt, ge-setzt und gelöscht, wobei speziell formatierte Strings benötigt wer-den. Die Lebenszeit und der Geltungsbereich jedes Cookies kannüber Cookie-Attribute individuell angegeben werden. Auch dieseAttribute werden über bestimmte Strings angegeben, die für diegleiche cookie-Eigenschaft gesetzt werden.

Die folgenden Unterabschnitte beschreiben die Cookie-Attributefür die Lebensdauer und den Geltungsbereich, und sie zeigen, wieman Cookie-Werte in JavaScript setzt und ausliest.

Cookie-Attribute: Lebensdauer und GeltungsbereichNeben einem Namen und einem Wert besitzt jedes Cookie optio-nale Attribute, die seine Lebensdauer und seinen Geltungsbereichbeeinflussen. Cookies sind standardmäßig kurzlebig – die in ihnenabgelegten Werte sind nur während der Session im Webbrowsergültig; verlässt der Anwender den Browser, verfallen sie. BeachtenSie, dass dies ein kleiner Unterschied zur Lebensdauer von ses-sionStorage ist: Cookies sind nicht auf ein einzelnes Fenster be-schränkt, und ihre Standardlebensdauer entspricht der des gesam-ten Browser-Prozesses, nicht der eines Fensters. Soll ein Cookieauch das Ende einer Browser-Session überleben, müssen Sie dem

256 | Kapitel 14: Clientseitiger Speicher

Page 269: 3868993886_Script

Browser mitteilen, wie lange er das Cookie (in Sekunden) behaltensoll, indem Sie ein Attribut max-age festlegen. Geben Sie so eineLebensdauer an, speichert der Browser die Cookies in einer Dateiund löscht sie, wenn ihre Spanne abgelaufen ist.

Die Sichtbarkeit von Cookies ist wie bei localStorage und session-Storage auf die Herkunft des Dokuments beschränkt, zusätzlich aberauch auf den Pfad des Dokuments. Dieser Geltungsbereich lässt sichüber die Cookie-Attribute path und domain konfigurieren. Standard-mäßig ist ein Cookie mit der Webseite verbunden, die es erstellt hat,sowie mit allen anderen Webseiten in diesem Verzeichnis und indessen Unterverzeichnissen. Auch sichtbar ist es nur für diese Seiten.Erstellt zum Beispiel die Webseite http://www.example.com/catalog/index.html ein Cookie, ist dieses auch für http://www.example.com/catalog/order.html und http://www.example.com/catalog/widgets/in-dex.html sichtbar, nicht aber für http://www.example.com/about.html.

Dieses Standardverhalten ist bezüglich der Sichtbarkeit genau das,was Sie haben wollen. Aber manchmal sollen die Cookie-Werte aufeiner ganzen Website verfügbar sein, egal, welche Seite das Cookieerstellt hat. Gibt der Anwender zum Beispiel seine Lieferadresse ineinem Formular auf einer Seite ein, wollen Sie diese Adresse viel-leicht als Standardadresse abspeichern, um sie beim nächsten Auf-ruf vorzuschlagen – aber sie auch als Vorlage in einem ganz anderenFormular auf einer anderen Seite nutzen, wo er seine Rechnungs-adresse angeben soll. Damit das möglich ist, geben Sie einen Pfadfür das Cookie an. Dann kann jede Webseite des gleichen Webser-vers, deren URL mit dem von Ihnen angegebenen Pfad-Präfixbeginnt, auf das Cookie zugreifen. Besitzt zum Beispiel ein Cookie,das durch http://www.example.com/catalog/widgets/index.html ge-setzt wurde, einen Pfad »/catalog«, ist dieses Cookie auch fürhttp://www.example.com/catalog/order.html sichtbar. Ist der Pfadsogar auf »/« gesetzt, kann das Cookie auf jeder Seite des Webser-vers http://www.example.com genutzt werden.

Setzt man den path eines Cookies auf »/«, erhält man einenGeltungsbereich wie bei localStorage, zudem wird festgelegt, dassder Browser den Namen und den Wert des Cookies immer dann

Cookies | 257

Page 270: 3868993886_Script

zum Server schicken muss, wenn eine Webseite dieser Site ange-fordert wird.

Standardmäßig bezieht sich der Geltungsbereich von Cookies aufdie Dokument-Herkunft. Große Websites wollen Cookies abervielleicht über Subdomains hinweg nutzen. So muss vielleicht derServer order.example.com Cookie-Werte lesen, die von catalog.example.com gesetzt wurden. Hier kommt das Attribut domain insSpiel. Sind für ein Cookie, das von einer Seite auf catalog.exam-ple.com erstellt wurde, das Attribut path auf »/« und das Attributdomain auf ».example.com« gesetzt, steht dieses Cookie allen Web-seiten auf catalog.example.com, orders.example.com und allen an-deren Servern in der Domain example.com zur Verfügung. Ist dasdomain-Attribut für ein Cookie nicht gesetzt, ist der Standardwertder Hostname des Webservers, der die Seite ausliefert. Beachten Sie,dass Sie die Domain eines Cookies nicht auf eine andere Domain alsdie Ihres Servers setzen können.

Das letzte Cookie-Attribut ist ein boolesches Attribut namens se-cure. Dieses legt fest, wie Cookies über das Netzwerk übertragenwerden. Standardmäßig sind Cookies unsicher, sie werden alsoüber eine normale, unsichere HTTP-Verbindung übermittelt. Istein Cookie als sicher markiert, wird es aber nur dann übertragen,wenn Browser und Server per HTTPS oder über ein anderes sicheresProtokoll verbunden sind.

Cookies speichernUm einen kurzlebigen Cookie-Wert mit dem aktuellen Dokumentzu verbinden, setzen Sie einfach die cookie-Eigenschaft auf einenString im folgenden Format:

name=value

Zum Beispiel:

var v = encodeURIComponent(document.lastModified);document.cookie = "version=" + v;

Wenn Sie das nächste Mal die cookie-Eigenschaft auslesen, findetsich das von Ihnen gespeicherte Name/Wert-Paar in der Liste derCookies für das Dokument. Cookie-Werte können keine Semiko-

258 | Kapitel 14: Clientseitiger Speicher

Page 271: 3868993886_Script

lons, Kommas oder Whitespace enthalten. Aus diesem Grundsollten Sie vielleicht die globale JavaScript-Funktion encodeURICom-ponent() nutzen, um den Wert vor dem Speichern im Cookie zukodieren.

Ein Cookie, das als einfaches Name/Wert-Paar geschrieben wurde,lebt nur, solange es die aktuelle Browsing-Session gibt. Verlässt derAnwender den Browser, wird es gelöscht. Um ein Cookie zu er-zeugen, das über mehrere Browser-Sessions hinweg genutzt werdenkann, geben Sie seine Lebenszeit (in Sekunden) mit einem Attributmax-age an. Dazu setzen Sie die cookie-Eigenschaft auf einen Stringim folgenden Format:

name=value; max-age=seconds

Die folgende Funktion setzt ein Cookie mit einem optionalenAttribut max-age:

// Speichere das Name/Wert-Paar als Cookie,// und kodiere den Wert mit encodeURIComponent(), um// Semikolons, Kommas und Whitespace zu maskieren.// Ist daysToLive eine Zahl, setze das Attribut max-age,// sodass das Cookie nach der angegebenen Anzahl an Tagen// verfallt. Wird 0 ubergeben, wird das Cookie geloscht.function setCookie(name, value, daysToLive) {

var cookie = name + "=" + encodeURIComponent(value);if (typeof daysToLive === "number")

cookie += "; max-age=" + (daysToLive*60*60*24);document.cookie = cookie;

}

Genauso können Sie die Attribute path, domain und secure einesCookies setzen. Sie fügen einfach Strings im folgenden Format anden Cookie-Wert an, bevor er in die cookie-Eigenschaft geschriebenwird:

; path=path; domain=domain; secure

Um den Wert eines Cookies zu ändern, setzen Sie ihn einfach erneutmit dem gleichen Namen, Pfad und der Domain. Sie können dieLebenszeit eines Cookies beim Ändern des Wertes anpassen, indemSie ein neues Attribut max-age angeben.

Cookies | 259

Page 272: 3868993886_Script

Um ein Cookie zu löschen, setzen Sie es erneut mit dem gleichenNamen, Pfad und der Domain, geben einen beliebigen (oder leeren)Wert an und setzen das Attribut max-age auf 0.

Cookies lesenWenn Sie die cookie-Eigenschaft in einem JavaScript-Ausdrucknutzen, handelt es sich bei dem zurückgegebenen Wert um einenString mit allen Cookies, die für das aktuelle Dokument gültig sind.Der String ist eine Liste von name=value-Paaren, die voneinanderdurch ein Semikolon und ein Leerzeichen getrennt sind. Der Coo-kie-Wert enthält keines der Attribute, die Sie eventuell für dasCookie gesetzt haben. Um die document.cookie-Eigenschaft zu nut-zen, müssen Sie meist die Methode split() aufrufen, um den Stringin seine einzelnen name=value-Paare aufzuteilen.

Haben Sie den Wert eines Cookies aus der cookie-Eigenschaftausgelesen, müssen Sie ihn basierend auf dem Format oder derKodierung interpretieren, die der Ersteller des Cookies gewählthat. Sie können den Cookie-Wert zum Beispiel an decodeURICompo-nent() und dann an JSON.parse() übergeben.

Beispiel 14-1 definiert eine Funktion getCookie(), die die docu-ment.cookie-Eigenschaft parst und ein Objekt zurückgibt, dessenEigenschaften die Namen und Werte der Dokument-Cookies ent-halten.

Beispiel 14-1: Die Eigenschaft document.cookies parsen

// Die Dokument-Cookies als Objekt mit Name/Wert-Paaren// zuruckgeben. Annahme: Cookie-Werte sind// mit encodeURIComponent() kodiert.function getCookies() {

var cookies = {}; // Das Objekt, das wir zuruckgeben.var all = document.cookie; // Alle Cookies in einem

// großen String erhalten.if (all === "") // Ist die Eigenschaft leer?

return cookies; // Leeres Objekt zuruckgeben.// In einzelne name=value-Paare aufteilenvar list = all.split("; ");// Schleife uber die Name=Wert-Paare ausfuhren.for(var i = 0; i < list.length; i++) {

var cookie = list[i];

260 | Kapitel 14: Clientseitiger Speicher

Page 273: 3868993886_Script

// Jedes Paar am =-Zeichen auftrennen.var p = cookie.indexOf("=");var name = cookie.substring(0,p);var value = cookie.substring(p+1);// Name und dekodierten Wert speichern.cookies[name] = decodeURIComponent(value);

}return cookies;

}

Grenzen der CookiesCookies sind dazu gedacht, kleine Datenmengen von serverseitigenSkripten setzen zu lassen, und die Daten werden auch jedes Malübertragen, wenn eine relevante URL angefordert wird. Der Stan-dard, der Cookies definiert, ermuntert die Browser-Hersteller, eineunbegrenzte Zahl von Cookies mit beliebiger Größe zu ermögli-chen. Erforderlich sind aber nicht mehr als 300 Cookies insgesamt,20 Cookies pro Webserver oder 4 KB Daten pro Cookie (sowohl derName als auch der Wert zählen dazu). In der Praxis erlauben dieBrowser viel mehr als 300 Cookies insgesamt, aber die 4-KB-Grenzekann immer noch wirksam sein.

Cookies | 261

Page 274: 3868993886_Script
Page 275: 3868993886_Script

Index

Symbole»200 OK«-Response 237»404 Not Found«-Response 237,

239! booleschen Wert invertieren 31! unärer Negations-Operator 19!= Ungleichheit/Ungleich-Opera-

tor 14, 31, 40!== Keine-Identität-Operator 31,

40, 85#top Bezeichner 174$ Bezeichner 2$ String-/Zeilenende 162% (Prozentzeichen) 7% Modulo-Operator 35& bitweises UND 32, 37&& logisches UND 32, 42&=-Operator 46\ Trefferanzahl 159\B keine Wortgrenze 163\b Wortgrenze 163\d ASCII-Ziffer 158\D keine-ASCII-Ziffer 158\S kein-Unicode-Leerzeichen 158\s Unicode-Leerzeichen 158\w ASCII-Wortzeichen 158\W kein-ASCII-Wortzeichen 1589 Existenzquantor 1098 »für alle« Allquantor 109> Größer-als-Operator 31, 41>> mit Vorzeichenerweiterung

nach rechts verschieben 31, 38

>>> mit Nullauffüllung nachrechts verschieben 31, 39

>= Größer-oder-gleich-Operator31, 41

< Kleiner-als-Operator 31, 41<= Kleiner-oder-gleich-Operator

31, 41" " (Anführungszeichen, doppelte)

10' ' (Anführungszeichen, einfache)

10(?! ) negativer Lookahead 163(?: ) runde Klammern für Grup-

pierung 161(?= ) positiver Lookahead 163* (Asterisk) 7* Multiplikations-Operator 31, 34* Wiederholungszeichen 158* wildcard-Argument 191*=-Operator 46+ (Pluszeichen) 7+ Additions-Operator 35+ in Zahl umwandeln 31+ Strings verketten 31+ unärer Plus-Operator 19, 36+ Wiederholungszeichen 158++ Inkrement-Operator 31, 36, 53++ Prä-/Post-Inkrement 31+=-Operator 46, 200, ersten Operanden verwerfen,

zweiten Operanden zurück-geben 32, 50

- (Minuszeichen) 7

Index | 263

Page 276: 3868993886_Script

- Subtraktions-Operator 35- unärer Minus-Operator 36- Zahl negieren 31– Dekrement-Operator 31, 36, 53– Prä-/Post-Dekrement 31. Punkt-Operator 15. value of-Eigenschaft 82/ (Schrägstrich) 7/ Divisions-Operator 34/* */ mehrzeiliger Kommentar 1// einzeiliger Kommentar 1/[ ]/ reguläre Ausdrücke 158; leere Anweisung 54; Semikolon 3= Zuweisung auf Variable/Eigen-

schaft 32, 45== Gleichheits-Operator 15, 18,

31, 40=== Identitäts-Operator (strikte

Gleichheit) 15, 31, 39? Wiederholungszeichen 158?: Bedingungsoperator 32, 48[] value of-Eigenschaft 82[]-Operator 15, 28, 98{} geschweifte Klammern 27, 54,

72, 78, 116| Alternative 161| bitweises ODER 32, 37|-Trennzeichen 159|| logisches ODER 32, 44||-Operator (logisches ODER) 127~ Bits invertieren 31~ bitweises NOT 38^ bitweises XOR 32, 37^ negierte Zeichenklassen 157^ Stringanfang 162_ Bezeichner 2

A<a>-Element 191, 197, 215abs-Eigenschaft, Math-Objekt 8add()-Methode 208addEventListener()-Methode 225,

228, 230, 238, 240, 254

alert()-Methode 73, 172, 177Allquantor (»für alle«) 8 109altKey-Eigenschaft, mouse-Events

217, 219angepasste Geltungsbereichskette

228Anker 162Anweisungen 51, 73

; (Semikolon)-Trennzeichen 3Ausdruck 53bedingungsabhängig 57Deklaration 55Regeln zum Abschluss 4Schleifen 62Sprung 66Syntax 52zusammengesetzt und leer 54

Anweisungen markieren 67Anweisungsblöcke 54Apostroph 11appendChild()-Methode 202Apple iPhone/iPad 222apply()-Methode 120, 124, 135appName-Eigenschaft, Navigator-

Objekt 176appVersion-Eigenschaft, Naviga-

tor-Objekt 176Argument eines Event-Handlers

227Argumente 115Argumentlisten variabler Länge

127arguments-Objekt 127arithmetische Operatoren 34Arrays 97

anlegen 98Array()-Funktion 16, 79, 98»Array-literale« Initialisierer 26Array-artige Objekte 112, 127Array.concat()-Methode 104Array.isArray()-Funktion 111Array.join()-Methode 102Array.reverse()-Methode 103Array.slice()-Methode 105

264 | Index

Page 277: 3868993886_Script

Array.sort()-Methode 103Array.splice()-Methode 105durchlaufen 101length-Eigenschaft 100Literale 99Methoden 102multidimensional 102Typen 111

asynchrone I/O-Events 222attachEvent()-Methode 254Attribute 197<audio>-Element 220Aufruf

Event-Handler 227von Funktionen 120indirekt 124Konstruktor 123von Methoden 120

Aufrufausdrücke 29Aufrufkontext 115»Aufzieh«-Geste 223Ausdrücke

Aufruf 29Auswertung 46definiert 25Funktionsdefinition 29Initialisierer 26logisch 42Methode 30Objekterzeugung 30primär 26relational 39Zugriff auf Eigenschaften 28Zuweisung 45

Ausdrücke auswerten 25Ausdrücke zur Definition von

Funktionen 29Ausdrücke zur Objekterzeugung 30Ausdrucks-Anweisungen 53Ausnahmen 70Auswertungsausdrücke 46availHeight-Eigenschaft, Wind-

ow-Objekt 177

availWidth-Eigenschaft, Window-Objekt 177

Bback().Methode 174Backslash (\) Escape-Zeichen 10,

158Backspace 11Basis 10 6Baumstruktur 186Bedingungsanweisungen 51, 57beforeunload-Events 217Bezeichner 1–2Beziehungen zwischen Frames

179bidirektionale Socket-artige Ver-

bindung 246Bildschirm, Informationen zu 176binäre Operatoren 33bind()-Methode 136Blockgeltung 22blur-Events 216<body>-Element 192, 225Boolean()-Funktion 18Boolesche Werte 5, 12borderLeftWidth-Eigenschaft 205break-Anweisung 52, 67break-Schlüsselwort 4, 61Browser und Bildschirm, Informa-

tionen zu 175Browser-Location und Navigation

173Browser-Verlauf 174<button>-Element 178, 226

Ccall()-Methode 94, 120, 124, 135,

152Caret-Zeichen (^) bitweises XOR

32, 37Caret-Zeichen (^) für negierte

Zeichenklasse 157Cascading Style Sheets (CSS) 204

Index | 265

Page 278: 3868993886_Script

case-Anweisung 52catch-Klausel 71catch-Schlüsselwort 72ceil-Eigenschaft, Math-Objekt 8change-Events 216changedTouches-Eigenschaft,

Event-Objekt 223charAt()-Methode 12, 113charCode-Eigenschaft, keypress-

Event-Objekt 219checkscope()-Funktion 131childElementCount-Eigenschaft

197childNodes-Eigenschaft, Node-

Objekt 195children-Eigenschaft,-Element-

Objekte 197class-Attribut 192, 206classList-Eigenschaft 207className-Eigenschaft 193, 198,

207classof()-Funktion 94clear()-Methode 253clearTimeout()-Methode 173Clientseitiger Speicher

API 253Cookies 256Events 254Lebensdauer und Geltungs-

bereich 252Speichern, Sicherheit und Pri-

vatsphäre 249clientseitiges JavaScript

Browser und Bildschirm, Infor-mationen zu 175

Browser-Location und Naviga-tion 173

Browser-Verlauf 174Dialogfenster 177Document-Elemente 178einbetten in HTML 169Event-gesteuerte Programmie-

rung 171

mehrere Fenster und Frames178

Timer 172Window-Objekt 171

clientX-Eigenschaft, mouse-Events 217

clientY-Eigenschaft, mouse-Events 217

cloneNode()-Methode 202close()-Methode 247Closures 116, 130color-Eigenschaft 205concat()-Methode 104configurable-Attribut 90confirm()-Methode 177constfuncs()-Funktion 134contains()-Methode 208contentWindow-Eigenschaft,

Window-Objekt 180continue-Anweisung 52, 68continue-Schlüsselwort 4cookie-Eigenschaft, Document-

Objekt 256Cookies 250, 256Cookies lesen 260CORS (Cross-Origin Resource

Sharing) 241count()-Funktion 133counter()-Funktion 133createElement()-Methode 202Cross-Document Messaging API

221Cross-Origin-URLs 240CSS (Cascading Style Sheets) 204CSS-Klassen 188, 192CSSStyleDeclaration-Objekt 205ctrlKey-Eigenschaft, mouse-

Events 217, 219

Ddata-Eigenschaft

event-Objekte 88, 221, 245dataTransfer-Eigenschaft 220Date()-Funktion 16, 79

266 | Index

Page 279: 3868993886_Script

debugger-Anweisung 52, 74decodeURIComponent()-Funk-

tion 260default actions 215defineClass()-Funktion 149, 152defineEigenschaften()-Methode,

Object. 150defineProperties()-Methode, -Ob-

jekt 92defineProperty()-Methode, -Ob-

jekt 92defineSubclass()-Funktion 152Deklaration von Variablen 21Deklarationsanweisungen 55»DELETE«-Methode 235delete-Operator 31, 34, 49, 53detail-Eigenschaft, mouse-Events

217Dialogfenster 177Digitaluhr 169dir-Attribut 197Direktive 74<div>-Element 192Division durch Null 8do/while-Schleife 52, 63Document Object Model (DOM)

185, 230document-Eigenschaft, Window-

Objekt 172document-Elemente

anhand der CSS-Klasse aus-wählen 192

nach ID auswählen 188nach Name auswählen 189über CSS-Selektoren auswäh-

len 193über ihren Typ auswählen 190

Document-Elemente 178documentElement-Eigenschaft,

Document-Klasse 192Dokument-globale Variable 188Dokument-Knoten 201Dokumentstruktur und Bewegen

im Dokumentenbaum 195

Dollarzeichen ($) Bezeichner 2DOM (Document Object Model)

185, 230domain-Attribut 257DOMContentLoaded-Events 216doppeltes Anführungszeichen 11,

161Drag-and-Drop-Events 220dynamische Arrays 97

EE-Eigenschaft, Math-Objekt 8eckige Klammern []-Operator 82ECMAScript 3 3, 120, 125, 136ECMAScript 5 3

Array-Methoden 107Backslash in 11bind()-Methode 136die Werte null und undefined

125Eigenschaftsattribute abfragen

und setzen 90for/in-Schleifen 66Funktionsaufrufe 120Getter- und Setter-Methoden

88Object.getOwnPropertyNa-

mes() 87Object.keys() 87Strings als Arrays 98, 113»use strict«-Direktive 74Zuweisung auf nicht dekla-

rierte Variablen 22Eigenschaften 77

abfragen und setzen 82Attribute 90data 88Deskriptoren 91Document-Elemente als 178Erweiterbarkeit und 95Existenz (in-Operator) 41function 135Iterieren über 52, 86Konstruktor 145

Index | 267

Page 280: 3868993886_Script

Kurzschrift 191löschen 31, 83Math-Objekt 7propertyIsEnumerable()-Me-

thode 84prototype 135, 145testen 84Vererbung 82Zugriff 28, 88

einbetten von JavaScript in HTML169

einfaches Anführungszeichen 11,161

eingebetteter Browsing Context179

Element-Inhalt 199elementare Typen 5Elemente 97Elemente über CSS-Selektoren

auswählen 193Elemente über die CSS-Klasse

auswählen 192Elemente über ihre ID auswählen

188Elemente über ihren Namen aus-

wählen 189Elemente über ihren Typ auswäh-

len 190Elementstil 204else if-Anweisung 59Elternknoten 186encodeURIComponent()-Funk-

tion 259enumerable-Attribut 85, 90Escape-Sequenzen 10Escape-Zeichen (\) 10, 158eval()-Funktion 15, 46, 76Event

Bubbling 230Listener 214Name 213Propagation 214, 230Typ 213Ziel 213

Event-gesteuerte Programmierung171

Event-HandlerArgument 227Attribute setzen 224Browser 225Eigenschaften setzen 224Formular 215Geltungsbereich 228HTML5 219Kontext 228Maus 217onmessage 246registrieren 223Rückgabewert 229Tastatur 219Touchscreen- und Mobil 222Window 216

Event-Handler für Touchscreen-und Mobilgeräte 222

Event-Objekt 214Events 213

abbrechen 231abfangen 226, 231Propagation 227

EventSource()-Konstruktor 245every()-Methode 109exec()-Methode 166execScript()-Funktion 48Existenzquantor 9 109exp-Eigenschaft, Math-Objekt 8explizite Typ-Umwandlung 18

Ffactorial()-Methode 72Fabrikfunktion 140filter()-Methode 108finally-Blocks 72firstChild-Eigenschaft, Node-Ob-

jekt 195firstElementChild-Eigenschaft 197Fließkommazahlen 6float-Eigenschaft 205floor-Eigenschaft, Math-Objekt 8

268 | Index

Page 281: 3868993886_Script

focus-Events 216font-size-Eigenschaft 205font-weight-Eigenschaft 205for-Anweisung 52, 63for-Schleife 21, 52, 101for-Schlüsselwort 65for/in-Schleife 21, 52, 65, 86forEach()-Methode 101, 108, 152<form>-Element 190, 198, 228,

230, 241

Fforms-Eigenschaft, HTMLCol-

lection-Objekte 191Formular-Event-Handler 215Formularvalidierung, Mechanis-

mus 221forward()-Methode 174<frame>-Element 179frameElement-Eigenschaft, Wind-

ow-Objekt 180Frames 178Frames-Eigenschaft, Window-Ob-

jekt 178<frameset>-Element 179freeze()-Funktion 95function()-Konstruktor 135, 137function-Anweisung 52function-Schlüsselwort 56, 89,

130, 137, 182, 225Funktionen 115

Argumente und Parameter 126Aufrufausdrücke 29aufrufen 120Definieren 116Deklaration 56Geltungsbereich 22als Namensräume 129scope 6verschachtelt 119

Funktionsaufrufe 4, 54

Gg (globaler Vergleich) 163

Geltungsbereich 250, 256Geltungsbereich einer Variable 22Geltungsbereich Event-Handler

228generische Methoden 98geolocation-Eigenschaft, Naviga-

tor-Objekt 177Geometrie und Scrolling 208geschweifte Klammern {}

Anweisungsblock 54, 72Funktionen und 29, 57, 116Objekt-Initialisierer 27, 31Objektliterale und 78

Geschwisterknoten 186»GET«-Methode 235get-Attribut 90getAllResponseHeaders()-Me-

thode 237getAttribute()-Methode 198, 206getBoundingClientRect()-Me-

thode 210getCookie()-Funktion 260getElementById()-Methode 181,

188getElements()-Funktion 189getElementsByClassName()-Me-

thode 193getElementsByName()-Methode

190, 192getElementsByTagName()-Me-

thode 190, 192getItem()-Methode 253getJSONP()-Funktion 243getOwnPropertyDescriptor()-

Funktion 91getOwnPropertyNames()-Funk-

tion 87getPrototypeOf()-Methode 93getResponseHeader()-Methode

237Getter 88getText()-Funktion 238gierige Wiederholung 159

Index | 269

Page 282: 3868993886_Script

global-Eigenschaft, RegExp-Ob-jekt 166

global-Objekt 15globale Variablen 6, 188globaler Geltungsbereich 22, 131globaler Vergleich (g) 163go()-Methode 175grabAttention()-Funktion 207Groß-/Kleinschreibung 1

HhandleResponse()-Funktion 243hasAttribute()-Methode 199hash-Eigenschaft, Location-Ob-

jekt 174hasOwnProperty()-Methode 84<head>-Element 192»HEAD«-Methode 235height-Eigenschaft, Window-Ob-

jekt 177Herkunft eines Dokuments 183hexadezimal 7history-Eigenschaft, Window-Ob-

jekt 174Hoisting 23Horizontaler Tabulator 11host-Eigenschaft, Location-Objekt

173hostname-Eigenschaft, Location-

Objekt 173href-Eigenschaft, Location-Objekt

173, 191, 197HTML

DOM-Überblick 185Element-Inhalt als 200Groß- und Kleinschreibung

191HTML5-Event-Handler 219HTMLDocument-Typ 187HTMLElement 178, 187, 197htmlFor-Eigenschaft 198

HTTPCross-Origin-Requests 240http:-Protokoll 183

JSONP 241per Skript erzeugte Requests

184Progress-Events 239Response erhalten 237USER-AGENT-Header 176

hypotenuse()-Funktion 119

Ii (keine Unterscheidung zw. Groß-

und Kleinschreibung) 163id-Attribut 178, 181, 188, 197IEEE 754-Standard 6if-Anweisung 52, 57–58if/else-Anweisung 52<iframe>-Element 175, 180, 189,

209, 241, 253ignoreCase-Eigenschaft, RegExp-

Objekt 166images-Eigenschaft, HTMLCol-

lection-Objekte 191<img>-Element 189, 197in-Operator 31, 41, 85includes()-Methode 152index 97index-Eigenschaft, RegExp-Ob-

jekt 167indexOf()-Methode 111Infinity globale Eigenschaft 15Infinity-Wert 8inherit()-Funktion 81, 83, 140,

151Initialisierer 26Initialisierungsausdruck 64Inkrement-Ausdruck 64Inline-Stildefinitionen (HTML)

206innerHeight-Eigenschaft, Wind-

ow-Objekt 210innerHTML-Eigenschaft, Wind-

ow-Objekt 200innerWidth-Eigenschaft 210<input>-Element 198

270 | Index

Page 283: 3868993886_Script

input-Eigenschaft, RegExp-Objekt167

insertAdjacentHTML()-Methode200

insertBefore()-Methode 202instanceof-Operator 31Instanzen 139, 147Instanzfelder 147Instanzmethoden 148Instanzobjekt 148Integerwerte 6interagierende Fenster 181Internet Explorer 8, Microsoft 254isArray()-Funktion 111isExtensible()-Funktion 95isFinite()-Funktion 9isFrozen()-Funktion 95isNaN()-Funktion 9, 15isPrototypeOf()-Methode 94isSealed()-Funktion 95

JJava-artige Klassen 147join()-Methode 102JSON.parse()-Funktion 87, 238,

242, 260JSON.stringify()-Funktion 87

Kkeine Unterscheidung zw. Groß-

und Kleinschreibung (i) 163key()-Methode 253key-Eigenschaft, Event-Objekt 255keyCode-Eigenschaft 219keydown Event-Typ 213keys()-Funktion 87Kindknoten 186Klassen 139

und Konstruktoren 142erweitern 151Erweiterung 153und Prototypen 140unveränderlich (immutable) 150Zeichen 157

Klassen erweitern 153Klassenfelder 148Klassenhierarchie 187Klassenmember 147Klassenmethoden 148Knoten VIII, 185

erstellen, einfügen und löschen201

NodeList-Objekt 190NodeList-Objekte 194nodeName-Eigenschaft, Node-

Objekt 196nodeType-Eigenschaft, Node-

Objekt 196nodeValue-Eigenschaft, Node-

Objekt 196, 201Komma-Operator (,) 32, 50, 116,

258Kommentare 1Konstruktoren

Eigenschaft 145eingebaut 79function() 135, 137Klassen und 142

Kontext Event-Handler 228Kontrollstrukturen 51Kurzschrift-Eigenschaften 191<label>-Element 198

Llabel-Anweisung 52lang-Attribut 197Länge eines Strings 9lastChild-Eigenschaft, Node-Ob-

jekt 195lastElementChild-Eigenschaft 197lastIndex-Eigenschaft, RegExp-

Objekt 166lastIndexOf()-Methode 111Latin-1-Kodierung 10, 156Lebensdauer 250, 256leere Anweisungen 52, 54leerer String 9Leerzeichen 258

Index | 271

Page 284: 3868993886_Script

length-Eigenschaft 11, 97, 100,112, 135

lengthComputable-Eigenschaft,Event-Objekt 240

lexikalische Geltung 6lexikalischer Geltungsbereich 130Link-Objekte 174links-Eigenschaft, HTMLCollecti-

on-Objekte 191Literale

Strings 10Werte 26Zeichen in regulären Ausdrü-

cken 156LN2-Eigenschaft, Math-Objekt 8LN10-Eigenschaft, Math-Objekt 8load-Events 213, 216loaded-Eigenschaft, Event-Objekt

240localStorage-Eigenschaft, Win-

dow-Objekt 250location-Eigenschaft, Window-

Objekt 172–173location.hash 175log-Eigenschaft, Math-Objekt 8logische Ausdrücke 42lokale Dateien und XMLHttpRe-

quest 234lokale Variablen 22lokaler Geltungsbereich 131lvalue 33

Mm (Mehrzeilen-Modus) 163Malware 250map()-Methode 108match()-Methode 165, 167Maus-Event-Handler 217max()-Funktion, Math-Objekt 125max()-Funktion, Math. 128max-age-Attribut 257max-Eigenschaft, Math-Objekt 8mehrere Fenster und Frames 178Mehrzeilen-Modus (m) 163

message-Eigenschaft, Error-Ob-jekt 70

metaKey-Eigenschaft, mouse-Events 217, 219

Methodenaufrufen 121überschreiben 151

Methoden überschreiben 151Methoden-Ausdrücke 30min-Eigenschaft, Math-Objekt 8Monkey-Patching 126mousedown-Events 217mouseenter-Events 218mouseleave-Events 218mousemove-Events 213, 217mouseout-Events 218mouseup-Events 217mousewheel-Events 218multiline-Eigenschaft, RegExp-

Objekt 166Mustervergleich

Strings, Methoden für 164Mustervergleiche

RegExp Eigenschaften undMethoden 166

NNachfahrenknoten 186nach links verschieben (<<) 31, 38name-Attribut 181, 189name-Eigenschaft, Error-Objekt

70Namensräume, Funktionen als

129NaN (not-a-number)-Wert 8, 15Navigation, Browser 173navigator-Eigenschaft, Window-

Objekt 176Nebeneffekte 34negativer Infinity-Wert 8Netzwerkverbindungen

HTTP per <script>: JSONP241

Server-Sent-Events 245

272 | Index

Page 285: 3868993886_Script

WebSockets 246XMLHttpRequest verwenden

233new-Schlüsselwort 30, 123, 142newValue-Eigenschaft, Event-Ob-

jekt 255nextElementSibling-Eigenschaft

197nextSibling-Eigenschaft, Node-

Objekt 195nicht dekrementierter Wert 37nicht inkrementierter Wert 36nicht-gierige Wiederholung 159not-a-number (NaN)-Wert 8NUL-Zeichen 11, 156Null, Division durch 8null-Schlüsselwort 14nullbasierte Arrays 97»Objekt-literale« Initialisierer 26

OObject()-Funktion 16, 18, 79

Object.create()-Methode 78,80, 93, 140, 151

Object.defineProperties()-Me-thode 92

Object.defineProperty 91Object.defineProperty()-Me-

thode 92Object.getOwnPropertyNames

87Object.keys 87Object.prototype 79, 83, 86Object.reload()-Methode 174

object-Datentyp 77Objektattribute

class-Attribut 94extensible-Attribut 95prototype-Attribut 93

Objektliterale 78Objekttypen 5, 42Offline-Web-Applikationen 221oldValue-Eigenschaft, Event-Ob-

jekt 255

onbeforeunload-Handler,Window 229

onchange-Eigenschaft, Event-Ob-jekt 224

onclick-Attribut, Event-Objekt197

onclick-Eigenschaft, Event-Objekt224, 226

onhashchange Event-Handler 175onLine-Eigenschaft, Navigator-

Objekt 177onload Event-Handler 171onload-Eigenschaft, Event-Objekt

224, 240onmouseover-Eigenschaft, Event-

Objekt 224onpopstate Event-Handler 175onprogress-Eigenschaft 240onreadystatechange-Eigenschaft,

XMLHttpRequest-Objekt 238onstorage-Eigenschaft, Window-

Objekt 254open()-Methode 235Operanden 25Operatoren 25, 31, 48

arithmetisch 34relational 39

Optionale Parameter 126Optionale Semikola 3»OPTIONS«-Methode 235orientation-Eigenschaft, Window-

Objekt 223orientationchange-Event 222Origin Policy 221overflow-Eigenschaft setzen 217<p>-Element 191, 199

Ppadding 210pageXOffset-Eigenschaft,

Window-Objekt 209pageYOffset-Eigenschaft,

Window-Objekt 209Parameter 115, 126

Index | 273

Page 286: 3868993886_Script

parent-Eigenschaft,Window-Objekt 180

parentNode-Eigenschaft, Node-Objekt 195

parseFloat()-Funktion 20parseInt()-Funktion 15, 20path-Attribut 257pathname-Eigenschaft, Location-

Objekt 174PI-Eigenschaft, Math-Objekt 8platform-Eigenschaft, Navigator-

Objekt 176pop()-Methode 106port-Eigenschaft, Location-Objekt

174»POST«-Methode 235–236pow-Eigenschaft, Math-Objekt 8preventDefault()-Methode 230preventExtensions()-Funktion 95previousElementSibling-Eigen-

schaft 197previousSibling-Eigenschaft, No-

de-Objekt 195primäre Ausdrücke 26Privatsphäre 250prompt()-Methode 73, 177protocol-Eigenschaft, Location-

Objekt 173Prototyp

Objekt 148prototype-Attribut 93prototype-Eigenschaft 79, 124,

135, 142, 145Prototypen 79

und Klassen 140Ketten 80Objekt 135

Prozedur 115Punkt-Operator (.) 15, 28, 82, 158push()-Methode 106pushState()-Methode 175»PUT«-Methode 235

QquerySelectorAll()-Methode 194

Rrandom-Eigenschaft, Math-Ob-

jekt 8range()-Funktion 142Range()-Konstruktor 143Range-Klasse 143readyState-Eigenschaft,

XMLHttpRequest-Objekt238–239

readystatechange-Events 216, 238reduce()-Methode 109reduceRight()-Methode 109reelle Zahlen 7RegExp()-Funktion 16, 79, 155,

164Registrierung von Event-Handler

223reguläre Ausdrücke 155

Alternativen, Gruppierung undReferenzen 159

Mustervergleiche mit 164Schalter 163Suchmuster beschreiben mit

155Trefferposition angeben 161Wiederholung in 158Zeichenklassen 157

Reintext, Element-Inhalt als 200relatedTarget-Eigenschaft, Event-

Objekt 218reload()-Methode 174remove()-Methode 208removeAttribute()-Methode 199removeChild()-Methode 203removeEventListener()-Methode

227removeItem()-Methode 253replace()-Methode 12, 164–165replaceChild()-Methode 203reservierte Wörter 2, 198reset-Events 216

274 | Index

Page 287: 3868993886_Script

Response-Body 237Response-Header 237responseText-Eigenschaft,

XMLHttpRequest-Objekt 237,241

return-Anweisung 30, 52, 69return-Schlüsselwort 4, 124reverse()-Methode 103rotation-Eigenschaft, Event-Ob-

jekt 223round-Eigenschaft, Math-Objekt

8Rückgabewert 229runde Klammern 116, 160Runden 9

SSafari 222Same-Origin-Policy 183scale-Eigenschaft, Event-Objekt

223Schalter 163Schleifenanweisungen 62screen-Eigenschaft, Window-Ob-

jekt 176–177<script>-Element 169, 183, 233,

241Scroll Offsets 209scrollBy()-Methode 211Scrolling 208scrollIntoView()-Methode 211scrollTo()-Methode 210seal()-Funktion 95search()-Methode 164search-Eigenschaft, Location-Ob-

jekt 174secure-Attribut 258Security und Skripten 244Seitenvorschub 11, 156Selektoren, CSS 193Semikolon (;) 3, 258send()-Methode 236, 239, 247Serialisierung 87Server-Sent-Events 221

sessionStorage-Eigenschaft,Window-Objekt 250

set-Attribut 90setAttribute()-Methode 198, 206setInterval()-Methode 172setItem()-Methode 253setRequestHeader()-Methode 236Setter 88setTimeout()-Methode 172shift()-Methode 106shiftKey-Eigenschaft, mouse-

Events 217, 219Sicherheit 250sin-Eigenschaft, Math-Objekt 8slice()-Methode 105some()-Methode 109sort()-Methode 103Source Policy 221source-Eigenschaft, RegExp-Ob-

jekt 166<span>-Element 190splice()-Methode 105split()-Methode 103, 166, 260Sprunganweisungen 66Spyware 250sqrt-Eigenschaft, Math-Objekt 8square()-Funktion 119src-Eigenschaft, HTMLElement

183, 197status-Eigenschaft, XMLHttpRe-

quest-Objekt 237statusText-Eigenschaft,

XMLHttpRequest-Objekt 237Stelligkeit 33Steuerzeichen 156stopImmediatePropagation()-Me-

thode 232stopPropagation()-Methode 232storageArea-Eigenschaft, Event-

Objekt 255Strict-Code 75Strict-Modus 2, 22, 75, 125String()-Funktion 16, 18Stringliterale 10

Index | 275

Page 288: 3868993886_Script

Strings 5, 10und Mustervergleiche 164Unveränderlichkeit 12

style-Eigenschaft, -Element-Ob-jekt 204

submit-Events 216Subroutine 115Superklassen 151switch-Anweisung 52–53, 60

TTabulator 156target-Eigenschaft, Event-Objekt

214, 220Tastatur-Event-Handler 219Tastaturkürzel 171, 219ternärer Operator (?:) 33test()-Methode 166Test-Ausdruck 64Text

Stringliterale 10textContent()-Funktion 201textContent-Eigenschaft, Node-

Objekt 200Textknoten, Element-Inhalt als

201this-Schlüsselwort 16, 31, 108,

134, 228throw-Anweisung 52–53, 70Timer 172timestamp-Eigenschaft, Event-

Objekt 240title-Attribut 197toggle()-Methode 208top-Eigenschaft, Window-Objekt

180toString()-Methode 19, 94, 107,

137, 152, 173total-Eigenschaft, Event-Objekt

240toUpperCase()-Methode 12, 122trace()-Funktion 126Trefferposition 161Trefferposition angeben 161

trim()-Methode, String 154try-Anweisung 53try/catch/finally-Anweisung 71Typ-Hierarchie 187type-Eigenschaft, Event-Objekt

214, 240, 245Typen 5

automatische Umwandlung 5Umwandlungen 16

typeof-Operator 31, 33, 49typlose Arrays 97

Uunäre Operatoren 33, 36undefined-Schlüsselwort 14Unicode-Zeichen 11, 156, 158uniqueInteger()-Funktion 132unload-Events 216unshift()-Methode 106Unterklassen 151Unterstrich (_) Bezeichner 2unveränderliche (immutable)

Klassen 150upload-Eigenschaft, XMLHttpRe-

quest-Objekt 240URL-Dekompositions-Eigenschaf-

ten 174url-Eigenschaft, Event-Objekt 255URLs 235, 240, 252use strict-Anweisung 53, 74USER-AGENT HTTP-Header 176userAgent-Eigenschaft, Navigator-

Objekt 176

Vvalue-Attribut 90var-Anweisung 53, 55var-Schlüsselwort 6, 21, 182Varargs-Funktionen 128Variablen

Deklaration 5, 21globale 6lokal 22typlos 5

276 | Index

Page 289: 3868993886_Script

Variablen deklarieren 5Verketten von Strings 11Verlaufsmanagement-Mechanis-

mus 220Vertikaler Tabulator 11, 156Verzweigungen 58<video>-Element 220

VViewport 209void-Operator 31, 50Vorfahrenknoten 186

WWagenrücklauf 11, 156WebSocket()-Konstruktor 246WebSockets 221, 246Werte 5Werte zurückgeben 25, 115Wertüberläufe 8Wertunterläufe 8while Standardanweisung 52while-Anweisung 52–53, 62while-Schleife 54, 57Whitespace-Zeichen 259width-Eigenschaft, Window-Ob-

jekt 177Wiederholung 158window-Bezeichner 171

window-Eigenschaft, Window-Objekt 16, 172

Window-Event-Handler 216Window-Objekt 171, 180window.onhashchange 175window.onpopstate 175wissenschaftliche Notation 7with statements 73with-Anweisungen 53, 228writable-Attribut 90

XXHR2-Spezifikation (Entwurf)

239XML 187XMLHttpRequest-Objekt 184,

230, 235, 237

ZZahlen 6Zahlliteral 6Zeichenklassen 157Zeilenumbrüche 4, 11, 156Zugriffseigenschaften 88zusammengesetzte Anweisungen

54»Zusammenzieh«-Geste 223Zuweisungsausdrücke 45

Index | 277

Page 290: 3868993886_Script