JavaScript für Ungeduldige - Cay Horstmann - E-Book

JavaScript für Ungeduldige E-Book

Cay Horstmann

0,0

Beschreibung

DER schnelle Einstieg in modernes JavaScript

  • Schneller und praxisnaher Einstieg für Entwickler*innen mit Vorkenntnissen in Java, C, C++ oder C#
  • Direkter Einstieg in aktuelles JavaScript (ES2020)
  • Beispiele und Übungen für das Lernen direkt an der Tastatur

JavaScript für Ungeduldige ist ein vollständiger und dennoch prägnanter Leitfaden für modernes JavaScript, bis zu ES2020. Wenn Sie mit Sprachen wie Java, C#, C oder C++ umgehen können, werden Sie mit diesem Buch schnell mit JavaScript produktiv arbeiten können, ohne sich lange mit veralteten Konzepten rumschlagen zu müssen.

Sie lesen das E-Book in den Legimi-Apps auf:

Android
iOS
von Legimi
zertifizierten E-Readern
Kindle™-E-Readern
(für ausgewählte Pakete)

Seitenzahl: 429

Das E-Book (TTS) können Sie hören im Abo „Legimi Premium” in Legimi-Apps auf:

Android
iOS
Bewertungen
0,0
0
0
0
0
0
Mehr Informationen
Mehr Informationen
Legimi prüft nicht, ob Rezensionen von Nutzern stammen, die den betreffenden Titel tatsächlich gekauft oder gelesen/gehört haben. Wir entfernen aber gefälschte Rezensionen.



Cay Horstmann ist Hauptautor von Core Java™, Band I und II, 11. Auflage (Pearson, 2018), Scala for the Impatient, 2. Auflage (Addison-Wesley, 2016) und Core Java SE 9 for the Impatient (Addison-Wesley, 2017). Er ist emeritierter Professor für Informatik an der San José State University (Kalifornien, USA), Java-Champion und häufiger Redner auf Konferenzen der Computerbranche.

Zu diesem Buch – sowie zu vielen weiteren dpunkt.büchern – können Sie auch das entsprechende E-Book im PDF-Format herunterladen. Werden Sie dazu einfach Mitglied bei dpunkt.plus+:

www.dpunkt.plus

Cay Horstmann

JavaScript fürUngeduldige

Der schnelle Einstieg in modernes JavaScript

Cay Horstmann

Lektorat: Melanie Andrisek

Übersetzung: Volkmar Gronau

Copy-Editing: Alexander Reischert, www.aluan.de

Satz: G&U Language & Publishing Services GmbH, Flensburg, www.GundU.com

Herstellung: Stefanie Weidner

Umschlaggestaltung: Helmut Kraus, www.exclam.de

Bibliografische Information der Deutschen Nationalbibliothek

Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über http://dnb.d-nb.de abrufbar.

ISBN:

Print    978-3-86490-801-9

PDF     978-3-96910-093-6

ePub   978-3-96910-094-3

mobi   978-3-96910-095-0

1. Auflage 2021

Copyright © 2021 dpunkt.verlag GmbH

Wieblinger Weg 17

69123 Heidelberg

Authorized translation from the English language edition, entitled MODERN JAVASCRIPT FOR THE IMPATIENT, 1st Edition by CAY HORSTMANN, published by Pearson Education, Inc, publishing as Addison-Wesley Professional, Copyright © 2020 Pearson Education, Inc

All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from Pearson Education, Inc.

Hinweis:Dieses Buch wurde auf PEFC-zertifiziertem Papier aus nachhaltiger Waldwirtschaft gedruckt. Der Umwelt zuliebe verzichten wir zusätzlich auf die Einschweißfolie.

Schreiben Sie uns:Falls Sie Anregungen, Wünsche und Kommentare haben, lassen Sie es uns wissen: [email protected].

Die vorliegende Publikation ist urheberrechtlich geschützt. Alle Rechte vorbehalten. Die Verwendung der Texte und Abbildungen, auch auszugsweise, ist ohne die schriftliche Zustimmung des Verlags urheberrechtswidrig und daher strafbar. Dies gilt insbesondere für die Vervielfältigung, Übersetzung oder die Verwendung in elektronischen Systemen.

Es wird darauf hingewiesen, dass die im Buch verwendeten Soft- und Hardware-Bezeichnungen sowie Markennamen und Produktbezeichnungen der jeweiligen Firmen im Allgemeinen warenzeichen-, marken- oder patentrechtlichem Schutz unterliegen.

Alle Angaben und Programme in diesem Buch wurden mit größter Sorgfalt kontrolliert. Weder Autor noch Verlag noch Übersetzer können jedoch für Schäden haftbar gemacht werden, die in Zusammenhang mit der Verwendung dieses Buches stehen.

5 4 3 2 1 0

Inhalt

Vorwort

1Werte und Variable

1.1JavaScript ausführen

1.2Typen und der Operator typeof

1.3Kommentare

1.4Variablendeklarationen

1.5Bezeichner

1.6Zahlen

1.7Arithmetische Operatoren

1.8Boolesche Werte

1.9null und undefined

1.10String-Literale

1.11Template-Literale

1.12Objekte

1.13Objektliteral-Syntax

1.14Arrays

1.15JSON

1.16Destrukturierung

1.17Destrukturierung für Fortgeschrittene

1.17.1Mehr zum Thema Objektstrukturierung

1.17.2Restdeklarationen

1.17.3Standardwerte

1.18Übungen

2Steuerstrukturen

2.1Ausdrücke und Anweisungen

2.2Semikolonergänzung

2.3Verzweigungen

2.4Falsy- und Truthy-Werte

2.5Vergleichs- und Gleichheitsoperatoren

2.6Vergleiche unterschiedlicher Typen

2.7Boolesche Operatoren

2.8Die switch-Anweisung

2.9while- und do-Schleifen

2.10for-Schleifen

2.10.1Die klassische for-Schleife

2.10.2Die for-of-Schleife

2.10.3Die for-in-Schleife

2.11break und continue

2.12Ausnahmen abfangen

2.13Übungen

3Funktionen und funktionale Programmierung

3.1Funktionen deklarieren

3.2Funktionen höherer Ordnung

3.3Funktionsliterale

3.4Pfeilfunktionen

3.5Funktionale Array-Verarbeitung

3.6Closures

3.7Harte Objekte

3.8Strikter Modus

3.9Argumenttypen prüfen

3.10Mehr oder weniger Argumente bereitstellen

3.11Standardargumente

3.12Restparameter und der Verteilungsoperator

3.13Benannte Argumente durch Destrukturierung simulieren

3.14Hoisting

3.15Exceptions auslösen

3.16Exceptions abfangen

3.17Die finally-Klausel

3.18Übungen

4Objektorientierte Programmierung

4.1Methoden

4.2Prototypen

4.3Konstruktoren

4.4Die Klassensyntax

4.5Get- und Set-Methoden

4.6Instanzfelder und private Methoden

4.7Statische Methoden und Felder

4.8Teilklassen

4.9Methoden überschreiben

4.10Konstruktion von Teilklassen

4.11Klassenausdrücke

4.12Der Verweis this

4.13Übungen

5Zahlen und Datumsangaben

5.1Zahlenliterale

5.2Zahlenformatierung

5.3Parsen von Zahlen

5.4Funktionen und Konstanten der Klasse Number

5.5Funktionen und Konstanten der Klasse Math

5.6Große Integer

5.7Datumsangaben konstruieren

5.8Funktionen und Methoden der Klasse Date

5.9Datumsformatierung

5.10Übungen

6Strings und reguläre Ausdrücke

6.1Konvertierung zwischen Strings und Codepunktfolgen

6.2Teil-Strings

6.3Weitere String-Methoden

6.4Tagged-Template-Literale

6.5Rohe Template-Literale

6.6Reguläre Ausdrücke

6.7Literale für reguläre Ausdrücke

6.8Flags

6.9Reguläre Ausdrücke und Unicode

6.10Die Methoden der Klasse RegExp

6.11Gruppen

6.12String-Methoden für reguläre Ausdrücke

6.13Mehr über das Ersetzen mit regulären Ausdrücken

6.14Exotische Merkmale

6.15Übungen

7Arrays und Sammlungen

7.1Arrays konstruieren

7.2Die Eigenschaft length und die Indexeigenschaften

7.3Elemente löschen und hinzufügen

7.4Weitere Methoden zur Veränderung von Arrays

7.5Elemente erstellen

7.6Elemente finden

7.7Alle Elemente durchlaufen

7.8Dünn besetzte Arrays

7.9Reduzierung

7.10Maps

7.11Mengen

7.12Schwache Maps und Mengen

7.13Typisierte Arrays

7.14Array-Puffer

7.15Übungen

8Internationalisierung

8.1Gebietsschemata

8.2Ein Gebietsschema angeben

8.3Zahlenformatierung

8.4Datum und Uhrzeit

8.4.1Date-Objekte formatieren

8.4.2Datumsbereiche

8.4.3Relative Zeitangaben

8.4.4Zerlegung in Teilangaben

8.5Sortierung

8.6Weitere gebietsschemaabhängige String-Methoden

8.7Pluralregeln und Listen

8.8Verschiedene gebietsschemaabhängige Merkmale

8.9Übungen

9Asynchrone Programmierung

9.1Parallele Aufgaben in JavaScript

9.2Promises erstellen

9.3Unmittelbar erledigte Promises

9.4Ergebnisse von Promises abrufen

9.5Promises verketten

9.6Umgang mit abgelehnten Promises

9.7Mehrere Promises ausführen

9.8Wettlauf mehrerer Promises

9.9async-Funktionen

9.10Rückgabewerte von async-Funktionen

9.11Gleichzeitiges Warten

9.12Ausnahmen in async-Funktionen

9.13Übungen

10Module

10.1Das Prinzip von Modulen

10.2ECMAScript-Module

10.3Standardimporte

10.4Benannte Importe

10.5Dynamische Importe

10.6Exporte

10.6.1Benannte Exporte

10.6.2Der Standardexport

10.6.3Exporte sind Variable

10.6.4Reexport

10.7Module verpacken

10.8Übungen

11Metaprogrammierung

11.1Symbole

11.2Anpassung mithilfe von Symboleigenschaften

11.2.1Die Methode toString anpassen

11.2.2Die Typumwandlung steuern

11.2.3species

11.3Attribute von Eigenschaften

11.4Eigenschaften auflisten

11.5Das Vorhandensein einer einzelnen Eigenschaft prüfen

11.6Objekte schützen

11.7Objekte erstellen und ändern

11.8Auf den Prototyp zugreifen und ihn ändern

11.9Objekte klonen

11.10Funktionseigenschaften

11.11Argumente binden und Methoden aufrufen

11.12Proxys

11.13Die Klasse Reflect

11.14Proxy-Invarianten

11.15Übungen

12Iteratoren und Generatoren

12.1Iterierbare Werte

12.2Iterierbare Objekte implementieren

12.3Abschließbare Iteratoren

12.4Generatoren

12.5Verschachtelte yield-Anweisungen

12.6Generatoren als Verbraucher

12.7Generatoren in der asynchronen Verarbeitung

12.8async-Generatoren und -Iteratoren

12.9Übungen

13Einführung in TypeScript

13.1Typanmerkungen

13.2TypeScript ausführen

13.3Typterminologie

13.4Primitive Typen

13.5Zusammengesetzte Typen

13.6Typinferenz

13.7Untertypen

13.7.1Die Substitutionsregel

13.7.2Optionale und überzählige Eigenschaften

13.7.3Untertypbeziehungen von Array- und Objekttypen

13.8Klassen

13.8.1Klassen deklarieren

13.8.2Der Instanztyp einer Klasse

13.8.3Der statische Typ einer Klasse

13.9Strukturelle Typisierung

13.10Schnittstellen

13.11Indizierte Eigenschaften

13.12Komplexe Funktionsparameter

13.12.1Optionale, Standard- und Restparameter

13.12.2Parameter destrukturieren

13.12.3Untertypbeziehungen von Funktionstypen

13.12.4Überladung

13.13Generische Programmierung

13.13.1Generische Klassen und Typen

13.13.2Generische Funktionen

13.13.3Typeinschränkungen

13.13.4Löschung

13.13.5Untertypbeziehungen von generischen Typen

13.13.6Bedingte Typen

13.13.7Zugeordnete Typen

13.14Übungen

Stichwortverzeichnis

Für Chi, die geduldigste Person in meinem Leben

Vorwort

Erfahrene Programmierer, die mit Sprachen wie Java, C# oder C++ vertraut sind, finden sich oft in Situationen wieder, in denen sie mit JavaScript arbeiten müssen. Das liegt daran, dass es immer mehr webgestützte Benutzerschnittstellen gibt und JavaScript nun einmal die Lingua franca der Browser ist. Das Electron-Framework hat die Anwendung dieser Sprache auf Rich-Client-Anwendungen ausgedehnt, und es gibt verschiedene Möglichkeiten, um JavaScript-Apps für Mobilgeräte zu erstellen. Auch serverseitig wird JavaScript immer häufiger eingesetzt.

Vor vielen Jahren galt JavaScript als eine Sprache zur Programmierung im Kleinen. Ihre Features konnten für umfangreiche Programme ziemlich verwirrend und fehleranfällig sein. Mit den heutigen Standardisierungsbemühungen und dem Angebot an Werkzeugen hat die Sprache sich jedoch weit über diese bescheidenen Anfänge hinaus entwickelt.

Leider ist es schwierig, modernes JavaScript zu lernen, ohne mit veraltetem JavaScript überschüttet zu werden. In den meisten Büchern, Kursen und Blogposts geht es um den Übergang von älteren JavaScript-Versionen, was für Personen nicht hilfreich ist, die von anderen Sprachen kommen.

Das ist die Lücke, die ich mit diesem Buch füllen möchte. Ich gehe davon aus, dass Sie bereits ein kompetenter Programmierer sind und sich mit Verzweigungen und Schleifen, Funktionen, Datenstrukturen und den Grundlagen der objektorientierten Programmierung auskennen. Ich erkläre Ihnen, wie Sie in modernem JavaScript produktiv arbeiten können, und erwähne veraltete Merkmale nur am Rande. Sie lernen hier, wie Sie modernes JavaScript nutzen, ohne über die Fallstricke der Vergangenheit zu stolpern.

JavaScript ist nicht perfekt, aber es hat sich für die Programmierung von Benutzerschnittstellen und für viele serverseitige Aufgaben als gut geeignet erwiesen. Wie Jeff Atwood es einmal vorausschauend formulierte: »Jede Anwendung, die in JavaScript geschrieben werden kann, wird irgendwann auch in JavaScript geschrieben.«

Arbeiten Sie dieses Buch durch, um zu lernen, wie Sie die nächste Version Ihrer Anwendung in modernem JavaScript schreiben können.

Fünf goldene Regeln

Wenn Sie auf einige wenige klassische Features von JavaScript verzichten, können Sie das Maß an geistiger Anstrengung erheblich verringern, um die Sprache zu erlernen und anzuwenden. Die folgenden Regeln werden Ihnen jetzt zwar noch nicht viel sagen, aber ich führe Sie hier trotzdem zum späteren Nachschlagen auf – auch um Ihnen zu zeigen, wie beruhigend wenige es sind:

Deklarieren Sie Variablen mit

let

oder

const

, nicht mit

var

.

Verwenden Sie den strikten Modus.

Seien Sie sich immer über die Typen im Klaren und vermeiden Sie die automatische Typkonvertierung.

Machen Sie sich damit vertraut, wie Prototypen funktionieren, aber verwenden Sie für Klassen, Konstruktoren und Methoden die moderne Syntax.

Verwenden Sie

this

nicht außerhalb von Konstruktoren und Methoden.

Darüber hinaus gibt es noch eine Metaregel: Schauen Sie sich keinen Wat-Code an, also diese verwirrenden Ausschnitte aus JavaScript-Code, die mit einem sarkastischen (und orthografisch nicht korrekten) »Wat?!« kommentiert sind. Manche Leute haben Spaß daran zu zeigen, wie furchtbar JavaScript angeblich ist, indem sie obskuren Code analysieren. Aus solchen Übungen habe ich jedoch nie etwas Sinnvolles mitgenommen. Welchen Vorteil bietet es Ihnen etwa zu wissen, dass 2 * ['21'] den Wert 42 ergibt, 2 + ['40'] aber nicht, wenn Ihnen die goldene Regel Nr. 3 klipp und klar sagt, dass Sie nicht mit Typkonvertierungen herumdoktern sollten? Wenn ich in eine solche verwirrende Situation gerate, frage ich mich normalerweise, wie ich sie vermeiden kann, anstatt sie in allen unnützen Einzelheiten zu erklären.

Lernstoff von unterschiedlichem Niveau

Den Lernstoff in diesem Buch habe ich so angeordnet, dass Sie die benötigten Informationen leicht wiederfinden können, wenn Sie sie brauchen. Das ist allerdings nicht unbedingt die richtige Anordnung, wenn Sie das Buch zum ersten Mal lesen. Um Ihnen das Lernen zu erleichtern, habe ich jedes Kapitel mit einem Symbol für das Niveau des behandelten Stoffs versehen. Einzelne Abschnitte, die auf einem höheren Niveau angesiedelt sind, bekommen dabei ihre eigenen Symbole. Lassen Sie diese Abschnitte bei der ersten Lektüre aus und lesen Sie sie erst dann, wenn Sie dazu bereit sind.

Zur Kennzeichnung habe ich die folgenden Symbole verwendet:

Der ungeduldige Hase steht für ein grundlegendes Thema, das selbst die ungeduldigsten Leser nicht überspringen sollten.

Alice kennzeichnet ein Thema von mittlerem Niveau, mit dem sich die meisten Programmierer vertraut machen sollten, allerdings nicht unbedingt beim ersten Lesen.

Die Grinsekatze weist auf ein fortgeschrittenes Thema hin, das Framework-Entwicklern ein Lächeln entlocken mag. Die meisten Anwendungsentwickler können diese Abschnitte jedoch getrost ignorieren.

Der verrückte Hutmacher schließlich kennzeichnet komplizierte Themen, die einen in den Wahnsinn treiben können und sich nur für Leser mit krankhafter Neugier eignen.

Der Aufbau dieses Buches

In Kapitel 1 legen wir mit den Grundlagen von JavaScript los: mit Werten und ihren Typen, mit Variablen und vor allem mit Objektliteralen. Kapitel 2 behandelt den Steuerungsfluss. Wenn Sie mit Java, C# oder C++ vertraut sind, können Sie dieses Kapitel wahrscheinlich einfach überfliegen. In Kapitel 3 lernen Sie Funktionen und die funktionale Programmierung kennen, die in JavaScript eine große Rolle spielt. JavaScript hat zwar ein Objektmodell, allerdings unterscheidet es sich stark von dem klassengestützter Programmiersprachen. Kapitel 4 beschreibt dieses Objektmodell ausführlich, wobei der Schwerpunkt auf der modernen Syntax liegt. Die Kapitel 5 und 6 behandeln die Bibliotheksklassen, die Sie am häufigsten zur Arbeit mit Zahlen, Datumsangaben, Strings und regulären Ausdrücken verwenden werden. Diese ersten sechs Kapitel sind auf grundlegendem Niveau, wobei einige etwas anspruchsvollere Abschnitte eingestreut wurden.

Die folgenden vier Kapitel behandeln Themen von mittlerem Niveau. In Kapitel 7 erfahren Sie, wie Sie mit Arrays und anderen Sammlungstypen aus der JavaScript-Standardbibliothek arbeiten. Wenn Ihr Programm von Benutzern in aller Welt verwendet wird, sollten Sie der Internationalisierung besondere Aufmerksamkeit schenken, um die es in Kapitel 8 geht. Kapitel 9 über asynchrone Programmierung ist für alle Programmierer äußerst wichtig. Die asynchrone Programmierung war in JavaScript ziemlich kompliziert, ist mit der Einführung von Promises und der Schlüsselwörter async und await aber viel einfacher geworden. Außerdem hat JavaScript jetzt ein standardmäßiges Modulsystem, das Thema von Kapitel 10 ist. Darin erfahren Sie, wie Sie Module von anderen Programmierern nutzen und ihre eigenen schreiben können.

In Kapitel 11 geht es um Metaprogrammierung auf fortgeschrittenem Niveau. Sie sollten es lesen, wenn Sie Werkzeuge erstellen, um beliebige JavaScript-Objekte zu analysieren und umzuwandeln. Kapitel 12 schließt die Erörterung von JavaScript mit einem weiteren fortgeschrittenen Thema ab, nämlich Iteratoren und Generatoren: zwei äußerst nützliche Mechanismen, um beliebige Folgen von Werten zu durchlaufen und zu produzieren.

Schließlich gibt es noch ein Bonuskapitel, nämlich Kapitel 13 über TypeScript. Dabei handelt es sich um eine Erweiterung von JavaScript, die eine Typüberprüfung zur Kompilierzeit bietet. Es gehört nicht zum JavaScript-Standard, ist aber sehr populär. Lesen Sie dieses Kapitel, um selbst zu entscheiden, ob Sie beim einfachen JavaScript bleiben oder die Typisierung zur Kompilierzeit nutzen wollen.

Dieses Buch soll Ihnen solide Grundkenntnisse der Sprache JavaScript verleihen, sodass Sie sie souverän nutzen können. Informationen über die Werkzeuge und Frameworks, die einem ständigen Wandel unterliegen, müssen Sie dagegen an einem anderen Ort suchen.

Warum ich dieses Buch geschrieben habe

JavaScript ist eine der am häufigsten verwendeten Programmiersprachen der Welt. Wie viele Programmierer kannte ich zunächst ein bisschen Pidgin-JavaScript. Eines Tages aber musste ich ziemlich überstürzt echtes JavaScript lernen. Doch wie?

Es gab zwar eine Menge Bücher, mit denen Programmierer, die sich nur gelegentlich mit Webentwicklung befassen, ein bisschen JavaScript lernen konnten, aber so viel verstand ich von der Sprache ohnehin. Das Nashornbuch von Flanagan1 war 1996 zwar großartig, setzt die Leser von heute aber zu vielen Missgriffen der Vergangenheit aus. Das Beste an JavaScript von Douglas Crockford2 hat die JavaScript-Welt 2008 wachgerüttelt, aber ein Großteil seiner Botschaft ist bereits in nachfolgende Änderungen der Sprache eingeflossen. Außerdem gibt es viele Bücher, die JavaScript-Programmierern der alten Schule die Welt der modernen Standards nahebringen, aber sie beschäftigen sich mit zu vielen klassischen JavaScript-Elementen, die mir nicht behagen.

Das Web ist voll von Blogs zum Thema JavaScript, allerdings von sehr unterschiedlicher Qualität. Einige sind korrekt, aber viele zeigen nur ein ziemlich schwaches Verständnis. Für mich war es nicht sehr sinnvoll, das Web nach Blogs zu durchsuchen und jeweils zu prüfen, wie wahrheitsgetreu sie sind.

Merkwürdigerweise konnte ich kein Buch für die Millionen Programmierer finden, die Java oder eine ähnliche Sprache kennen und JavaScript in seiner heutigen Form ohne den historischen Ballast lernen wollen.

Also musste ich es selbst schreiben.

Danksagung

Ein weiteres Mal möchte ich meinem Herausgeber Greg Doench für die Unterstützung dieses Projekts danken sowie Dmitry Kirsanov und Alina Kirsanova für das Korrekturlesen und den Satz des Buches. Mein besonderer Dank geht an meine Lektoren Gail Anderson, Tom Austin, Scott Davis, Scott Good, Kito Mann, Bob Nicholson, Ron Mak und Henri Tremblay, die sorgfältig Fehler aufgespürt und wohlüberlegte Vorschläge für Verbesserungen gemacht haben.

Cay Horstmann

Berlin

März 2020

1

Werte und Variable

In diesem Kapitel lernen Sie die Datentypen kennen, mit denen Sie in JavaScript-Anwendungen arbeiten können: Zahlen, Strings und andere primitive Typen sowie Objekte und Arrays. Sie erfahren hier, wie Sie solche Werte in Variablen speichern, wie Sie Werte von einem Typ in einen anderen umwandeln und wie Sie sie mithilfe von Operatoren kombinieren.

Selbst begeisterte JavaScript-Programmierer geben zu, dass einige Konstrukte von JavaScript – die eigentlich dabei helfen sollen, Programme möglichst kurz und knapp zu schreiben – zu widersinnigen Ergebnissen führen können und daher am besten vermieden werden sollten. In diesem und den folgenden Kapiteln werde ich solche Probleme aufzeigen und einige einfache Regeln für sicheres Programmieren vorstellen.

1.1JavaScript ausführen

Es gibt verschiedene Möglichkeiten, um während der Lektüre dieses Buches JavaScript-Programme auszuführen. Da JavaScript ursprünglich zur Ausführung in einem Browser gedacht war, können Sie JavaScript-Code in eine HTML-Datei einbetten. Um Werte anzuzeigen, rufen Sie darin die Methode window.alert auf. Eine solche Datei sieht wie folgt aus:

Wenn Sie die Datei in einem Browser öffnen, wird das Ergebnis wie in Abbildung 1–1 in einem Dialogfeld angezeigt:

Abb. 1–1Ausführung von JavaScript-Code in einem Browser

Sie können auch kurze Folgen von Anweisungen in die Konsole eingeben, die zu den Entwicklerwerkzeugen des Browsers gehört. Suchen Sie den Menübefehl oder die Tastenkombination zur Anzeige dieser Werkzeuge. (In vielen Browsern ist das die Taste oder die Kombination + + bzw. + + auf dem Mac.) Bringen Sie dann die Registerkarte Konsole in den Vordergrund und geben Sie darin Ihren JavaScript-Code ein (siehe Abb. 1–2).

Abb. 1–2Ausführung von JavaScript-Code in der Entwicklerkonsole

Eine dritte Möglichkeit besteht darin, Node.js von http://node.js.org zu installieren, ein Terminalfenster zu öffnen und darin das Programm node auszuführen. Dadurch wird eine JavaScript-»REPL« gestartet (Read-Eval-Print Loop, also etwa »Lese-, Ausführungs- und Ausgabeschleife«). Darin können Sie Befehle eingeben und sich die Ergebnisse anzeigen lassen (siehe Abb. 1–3).

Abb. 1–3Ausführung von JavaScript-Code in der Node.js-REPL

Wenn Sie längere Codefolgen ausführen wollen, schreiben Sie die Anweisungen in eine Datei. Für Ausgaben verwenden Sie dabei die Methode console.log. Beispielsweise können Sie die folgenden Anweisungen in eine Datei aufnehmen, die Sie first.js nennen:

Führen Sie anschließend den folgenden Befehl aus:

node first.js

Im Terminal wird nun die Ausgabe des Befehls console.log angezeigt.

Sie können auch eine Entwicklungsumgebung wie Visual Studio Code, Eclipse, Komodo IDE oder WebStorm verwenden. Darin können Sie, wie in Abbildung 1–4 gezeigt, JavaScript-Code bearbeiten und ausführen:

Abb. 1–4Ausführung von JavaScript-Code in einer Entwicklungsumgebung

1.2Typen und der Operator typeof

Werte in JavaScript sind jeweils von einem der folgenden Typen:

eine Zahl

einer der booleschen Werte

false

oder

true

einer der besonderen Werte

null

oder

undefined

ein String

ein Symbol

ein Objekt

Die Typen, die keine Objekte sind, werden zusammengenommen als primitive Typen bezeichnet.

Mehr über diese Typen erfahren Sie in den folgenden Abschnitten. Die einzige Ausnahme bilden die Symbole, die erst in Kapitel 11 behandelt werden.

Den Typ eines gegebenen Werts können Sie mit dem Operator typeof herausfinden, der einen der Strings 'number', 'boolean', 'undefined', 'object', 'string' oder 'symbol' oder einen von wenigen weiteren möglichen Strings zurückgibt. Beispielsweise ergibt typeof 42 den String 'number'.

Hinweis

Obwohl der Typ null nicht identisch mit dem Typ object ist, ergibt typeof null den String 'object'. Das ist leider historisch so gewachsen.

Vorsicht

Ähnlich wie in Java können Sie in JavaScript Objekte als Wrapper für Zahlen, boolesche Werte und Strings konstruieren. Beispielsweise werden sowohl typeof new Number(42) als auch typeof new String('Hello') zu 'object' ausgewertet. Allerdings gibt es in JavaScript keinen sinnvollen Grund dafür, solche Wrapper-Instanzen zu erstellen. Da sie eher für Verwirrung sorgen, ist ihre Verwendung in vielen Programmierstandards untersagt.

1.3Kommentare

In JavaScript können Sie zwei verschiedene Arten von Kommentaren einfügen. Einzeilige Kommentare beginnen mit // und laufen bis zum Ende der Zeile:

// Einzeiliger Kommentar

Dagegen können mit /* und */ abgegrenzte Kommentare mehrere Zeilen umspannen:

/*

mehrzeiliger

Kommentar

*/

In diesem Buch verwende ich eine Serifenschrift, um die Kommentare in Listings deutlicher hervorzuheben. In Ihrem Texteditor werden die Kommentare wahrscheinlich farbig gekennzeichnet sein.

Hinweis

Anders als Java bietet JavaScript keine besondere Formatierung für Kommentare zur Dokumentation. Für diesen Zweck können Sie jedoch Drittanbieterwerkzeuge wie JSDoc (http://usejsdoc.org) nutzen.

1.4Variablendeklarationen

Mit der Anweisung let können Sie einen Wert in einer Variablen speichern:

In JavaScript haben Variable keinen festen Typ. Daher können Sie in jeder Variablen Werte beliebigen Typs speichern. Beispielsweise ist es zulässig, den Inhalt von counter durch einen String zu ersetzen:

Während es wohl kaum jemals sinnvoll sein dürfte, so etwas zu tun, erleichtern untypisierte Variablen es Ihnen in manchen Situationen, generischen Code zu schreiben, der mit unterschiedlichen Typen funktioniert.

Wenn Sie eine Variable nicht initialisieren, hat sie den besonderen Wert undefined:

let x // Deklariert die Variable x und setzt sie auf undefined

Hinweis

Vielleicht ist Ihnen aufgefallen, dass die vorstehenden Anweisungen nicht mit einem Semikolon abschließen. Ebenso wie in Python sind Semikolons am Zeilenende in JavaScript nicht erforderlich. Während in Python unnötige Semikolons als »unpythonisch« gelten, sind JavaScript-Programmierer darüber geteilter Ansicht. In Kapitel 2 werde ich die Argumente beider Seiten besprechen. Im Allgemeinen versuche ich mich aus solchen fruchtlosen Diskussionen herauszuhalten, aber in diesem Buch musste ich mich für eine Vorgehensweise entscheiden. Den semikolonfreien Stil habe ich aus einem ganz einfachen Grund gewählt: Code dieser Art lässt sich nicht mit Java oder C++ verwechseln. So lässt sich auf den ersten Blick erkennen, dass es sich um JavaScript handelt.

Wenn Sie den Wert einer Variablen niemals ändern, sollten Sie sie mit der Anweisung const deklarieren:

Sollten Sie versuchen, den Wert in einer solchen Konstante zu ändern, tritt ein Laufzeitfehler auf.

In einer einzigen let- oder const-Anweisung können Sie auch mehrere Variable deklarieren:

Viele Programmierer ziehen es jedoch vor, jede Variable in einer eigenen Anweisung zu deklarieren.

Tipp

Wenn Sie sich nach den im Vorwort aufgeführten fünf goldenen Regeln richten, können Sie die Verwirrung, die solche klassischen JavaScript-Merkmale verursachen, größtenteils vermeiden. Die ersten beiden dieser Regeln lauten:

Deklarieren Sie Variablen mit let oder const, nicht mit var.Verwenden Sie den strikten Modus.

1.5Bezeichner

Die Namen von Variablen müssen der allgemeinen Syntax für Bezeichner folgen. Bezeichner dürfen aus Unicode-Buchstaben, Ziffern sowie den Zeichen _ und $ bestehen. Ziffern dürfen nicht am Anfang stehen. In manchen Tools und Bibliotheken werden Namen mit $-Zeichen verwendet und manche Programmierer setzen einen Unterstrich an den Anfang oder das Ende von Bezeichnern, um private Merkmale zu kennzeichnen. Daher ist es am besten, wenn Sie bei den Namen, die Sie selbst festlegen, auf das Zeichen $ sowie auf _ am Anfang und Ende verzichten. Interne Unterstriche sind kein Problem, aber viele JavaScript-Programmierer bevorzugen die Camel-Case-Schreibweise mit Binnenmajuskel, um einzelne Namensbestandteile abzugrenzen.

Die folgenden Schlüsselwörter dürfen nicht als Bezeichner verwendet werden:

break case catch class const continue debugger default delete do

else enum export extends false finally for function if import in instanceof

new null return super switch this throw true try typeof var void while with

Im strikten Modus sind auch die folgenden Schlüsselwörter unzulässig:

implements interface let package protected private public static

Die folgenden Schlüsselwörter wurden erst kürzlich hinzugefügt. Sie können sie noch aus Gründen der Rückwärtskompatibilität als Bezeichner nutzen, sollten es aber lieber nicht tun:

await as async from get of set target yield

Hinweis

In Bezeichnern können Sie beliebige Unicode-Buchstaben und Ziffern verwenden, also beispielsweise auch Folgendes:

const

So etwas ist jedoch unüblich, da vielen Programmierern die Möglichkeiten zur schnellen Eingabe solcher Zeichen fehlen.

1.6Zahlen

JavaScript hat keinen expliziten Typ für Integer. Alle Zahlen sind Fließkommazahlen mit doppelter Genauigkeit. Natürlich können Sie ganzzahlige Werte verwenden; kümmern Sie sich einfach nicht um die Unterschiede zwischen 1 und 1.0. Aber wie sieht es mit Rundungen aus? Alle ganzen Zahlen zwischen Number.MIN_SAFE_INTEGER (–253 + 1 gleich –9.007.199.254.740.991) und Number.MAX_SAFE_INTEGER (+253 – 1 gleich 9.007.199.254.740.991) werden exakt dargestellt. Das ist ein größeres Intervall als das für Integer in Java. Solange die Ergebnisse innerhalb dieses Intervalls bleiben, sind arithmetische Operationen auf Integern exakt. Außerhalb dieses Bereichs dagegen kann es zu Rundungsfehlern kommen. Beispielsweise wird Number.MAX_SAFE_INTEGER * 10 zu 90071992547409900.

Hinweis

Wenn der Integer-Bereich nicht ausreicht, können Sie auch große Integer mit einer beliebigen Anzahl von Stellen verwenden. Mehr darüber erfahren Sie in Kapitel 5.

Beim Rechnen mit Fließkommazahlen kann es wie in allen Programmiersprachen zu Rundungsfehlern kommen. Beispielsweise wird 0.1 + 0.2 wie in Java, C++ und Python zu 0.30000000000000004 ausgewertet. So etwas ist unvermeidlich, da es keine exakte binäre Darstellung für Dezimalbrüche wie 0.1, 0.2 und 0.3 gibt. Wenn Sie mit Euro- und Centbeträgen rechnen müssen, sollten Sie daher alle Werte als ganzzahlige Vielfache eines Cent angeben. In Kapitel 5 werden Sie noch weitere Formen von numerischen Literalen kennenlernen, z. B. Hexadezimalzahlen.

Um einen String in eine Zahl umzuwandeln, können Sie die Funktionen parseFloat und parseInt verwenden:

Mit der Methode toString dagegen konvertieren Sie eine Zahl in einen String:

Hinweis

Anders als in Java, aber ebenso wie in C++ gibt es in JavaScript sowohl Funktionen als auch Methoden. Bei den Funktionen parseFloat und parseInt handelt es sich nicht um Methoden. Deshalb werden sie nicht mit der Punktschreibweise aufgerufen.

Hinweis

Wie der vorige Code zeigt, ist es möglich, Methoden auf numerische Literale anzuwenden. Dabei müssen Sie die Literale jedoch in Klammern einschließen, damit der Punkt nicht fälschlicherweise als Dezimaltrennzeichen aufgefasst wird.

Vorsicht

Was geschieht, wenn Sie in einem Fall, in dem ein Integer erwartet wird, einen Dezimalbruch verwenden? Das hängt von der jeweiligen Situation ab. Nehmen wir an, Sie wollen aus einem String einen Teil-String entnehmen. Dabei werden Dezimalbrüche als Positionsangaben abgeschnitten, sodass sich der nächstkleinere Integer ergibt:

'Hello'.substring(0, 2.5) // Der String 'He'

Geben Sie aber einen Dezimalbruch als Index an, lautet das Ergebnis undefined:

'Hello'[2.5] // undefined

Es lohnt nicht, sich damit zu beschäftigen, wann ein Dezimalbruch anstelle eines Integers verwendet werden kann und wann nicht. Machen Sie in solchen Situationen deutlich, was Sie beabsichtigen, indem Sie ausdrücklich Math.trunc(x) oder Math.round.(x) aufrufen, um die Nachkommastellen abzuschneiden bzw. die Zahl auf den nächsten Integer zu runden.

Bei einer Division durch null lautet das Ergebnis Infinity oder -Infinity. Allerdings wird 0 / 0 zu NaN ausgewertet, der Konstante »not a number«. Manche Funktionen, die Zahlen generieren, geben NaN zurück, um auf eine fehlerhafte Eingabe hinzuweisen. Beispielsweise wird parseFloat('pie') zu NaN ausgewertet.

1.7Arithmetische Operatoren

JavaScript verfügt über die üblichen Operatoren +, -, * und / für Addition, Subtraktion, Multiplikation und Division. Beachten Sie, dass der Operator / stets eine Fließkommazahl ergibt, selbst wenn beide Operanden Integer sind. Beispielsweise ergibt 1 / 2 die Zahl 0.5 und nicht 0, wie es in Java oder C++ der Fall wäre.

Ebenso wie in Java, C++ und Python ergibt der Operator % den Rest der Division zweier nichtnegativer Integer-Operanden. Wenn k ein nichtnegativer Integer ist, wird k % 2 für ein gerades k zu 0 ausgewertet und für ein ungerades k zu 1.

Sind k und n positiv (und möglicherweise nicht ganzzahlig), dann ist k % n der Wert, der sich ergibt, wenn fortgesetzt n von k subtrahiert wird, bis das Ergebnis kleiner als n ist. Beispielsweise ergibt 3.5 % 1.2 den Wert 1.1, das Ergebnis der zweimaligen Subtraktion von 1.2. Was bei negativen Operanden geschieht, erfahren Sie in Übung 3.

Der Operator ** steht ebenso wie in Python (und schon in Fortran) für eine Potenzierung. Der Wert von 2 ** 10 ist 1024, der von 2 ** -1 ist 0.5 und der von 2 ** 0.5 die Quadratwurzel von 2.

Ist einer der Operanden eines arithmetischen Operators der »Not-a-Number-Wert« NaN, so ist das Ergebnis ebenfalls NaN.

Wie in Java, C++ und Python können Sie Zuweisungen und arithmetische Operationen kombinieren:

Die Operatoren ++ und -- inkrementieren bzw. dekrementieren eine Variable:

Wie in Java wird der Operator + auch zur String-Verkettung verwendet. Wenn s ein String ist und x ein Wert eines beliebigen Typs, dann sind sowohl s + x als auch x + s Strings, die dadurch zustande kommen, dass x in einen String umgewandelt und mit s verkettet wird.

Betrachten Sie dazu das folgende Beispiel:

Vorsicht

Wie Sie gesehen haben, ist der Ausdruck x + y eine Zahl, wenn beide Operanden Zahlen sind, und ein String, wenn es sich bei mindestens einem Operanden um einen String handelt. In allen anderen Fällen sind die Regeln ziemlich kompliziert und die Ergebnisse nur selten sinnvoll. Entweder werden beide Operanden in Strings verwandelt und verkettet oder in Zahlen konvertiert und addiert. Beispielsweise wird der Ausdruck null + undefined zu der numerischen Addition 0 + NaN ausgewertet, die wiederum NaN ergibt (siehe Tabelle 1–1). Bei den anderen arithmetischen Operatoren wird nur eine Umwandlung in Zahlen versucht. So ergibt beispielweise 6 * '7' den Wert 42, da der String '7' in die Zahl 7 konvertiert wird.

Wert

Umwandlung in Zahl

Umwandlung in String

Eine Zahl

Die Zahl selbst

Ein String aus den Ziffern dieser Zahl

Ein String aus Ziffern, die eine Zahl bilden

Der Wert der Zahl

Der String selbst

Der leere String ''

0

''

Jeder andere String

NaN

Der String selbst

false

0

'false'

true

1

'true'

null

0

'null'

undefined

NaN

'undefined'

Das leere Array []

0

''

Ein Array, das eine einzige Zahl enthält

Die Zahl

Ein String aus den Ziffern der Zahl

Andere Arrays

NaN

Die Elemente, umgewandelt in Strings und durch Kommata getrennt, z. B. '1,2,3'.

Objekte

Standardmäßig NaN, kann aber angepasst werden

Standardmäßig '[object Object]', kann aber angepasst werden.

Tab. 1–1Umwandlung in Zahlen und Strings

Tipp

Verlassen Sie sich nicht auf die automatische Typumwandlung bei arithmetischen Operatoren. Die Regeln sind kompliziert und können zu unerwarteten Ergebnissen führen. Wenn Sie Strings oder einelementige Arrays als Operanden verarbeiten wollen, dann wandeln Sie sie explizit um.

Tipp

Verwenden Sie lieber Template-Literale (siehe Abschnitt 1.11, »Template-Literale«) als die String-Verkettung. Dadurch müssen Sie sich nicht merken, was der Operator + bei nichtnumerischen Operanden macht.

1.8Boolesche Werte

Der boolesche Typ kann die beiden Werte false und true annehmen. In einer Bedingung werden Werte beliebiger Typen in einen booleschen Wert umgewandelt. Dabei werden 0, NaN, null, undefined und der leere String zu false konvertiert und alle anderen zu true.

Das klingt zwar ganz einfach, aber wie Sie im folgenden Kapitel noch sehen werden, kann das zu verwirrenden Resultaten führen. Um Unklarheiten auf ein Minimum zu reduzieren, ist es sinnvoll, in Bedingungen grundsätzlich echte boolesche Werte zu verwenden.

1.9null und undefined

JavaScript kennzeichnet das Fehlen eines Wertes auf zwei verschiedene Arten. Wenn eine Variable deklariert, aber nicht initialisiert wird, ist ihr Wert undefined. Das geschieht häufig bei Funktionen: Wenn Sie eine Funktion aufrufen, aber keinen Parameter bereitstellen, hat die Parametervariable den Wert undefined. Der Wert null dagegen dient dazu, die beabsichtigte Abwesenheit eines Wertes zu kennzeichnen.

Ist diese Unterscheidung sinnvoll? Darüber gehen die Meinungen auseinander. Manche Programmierer meinen, dass die Verwendung zweier solcher Verlegenheitswerte fehleranfällig ist. Deshalb raten sie dazu, nur einen davon zu verwenden. Das sollte dann undefined sein, da es nicht möglich ist, diesen Wert in JavaScript zu vermeiden, wohingegen Sie auf null (fast) immer verzichten können.

Nach der gegenteiligen Ansicht sollten Sie weder Werte auf undefined setzen noch undefined von einer Funktion zurückgeben lassen, sondern für fehlende Werte stets null verwenden. Dadurch bleibt undefined als ein Signal reserviert, das auf ernste Probleme hindeutet.

Tipp

Einigen Sie sich bei jedem Projekt auf die eine oder die andere Vorgehensweise, also entweder undefined oder null zur Anzeige der beabsichtigten Abwesenheit eines Wertes zu verwenden. Dadurch ersparen Sie sich später endlose philosophische Diskussionen und unnötige Prüfungen auf undefined und null.

Vorsicht

Im Gegensatz zu null ist undefinedkein reserviertes Wort, sondern eine Variable im globalen Gültigkeitsbereich. Früher war es sogar möglich, der globalen Variablen undefined einen neuen Wert zuzuweisen! So etwas zu tun, ist natürlich eine furchtbare Idee, und heutzutage ist undefined eine Konstante. Allerdings können Sie immer noch lokale Variable mit dem Namen undefined deklarieren. Das ist allerdings nach wie vor eine schlechte Idee. Deklarieren Sie auch keine lokalen Variablen mit den Namen NaN und Infinity.

1.10String-Literale

String-Literale sind in einfache oder doppelte Anführungszeichen eingeschlossen, also z. B. 'Hallo' oder "Hallo". In diesem Buch verwende ich dazu immer einfache Anführungszeichen.

Wenn innerhalb eines Strings ein Anführungszeichen der gleichen Art steht, mit der der String begrenzt ist, dann müssen Sie es mit einem Backslash maskieren. Auch Backslashs selbst und die Steuerzeichen aus Tabelle 1–2 müssen Sie mit Backslashs maskieren.

Beispielsweise ist '\\\'\'\\\n' ein String von fünf Zeichen Länge, der die Zeichenfolge \''\ gefolgt von einem Zeilenumbruch enthält.

Um beliebige Unicode-Zeichen in einen JavaScript-String aufzunehmen, können Sie sie einfach eingeben oder kopieren, wobei die Quelldatei jedoch eine geeignete Kodierung verwenden muss (z. B. UTF-8):

Wenn Ihre Dateien unbedingt ASCII-Format haben müssen, können Sie stattdessen die Schreibweise \u{Codepunkt} verwenden:

Maskierungssequenz

Maskiertes Zeichen

Unicode-Wert

\b

Rückschritt

\u{0008}

\t

Tabulator

\u{0009}

\n

Zeilenvorschub

\u{000A}

\r

Wagenrücklauf

\u{000D}

\f

Seitenvorschub

\u{000C}

\v

Vertikaler Tabulator

\u{000B}

\'

Einfaches Anführungszeichen

\u{0027}

\"

Doppeltes Anführungszeichen

\u{0022}

\\

Backslash

\u{005C}

\Zeilenumbruch

Fortsetzung in der nächsten Zeile

Nichts – es erfolgt kein Zeilenumbruch:"Hel\lo"ergibt den String "Hello".

Tab. 1–2Maskierungssequenzen für Sonderzeichen

Leider gibt es bei der Verwendung von Unicode in JavaScript einen bösen Haken. Um die Feinheiten zu verstehen, müssen wir einen Blick auf die Geschichte von Unicode werfen. Vor der Erfindung von Unicode gab es verschiedene, nicht miteinander vereinbare Systeme zur Zeichenkodierung, wobei ein und dieselbe Bytefolge für Benutzer in Europa, Russland oder China ganz andere Bedeutungen haben konnte.

Unicode sollte diese Probleme lösen. Als man in den 80er Jahren mit dieser Vereinheitlichung begann, schien es so, als reiche ein 16-Bit-Code völlig aus, um sämtliche Zeichen in allen Sprachen der Welt zu kodieren und dabei noch Platz für zukünftige Erweiterungen zu lassen. 1991 wurde Unicode 1.0 veröffentlicht, worin knapp die Hälfte der verfügbaren 65.536 Codewerte belegt war. Als JavaScript und Java 1995 erschienen, nutzten sie die Unicode-Kodierung. In beiden Sprachen sind Strings Folgen von 16-Bit-Werten.

Im Laufe der Zeit aber geschah das Unvermeidliche: Der Umfang von Unicode überstieg den Vorrat von 65.536 Zeichen. Heutzutage nutzt Unicode 21 Bits, was nach gängiger Meinung nun wirklich ausreichen sollte. JavaScript aber ist bei den 16-Bit-Werten stecken geblieben.

Um zu erklären, wie dieses Problem gelöst wird, müssen wir uns ein bisschen mit dem technischen Hintergrund beschäftigen. Ein Unicode-Codepunkt ist ein 21-Bit-Wert, der mit einem Zeichen verknüpft wird. JavaScript nutzt die UTF-16-Kodierung, die alle Unicode-Codepunkte durch einen oder zwei 16-Bit-Werte oder Codeeinheiten darstellt. Für Zeichen bis \u{FFFF} wird jeweils eine Codeeinheit verwendet. Alle anderen Zeichen dagegen werden mit zwei Einheiten kodiert, die aus einem reservierten Bereich stammen und nicht zur Darstellung irgendwelcher anderer Zeichen genutzt werden. Beispielsweise wird \u{1F310} durch die Folge 0xD83C 0xDF10 kodiert. (Eine Beschreibung des Kodieralgorithmus finden Sie auf https://de.wikipedia.org/wiki/UTF-16.)

Mit den Einzelheiten der Kodierung müssen Sie sich nicht beschäftigen, aber wissen, dass einige Zeichen eine einzelne 16-Bit-Codeeinheit erfordern, andere dagegen zwei.

Beispielsweise hat der String 'Hello' eine Länge von 8, obwohl er nur sieben Unicode-Zeichen enthält (einschließlich des Leerzeichens zwischen Hello und ). Mit dem Operator [] können Sie auf die Codeeinheiten eines Strings zugreifen. Der Ausdruck greeting[0] ist ein String, der nur aus dem Zeichen 'H' besteht. Allerdings funktioniert dieser Operator nicht bei Zeichen, die aus zwei Codeeinheiten aufgebaut sind. Die Codeeinheiten für das Zeichen befinden sich an den Positionen 6 und 7. Die Ausdrücke greeting[6] und greeting[7] sind Strings der Länge 1 und enthalten jeweils eine einzige Codeeinheit, die aber kein Zeichen kodiert. Mit anderen Worten: Es handelt sich nicht um gültige Unicode-Strings.

Tipp

In Kapitel 2 werden Sie erfahren, wie Sie mit einer for-of-Schleife die einzelnen Codepunkte abrufen können.

Hinweis

Sie können in String-Literalen auch 16-Bit-Codeeinheiten angeben, müssen dabei aber die geschweiften Klammern weglassen: \uD83C\uDF10. Für Codeeinheiten bis \u{0xFF} können Sie die Hexmaskierung verwenden, also z. B. \xA0 statt \u{00A0]. Allerdings kann ich mir für beides keinen guten Grund vorstellen.

In Kapitel 6 lernen Sie die Methoden für die Arbeit mit Strings kennen.

Hinweis

In JavaScript gibt es auch Literale für reguläre Ausdrücke. Mehr darüber erfahren Sie in Kapitel 6.

1.11Template-Literale

Template-Literale sind Strings, die Ausdrücke enthalten und mehrere Zeilen umspannen können. Sie werden in Backticks (`...`) eingeschlossen:

Die in ${...} eingebetteten Ausdrücke werden ausgewertet, bei Bedarf in einen String umgewandelt und dann in das Template eingefügt. Im vorigen Beispiel ergibt sich dadurch der folgende String:

Hello, WORLD!

Sie können auch weitere Template-Literale in dem ${...}-Ausdruck verschachteln:

Alle Zeilenumbrüche innerhalb des Template-Literals werden in den String aufgenommen. Betrachten Sie dazu das folgende Beispiel:

Hier wird greeting auf den String '<div>Hello</div>\n<div>World</div>\n' mit Zeilenumbrüchen hinter jeder Zeile gesetzt. (Für den resultierenden String werden die Windows-Zeilenende-Zeichen \r\n in das Unix-Zeilenende-Zeichen \n umgewandelt.)

Um in Template-Literale Backticks, Dollarzeichen und Backslashs aufzunehmen, müssen Sie sie mit Backslashs maskieren. Die Zeichenfolge `\`\$\\` enthält die drei Zeichen `$\.

Hinweis

Als Tagged-Template-Literale werden Template-Literale bezeichnet, denen eine Funktion vorausgeht:

html`<div>Hello, ${destination}</div>`

Hier wird die Funktion html mit den Template-Fragmenten '<div>Hello, ' und '</div>' sowie dem Wert des Ausdrucks destination aufgerufen.

In Kapitel 6 erfahren Sie, wie Sie eigene Tag-Funktionen schreiben.

1.12Objekte

JavaScript-Objekte unterscheiden sich von denen in klassengestützten Sprachen wie Java und C++. Ein JavaScript-Objekt ist lediglich eine Menge von »Eigenschaften« genannten Name-Wert-Paaren wie dem folgenden:

{ name: 'Harry Smith', age: 42 }

Ein solches Objekt enthält nur öffentliche Daten und bietet keine Kapselung und kein Verhalten. Es ist auch keine Instanz einer bestimmten Klasse. Kurz gesagt, ist es etwas ganz anderes als ein Objekt im Sinne der objektorientierten Programmierung. Wie Sie in Kapitel 2 noch sehen werden, ist es zwar möglich, Klassen und Methoden zu definieren, doch unterscheiden sich die Mechanismen sehr stark von denen in den meisten anderen Sprachen.

Natürlich ist es möglich, ein Objekt in einer Variablen zu speichern:

Bei einer solchen Variablen können Sie mit der üblichen Punktschreibweise auf die Eigenschaften des Objekts zugreifen:

Dadurch können Sie vorhandene Eigenschaften bearbeiten und neue hinzufügen:

Um eine Eigenschaft zu entfernen, verwenden Sie den Operator delete:

delete harry.salary

Der Versuch, auf eine nicht vorhandene Eigenschaft zuzugreifen, resultiert in dem Wert undefined:

Ein Eigenschaftenname kann auch berechnet werden. Verwenden Sie in einem solchen Fall eckige Array-Klammern, um auf den Wert der Eigenschaft zuzugreifen:

1.13Objektliteral-Syntax

Dies ist der erste von mehreren Abschnitten mit mittlerem Niveau in diesem Kapitel. Wenn Sie gerade erst damit beginnen, JavaScript zu lernen, können Sie ihn getrost überspringen.

Am Ende eines Objektliterals kann auch ein Komma stehen. Dadurch wird es einfacher, bei der Erweiterung des Codes zusätzliche Eigenschaften hinzuzufügen:

Beim Deklarieren von Objektliteralen werden die Werte der Eigenschaften oft in Variablen mit demselben Namen gespeichert:

Es gibt dafür die folgende Kurzschreibweise:

Berechnete Eigenschaftennamen in Objektliteralen werden mit eckigen Klammern angegeben:

Die Namen von Eigenschaften sind immer Strings. Wenn ein Name die Regel für Bezeichner nicht erfüllt, müssen Sie ihn in einem Objektliteral in Anführungszeichen setzen:

Ein Zugriff auf eine solche Eigenschaft ist mit der Punktschreibweise nicht möglich. Verwenden Sie stattdessen eckige Klammern:

Solche Eigenschaftennamen sind eher unüblich, können manchmal aber durchaus praktisch sein. Stellen Sie sich beispielsweise ein Objekt vor, bei dem die Namen der Eigenschaften Dateinamen und die Werte die Inhalte dieser Dateien sind.

Vorsicht

Wenn es beim Parsen unklar ist, ob eine öffnende geschweifte Klammer ein Objektliteral oder eine Blockanweisung einleitet, wird davon ausgegangen, dass eine Blockanweisung vorliegt. Geben Sie als Beispiel Folgendes in die Browserkonsole oder Node.js ein:

{} - 1

In diesem Fall wird der leere Block ausgeführt und anschließend der Ausdruck -1 ausgewertet und angezeigt.

Bei dem folgenden Ausdruck geschieht jedoch etwas anderes:

1 - {}

Hier wird {} als ein leeres Objekt aufgefasst und in NaN umgewandelt. Anschließend wird das Ergebnis (ebenfalls NaN) angezeigt.

In der Praxis treten solche Mehrdeutigkeiten jedoch nicht auf. Wenn Sie ein Objektliteral erstellen, speichern Sie es gewöhnlich in einer Variablen, übergeben es als Argument oder geben es als Ergebnis zurück. In all diesen Fällen erwartet der Parser keinen Block.

Sollte es Ihnen jemals passieren, dass ein Objektliteral fälschlicherweise als Block geparst wird, können Sie ganz einfach Abhilfe schaffen: Schließen Sie das Objektliteral in runde Klammern ein. Ein Beispiel dafür finden Sie in Abschnitt 1.16, »Destrukturierung«.

1.14Arrays

In JavaScript ist ein Array einfach ein Objekt, dessen Eigenschaften die Namen '0', '1', '2' usw. haben. (Es sind Strings, da Zahlen nicht als Eigenschaftennamen verwendet werden können.)

Um Array-Literale zu deklarieren, schließen Sie ihre Elemente in eckige Klammern ein:

Dieses Objekt hat die fünf Eigenschaften '0', '1', '2', '3' und 'length'.

Die Eigenschaft length ist eins höher als der höchste Index, umgewandelt in eine Zahl. Der Wert von numbers.length ist also die Zahl 4.

Um auf die ersten vier Eigenschaften zuzugreifen, müssen Sie eckige Klammern verwenden: numbers['1] ist 2. Das Argument in den eckigen Klammern wird automatisch in einen String umgewandelt, sodass wir auch numbers[1] schreiben können, um uns der Illusion hinzugeben, mit einem Array in einer Sprache wie Java oder C++ zu arbeiten.

Die Elemente in einem Array müssen nicht vom selben Typ sein. Beispielsweise enthält das Array numbers drei Zahlen und einen String.

In einem Array dürfen auch einzelne Elemente fehlen:

Wie bei anderen Objekten haben auch bei Arrays die nicht vorhandenen Eigenschaften wie hier someNumbers[0] und someNumbers[2] den Wert undefined.

Sie können neue Elemente hinten anhängen:

Wie bei allen Objekten können Sie auch bei einem Array, auf das eine const-Variable verweist, die Eigenschaften ändern.

Da Arrays Objekte sind, können Sie ihnen auch beliebig neue Eigenschaften hinzufügen:

Das ist zwar nicht üblich, aber gültiges JavaScript.

Bei einem Array gibt der Operator typeof den Wert 'object' zurück. Um zu prüfen, ob es sich bei diesem Objekt um ein Array handelt, müssen Sie Array.isArray(obj) aufrufen.

Wenn Sie ein Array in einen String umwandeln, werden alle Elemente zu Strings gemacht und mittels Kommata verkettet. Betrachten Sie den folgenden Ausdruck:

'' + [1, 2, 3]

Dies ergibt den String '1,2,3'.

Aus einem Array der Länge 0 wird ein leerer String.

Ebenso wie in Java gibt es in JavaScript keine Vorkehrungen für mehrdimensionale Arrays, allerdings können Sie sie mit Arrays aus Arrays simulieren:

Anschließend können Sie mit jeweils zwei Paaren eckiger Klammern auf einzelne Elemente zugreifen:

melancholyMagicSquare[1][2] // 11

In Kapitel 2 erfahren Sie, wie man auf alle Elemente eines Arrays zugreifen kann. Eine ausführliche Beschreibung aller Array-Methoden folgt in Kapitel 7.

1.15JSON

JSON (JavaScript Object Notation) ist ein schlankes Textformat für den Austausch von Objektdaten zwischen Anwendungen (unabhängig davon, ob diese Anwendungen in JavaScript geschrieben sind oder nicht). Kurz gesagt, nutzt JSON die JavaScript-Syntax für Objekt- und Array-Literale, allerdings mit den folgenden Einschränkungen:

Zulässige Werte sind Objektliterale, Array-Literale, Strings, Fließkommazahlen sowie

true

,

false

und

null

.

Alle Strings stehen in doppelten Anführungszeichen, nicht in einfachen.

Alle Eigenschaftennamen stehen in doppelten Anführungszeichen.

Es gibt keine nachfolgenden Kommata und es dürfen keine Elemente fehlen.

Eine formale Beschreibung der Notation finden Sie auf www.json.org.

Das folgende Beispiel zeigt einen JSON-String:

{ "name": "Harry Smith", "age": 42, "lucky numbers": [17, 29], "lucky": false }

Die Methode JSON.stringify wandelt ein JavaScript-Objekt in einen JSON-String um. Umgekehrt analysiert JSON.parse einen JSON-String und gibt ein JavaScript-Objekt zurück. Diese beiden Methoden werden gewöhnlich bei der Kommunikation mit einem Server über HTTP eingesetzt.

Vorsicht

JSON.stringify verwirft Objekteigenschaften mit dem Wert undefined und wandelt Array-Elemente mit dem Wert undefined in null um. So ergibt beispielsweise JSON.stringify({ name: ['Harry', undefined, 'Smith'], age: undefined }) den String '{"name":["Harry",null,"Smith"]}'.

Manche Programmierer verwenden die Methode JSON.stringify zur Protokollierung. Betrachten Sie den folgenden Protokollierungsbefehl:

console.log(`harry=${harry}`)

Dadurch erhalten Sie nur die folgende nutzlose Meldung:

harry=[object Object]

Ein Aufruf von JSON.stringify kann hier Abhilfe schaffen:

console.log(`harry=${JSON.stringify(harry)}`)

Dieses Problem tritt jedoch nur bei Strings auf, die Objekte enthalten. Wenn Sie ein Objekt für sich selbst protokollieren, zeigt die Konsole es korrekt an. Eine einfache Alternative besteht darin, die Namen und Werte separat zu protokollieren:

console.log('harry=', harry, 'sally=', sally)

Noch einfacher ist es, sie in ein Objekt zu packen:

console.log({harry, sally}) // Protokolliert das Objekt { harry: { . . . }, sally: { . . . } }

1.16Destrukturierung

Die Destrukturierung bietet eine komfortable Syntax, um die Elemente eines Arrays oder die Werte eines Objekts abzurufen. Wie die anderen Themen auf mittlerem Niveau in diesem Kapitel können Sie auch dieses zunächst überspringen. In diesem Abschnitt geht es um die grundlegende Syntax. Mit den Einzelheiten beschäftigen wir uns im Anschluss.

Wenden wir uns als Erstes den Arrays zu. Angenommen, wir haben das Array pair mit zwei Elementen. Dabei können wir wie folgt auf die einzelnen Elemente zugreifen:

Durch Destrukturierung wird daraus Folgendes:

Diese Anweisung deklariert die Variablen first und second und initialisiert sie mit pair[0] und pair[1].

Die linke Seite der Destrukturierungszuweisung ist in Wirklichkeit gar kein Array-Literal, denn schließlich gibt es first und second noch gar nicht. Sie können sich diese linke Seite als Muster vorstellen, das angibt, wie die Variablen der rechten Seite zugewiesen werden sollen.

Betrachten Sie nun den folgenden komplizierteren Fall. Schauen Sie sich dabei an, wie die Variablen den Array-Elementen zugeordnet werden:

Das Array auf der rechten Seite kann länger sein als das Muster auf der linken. Die Elemente, für die es keine Entsprechung gibt, werden einfach ignoriert:

Ist das Array dagegen kürzer, werden die Variablen ohne Entsprechung auf undefined gesetzt:

Wenn die Variablen first und second bereits deklariert sind, können Sie sie mithilfe der Destrukturierung auf neue Werte setzen:

Wenn Sie die Destrukturierung für eine Zuweisung verwenden, muss die linke Seite nicht unbedingt aus Variablen bestehen. Stattdessen können Sie auch L-Werte verwenden, also Ausdrücke, die nur auf der linken Seite einer Anweisung stehen können. Die Destrukturierung im folgenden Beispiel ist daher gültig:

Die Destrukturierung für Objekte erfolgt auf ähnliche Weise, allerdings verwenden Sie dabei Eigenschaftennamen statt Array-Positionen:

Dieser Code deklariert die beiden Variablen harrysName und harrysAge und initialisiert sie mit den Werten der Eigenschaften name und age des Objekts auf der rechten Seite. Denken Sie daran, dass die linke Seite kein Objektliteral ist, sondern ein Muster, das angibt, wie die Variablen der rechten Seite zugeordnet werden.

Die Destrukturierung für Objekte ist besonders dann praktisch, wenn die Eigenschaft denselben Namen hat wie die Variable. In einem solchen Fall können Sie den Eigenschaftennamen und den Doppelpunkt weglassen. Die folgende Anweisung deklariert die beiden Variablen name und age und initialisiert sie mit den gleichnamigen Eigenschaften des Objekts auf der rechten Seite:

Das ist gleichbedeutend mit der folgenden Anweisung:

Und natürlich auch mit dieser Schreibweise:

1.17Destrukturierung für Fortgeschrittene

Im vorangegangenen Abschnitt habe ich mich auf die grundlegenden Aspekte der Destrukturierungssyntax beschränkt. In diesem Abschnitt für Fortgeschrittene sehen wir uns zusätzliche Merkmale an, die zwar sehr praktisch, aber nicht so unmittelbar verständlich sind. Sie können diesen Abschnitt auch gern überspringen und später darauf zurückkommen, wenn Sie die Grundlagen gemeistert haben.

1.17.1Mehr zum Thema Objektstrukturierung

Es ist auch möglich, verschachtelte Objekte zu destrukturieren:

Denken Sie auch hier wieder daran, dass die linke Seite der zweiten Anweisung kein Objekt ist, sondern ein Muster für die Zuordnung der Variablen zur rechten Seite. Diese Anweisung ist gleichbedeutend mit:

Ebenso wie bei Objektliteralen können auch hier berechnete Eigenschaftennamen verwendet werden:

1.17.2Restdeklarationen

Bei der Destrukturierung eines Arrays können Sie alle übrig gebliebenen Elemente in einem eigenen Array erfassen. Stellen Sie dem betreffenden Variablennamen eine Ellipse (...) voran.

Wenn das Array auf der rechten Seite über zu wenige Elemente verfügt, ist die Restvariable ein leeres Array:

Restdeklarationen sind auch bei Objekten möglich:

Die Variable allButName wird auf ein Objekt gesetzt, das alle Eigenschaften außer derjenigen mit dem Schlüssel name enthält.

1.17.3Standardwerte

Für jede Variable können Sie einen Standardwert festlegen, der verwendet wird, wenn der entsprechende Wert in dem Objekt oder Array nicht vorhanden oder undefined ist. Geben Sie dazu hinter dem Variablennamen mit einem Gleichheitszeichen den gewünschten Ausdruck an:

Ausdrücke für Standardwerte können auch die zuvor festgelegten Variablen enthalten: