JavaScript/Tutorials/Einstieg/Programmieren und Debuggen

Aus SELFHTML-Wiki
< JavaScript‎ | Tutorials‎ | Einstieg(Weitergeleitet von Debuggen)
Wechseln zu: Navigation, Suche

Programmieren beinhaltet immer auch die Suche nach Fehlern. In JavaScript erzeugen Fehler oft keinen Abbruch oder eine Fehlermeldung und sorgen erst viel später für Probleme, deren Ursache dann durch Debuggen herausgefunden werden muss.

In diesem Kapitel lernen Sie, wie Sie Scripte auf Programmierfehler überprüfen und debuggen können. Als wichtiges Entwicklungswerkzeug, das die JavaScript-Programmierung entscheidend erleichtert, verwenden wir die eingebaute Konsole.

Damit können Sie ...

Kontrollausgabe mit der Konsole

Screenshot
Ansicht der Entwicklertools in Microsoft Edge 112

Die Konsole öffnet sich …

  1. über die Funktionstaste F12
  2. über die jeweiligen Menüs,
  3. oder aber durch eine der folgenden Tastenkombinationen:
    • Mozilla Firefox: unter Windows und Linux mit Strg + Umschalt + I, unter Mac mit Cmd + Opt + I
    • Google Chrome: unter Windows und Linux mit Strg + Umschalt + I, unter Mac mit Cmd + Opt + I
    • Microsoft Edge: Strg + Umschalt + I oder Strg + Umschalt + J

In dem Screenshot sehen Sie, wie sich das Beispiel der vorherigen Seite (noch ohne Verwendung von Template-Literalen) zusammen mit einem HTML Rahmen auf der "Quelltext"-Seite der Entwicklerwerkzeuge darstellt. In der Zeile 11 befindet sich ein roter Punkt, und wenn Sie die rechte Seite des Screenshots anschauen, finden Sie diese Programmzeile unter "Haltepunkte" aufgeführt. Wenn die Programmausführung diese Stelle erreicht, wird der Browser das Script anhalten - nicht abbrechen! - und wir können Variableninhalte inspizieren oder ändern.

Künftig wollen wir mit der Konsole …

  • Ergebnisse ausgeben,
  • Variablen untersuchen
  • und den JavaScript-Code in der Konsole selbst ändern und erneut ausführen.
Hinweis:
Die im Frickl verwendete Technik erschwert je nach Browser das Debuggen von JavaScript-Code. Sie können zwar die Ausgabe mit console.log nutzen; die Ansicht des Codes ist aber nur umständlich möglich. Sie können Sich behelfen, in dem Sie das gewünschte Beispiel-Script in Ihren Editor kopieren und es auf Ihrem Computer als HTML-Dokument speichern. Sie können dann ihre lokale Kopie im Browser öffnen und debuggen.

Kontrollstrukturen

Bevor wir uns das Programm mit den Entwicklerwerkzeugen anschauen, soll es noch etwas erweitert werden.

Ein Programm ist nicht nur eine geradlinige Kette von aufeinander folgenden Anweisungen, sondern soll auf Eingaben unterschiedlich reagieren. Dafür kann man bedingte Anweisungen verwenden, die bei Erfüllen einer Bedingung einen Anweisungsblock ausführen, bei Nichterfüllen nichts oder etwas anderes tun.

Bedingte Anweisungen (Verzweigungen)

Das Schlimmste, was einem angehenden Software-Ingenieur passieren kann, ist, dass der kleine Bruder versucht, einen Blick auf sein Programm zu werfen. Deshalb bauen wir als nächsten Schritt nun eine Altersabfrage ein. In unserem Quellcode wollen wir uns auch mehr einer HTML Seite nähern, und darum zeigen wir nun auch einen Teil des Grundgerüsts, das Sie im HTML Tutorial kennengelernt haben.

Wenn-dann-Bedingung mit if

4. Eingabe von Namen und Alter ansehen …
 1 <!doctype html>
 2 <html>
 3 <head>
 4 <meta charset="utf-8">
 5 <title>4. Eingabe von Namen und Alter</title>
 6 </head>
 7 <body>
 8 <script>
 9    'use strict';
10    const erwachsen = 18;
11    let name  = '',
12        alter = '';
13    name = prompt('Bitte geben Sie Ihren Namen ein!', name);
14    alter = prompt('Bitte geben Sie Ihr Alter ein!', alter);
15 
16    if (alter < erwachsen) {
17       console.log('Du kommst hier net rein!');
18    }
19    else {
20       let ausgabe = `Hallo ${name}!`;
21       console.log(ausgabe);
22    }
23 </script>
24 </body>
25 </html>

Das vorherige Beispiel wurde so ausgebaut, dass eine weitere Variable alter deklariert und ebenfalls abgefragt wird. Dazu kommt eine Konstante erwachsen mit dem Wert 18. Eine Konstante ist eine Variable, deren Wert während des Programmablaufs nicht verändert werden kann.

In Zeile 16 wird mit einer if-Anweisung überprüft, ob der in runden Klammern stehende Ausdruck (alter < erwachsen) richtig ist. Der Operator < vergleicht die Werte auf seiner linken und rechten Seite und liefert den logischen Wert true, wenn der linke Wert kleiner ist. Die if-Anweisung prüft, ob sie true erhält (oder einen Wert, den sie als true interpretieren kann).

Ist das der Fall, wird der direkt dahinter in geschweiften Klammern stehende Anweisungsblock ausgeführt. In vielen Programmiersprachen wird dem Anweisungsblock für true noch das Schlüsselwort then vorangestellt (in JavaScript nicht!), deshalb spricht man bei diesem Block auch vom then-Zweig der Abfrage. Falls die Bedingung nicht zutrifft, kann mit Hilfe des Schlüsselworts else (sonst) ein Anweisungsblock vorgegeben werden, der statt dessen ausgeführt werden soll. Diesen nennt man dementsprechend den else-Zweig.

Unser Beispiel gibt im then-Zweig die Meldung aus, dass eine unbefugte Nutzung vorliegt. Gelangt das Programm in den else-Zweig, gilt der Anwender als berechtigt und wird mit seinem Namen begrüßt. Diese Ausgabe findet jetzt nicht mehr mit alert() statt, sondern mit console.log().

Beachten Sie: Es ist nicht zwingend nötig, den ersten Anweisungsblock in geschweifte Klammern zu setzen, da er nur aus einer Zeile besteht. Aus Gründen der Übersichtlichkeit (und falls Sie später den Code erweitern) ist dies jedoch zu empfehlen.
Hinweis:
  1. Klicken Sie mit der rechten Maustaste auf "Vorschau" und wählen Sie "In neuem Fenster öffnen" aus.
  2. Statt dessen können Sie das Beispiel auch in Ihren Editor kopieren, als HTML Datei speichern und mit Ihrem Browser öffnen.
  3. Öffnen Sie die Konsole mit F12!

Je nach Einstellungen der Entwicklerwerkzeuge sehen Sie unten ein Konsolenfenster eingeblendet. Sie können das mit der Esc-Taste ein- und ausblenden. Ohne die Zusatzkonsole unten können Sie die Programmausgabe mit einem Klick auf Konsole am oberen Fensterrand überprüfen.

Screenshot der Konsole des Edge 79

Nach der Eingabe der gewünschten Parameter erscheint nun die Begrüßung in der Konsole.

Der > Winkel ist eine Kommandozeile, in der Sie mit console.log z. B. den Wert der Variablen name ausgeben können. Der Screenshot wurde mit Microsoft Edge gemacht. Im Firefox sieht es ähnlich aus. Hier wird die Kommandozeile durch einen Doppelwinkel angezeigt.

Screenshot der Konsole mit einigen Testausgaben

Beachten Sie: Selbstdefinierte Namen in JavaScript unterscheiden Groß- und Kleinschrift. Die Variable Name ist nicht mit name identisch, darum wird ein Referenzfehler „geworfen“.

Beachten Sie die Ausgabe der Variablen alter. Hier wurde auf console.log() verzichtet. Das funktioniert deshalb, weil die Konsole jeden JavaScript-Ausdruck, den man dort eintippt, auswertet und das Ergebnis anzeigt.

Auf der Konsole können Sie deshalb nicht nur Variablen und Konstanten, sondern auch komplexere Ausdrücke wie die Bedingung der If-Abfrage überprüfen. Das if-Schlüsselwort müssen Sie aber weglassen, da es kein Teil der Bedingung ist. Innerhalb von console.log() erhalten Sie einen Syntaxfehler, wenn Sie es trotzdem tun. Und natürlich können Sie auch einfach alter < erwachsen abfragen und erhalten false.

In der letzten Zeile ist etwas merkwürdiges geschehen. Nach Eingabe von if (alter < erwachsen) wurde die Eingabetaste betätigt, und es geschah - nichts? Nur der Cursor blinkt auf der nächsten Zeile. Der Erklärung dafür ist: Sie können im Konsolenfenster auch umfangreichere Anweisungen eingeben, und für if fehlt noch der Anweisungsblock für den then-Zweig. Deswegen tut die Konsole noch nichts und wartet auf die Vervollständigung.

Wenn kleiner nicht kleiner ist

Rufen Sie doch dieses Beispiel bitte noch einmal auf, und geben Sie als Alter 9 ein. Was geschieht? Und warum? Denken Sie kurz einmal darüber nach - und achten Sie genau darauf, was im vorigen Beispiel ausgegeben wurde, als alter angezeigt wurde.

Hier für die Auflösung klicken!Das Alter wird in Anführungszeichen ausgegeben, es ist also eine Zeichenkette. In der Konstanten erwachsen steht die Zahl 18. Für JavaScript sind Zahlen und Zeichenketten unterschiedliche Arten von Daten und es kann Zeichenketten und Zahlen nicht direkt vergleichen. Deshalb wandelt es einen der beiden Werte in die allgemeinere Form um - und das ist die Zeichenkette. JavaScript vergleicht beim Alter 9 also '9' mit '18' - und weil Zeichenketten Zeichen für Zeichen von links nach rechts verglichen werden, stellt er beim ersten Zeichen fest, dass '9' nicht kleiner ist als '1' und beendet den Vergleich mit dem Ergebnis false.

Was tun? Wir müssen dafür sorgen, dass der Vergleich numerisch durchgeführt wird. Dafür müssen wir die Benutzereingabe in eine Zahl umwandeln. JavaScript bietet uns dafür die Funktion parseInt() an. Sie übergeben dieser Funktion eine Zeichenkette, und erhalten als Ergebnis eine Interpretation dieser Zeichenkette als Zahl. Das gelingt gut, wenn die übergebene Zeichenkette nur Ziffern enthält. Findet parseInt() allerdings ein Zeichen, das in einer Zahl nicht gültig ist (zum Beispiel einen Buchstaben), dann bricht es ab und gibt die Zahl zurück, die bis dahin erkannt wurde. parseInt('12a') würde also 12 ergeben.


Aufgabe: Ändern Sie in Ihrer eigenen HTML Datei die Bedingung der if-Anweisung so ab, dass an Stelle von alter das Ergebnis von parseInt(alter) mit erwachsen verglichen wird.

4. Eingabe von Namen und Alter - mit Zahlenkonvertierung ansehen …
   alter = prompt('Bitte geben Sie Ihr Alter ein!', alter);

   if ( ? ? ? ? ) {
      console.log('Du kommst hier net rein!');
   }
   else ...

Was muss an Stelle der Fragezeichen stehen? Klicken Sie auf "Vorschau" und untersuchen Sie mit den Entwicklerwerkzeugen den Quelltext, um die Lösung zu finden!


War es das jetzt? Testen Sie das reparierte Programm noch einmal, und geben Sie dreißig als Alter ein. Sie werden begrüßt - aber warum? Was geschieht?

Wechseln Sie in die Konsole und geben Sie dort parseInt('dreißig') ein. Können Sie mit dem Wiki herausfinden, was es mit dem Ergebnis auf sich hat?

Hier klicken, um zu erfahren, wie man das löst!Weil in 'dreißig' keine einzige gültige Ziffer gefunden wird, liefert parseInt('dreißig') den speziellen Wert NaN (not a number). Wie im Artikel zu NaN beschrieben, sind Vergleiche mit diesem Wert immer false, und deshalb läuft die Abfrage in den else-Zweig.

Wir haben hier also keinen Syntaxfehler in unserem Programm, der es abbrechen lässt, sondern eine Lücke in unserer Logik, die durch zusätzliche Prüfungen geschlossen werden muss. Dazu kann man die globale Funktion isNaN verwenden, die zum einen den Wert NaN erkennt und zum anderen strenger dabei ist, ob eine Zeichenkette eine Zahl ist. Während parseInt() versucht, einen Zahlenanteil in seiner Eingabe zurückzumelden, verwendet isNaN die Number-Funktion dafür, die den Wert NaN zurückgibt, wenn die Zeichenkette nicht vollständig als Zahl interpretiert werden kann. Sie können also als erstes mit isNaN(alter) prüfen, ob eine gültige Eingabe vorlag und in diesem Fall den Zugang abweisen.

5. Altersabfrage mit Gültigkeitsüberprüfung ansehen …
   const ablehnungsText = 'Du kommst hier net rein!';

   alter = prompt('Bitte geben Sie Ihr Alter ein!', alter);

   if (isNaN(alter)) {
      console.log(ablehnungsText);
   }
   else if (parseInt(alter) < erwachsen) {
      console.log(ablehnungsText);
   }
   else ...

Um den Ablehnungstext nicht zweimal im Programm zu haben, wurde eine zusätzliche Konstante deklariert, in der der Text abgelegt wird.


Test: Variablen vergleichen

Oft sind es die kleinen Fehler, die man übersieht, auch wenn man sich den Code dreimal anschaut.

In diesem Script sollen die Variablen a und b verglichen werden. Anscheinend ist 23 wohl auch 42 !?![1]

Variablen vergleichen ansehen …
let a = 23;
let b = 42;
if (a = b) {
  alert('23 ist 42, q.e.d.');
}

Zumindest könnte man mit einem console.log(a) vor der if-Abfrage bemerken, dass der Inhalt von a noch stimmt, innerhalb dann aber den Inhalt von b angenommen hat, was nicht beabsichtigt war. Und so muss der Fehler zwischen den beiden console.log() sitzen.

Mit a = b wird der Wert der Variable b der Variablen a zugewiesen. Das ist hier aber nicht gewünscht. Um die Variablen a und b zu vergleichen, muss der Vergleichsoperator a == b verwendet werden.


Variablen vergleichen ansehen …
let a = 23;
let b = 42;
console.log(a, b);
if (a == b) {
  console.log(a, b);
  alert('23 ist 42, q.e.d.');
} else {
  alert('23 ist gar nicht 42!');
}

Verzweigung mit switch

Mit if und else können Sie genau zwei Fälle unterscheiden. Wenn Sie feiner differenzieren, also zwischen mehreren Fällen unterscheiden wollen, können Sie zwar mehrere if-Abfragen hintereinander notieren, aber es gibt noch eine elegantere Möglichkeit: die Fallunterscheidung mit switch:

6. Altersabfrage mit Gültigkeitsüberprüfung ansehen …
 1 'use strict';
 2 let name  = '',
 3     alter = '',
 4     eingabe = '',
 5     text = '';
 6 const erwachsen = 18;
 7 
 8 name = prompt('Bitte geben Sie Ihren Namen ein!', name);
 9 alter = prompt('Bitte geben Sie Ihr Alter ein!', alter);
10 
11 if (isNaN(alter)) {
12     eingabe = 'dumm';
13 } 
14 else if (alter < erwachsen) {
15     eingabe = 'jung';
16 } else {
17     eingabe = 'ok';
18 }
19 
20 switch (eingabe) {
21     case 'jung':
22         text ='Du bist leider zu jung!';
23         break;
24     case 'dumm':
25         text ='Sie sind leider zu dumm, eine Zahl einzugeben!';
26         break;
27     default:
28         text = `Hallo ${name}!`;
29         break;
30 }
31   
32 console.log(text);

In diesem Beispiel wird die eingegebene Variable alter überprüft und anhand dessen einer neuen Variable eingabe ein Wert zugewiesen.
In einer switch-Abfrage wird dieser Wert wieder abgefragt und ein entsprechender Text ausgegeben.


Beachten Sie: JavaScript kennt zwei Arten des Tests auf Gleichheit: == und ===. Das zweifache Gleichheitszeichen versucht, die Typen der verglichenen Werte aneinander anzupassen, bevor der Vergleich stattfindet, und würde 2 == "2" mit „wahr“ beantworten. Das dreifache Gleichheitszeichen prüft dagegen strikt und ergibt für jeden Vergleich, bei dem die Typen der verglichenen Werte nicht übereinstimmen, ein „falsch“. Die switch-Anweisung verwendet den strikten Vergleich mit ===!

Schleifen

Zu den wichtigsten Kontrollstrukturen gehören Schleifen (auch „Wiederholung“ oder englisch loop) mit denen Sie einen Anweisungs-Block – den sogenannten Schleifenrumpf oder Schleifenkörper – wiederholt durchlaufen, solange die Schleifenbedingung gültig bleibt bzw. als Abbruchbedingung nicht eintritt.

Auf dieser Tutorialseite stellen wir nur zwei einfache Schleifenkonstrukte vor. Es gibt noch weitere Schleifenkonstrukte in JavaScript.

while

Mit Hilfe von while-Schleifen wiederholen Sie Programmanweisungen beliebig oft, nämlich solange, wie die im Schleifenkopf formulierte Bedingung erfüllt ist. Solche Schleifen eignen sich dann, wenn Sie nicht wissen, wie oft die Schleife durchlaufen werden soll.


7. Wiederholung einer Anweisung mit While ansehen …
'use strict';
let zahl,
    text;

zahl = prompt('Bitte geben Sie eine Zahl ein!', zahl);

  while (zahl >= 1) {
	text = zahl + '- 1 = ' + (zahl-1);
	zahl = zahl -1;
	console.log(text);
  }

In diesem Beispiel wird von der eingegebenen Zahl zahl in einer Schleife solange eins subtrahiert, bis die Summe 0 erreicht ist. Der Rechenweg wird in der Variablen text gespeichert und dann mit console.log ausgegeben.


Beachten Sie: Die hier verwendeten Scripte verzichten auf jegliches HTML. Normalerweise werden einzelne Elemente im DOM herausgesucht und ihr Inhalt überschrieben (siehe auch: DOM-Tutorial: Ausgabefunktion). Wir wollten es hier aber bewusst einfach halten.

for

Bei einer for-Schleife zählt der Computer von einer Anfangszahl bis zu einer Endzahl und wiederholt dabei jedes mal den Codeblock („Schleifenrumpf“) hinter dem for(...). Die aktuelle Zahl wird in eine Variable i („Iterator“) gesetzt, damit sie bei Bedarf in dem Codeblock Verwendung finden kann.

8. Zählschleife mit for ansehen …
'use strict';
let zahl,
    text;

confirm('Sind Sie bereit?');

for (let i = 1; i <= 10; i++) {
    zahl =  i * i;	  
    text = zahl +' = '+ i +' * '+ i ;
    console.log(text);
}

Sobald sie mit confirm() bestätigen, wird die Schleife ausgeführt. Die Zählschleife wird insgesamt 10 mal durchlaufen, nämlich so oft, wie der Zähler, der in der Variablen i definiert und mit dem Wert 1 initialisiert wird, kleiner oder gleich 10 ist (Ausdruck i <= 10), wobei er bei jedem Schleifendurchlauf um 1 erhöht wird (Ausdruck i++). Die Zählvariable i wird mit sich selbst multipliziert und der Variable zahl zugewiesen.

Anschließend wird alles ausgegeben.

Man kann der Variablen auch einen anderen Namen geben (z. B. zaehler); konventionsgemäß nennt man sie aber meistens i, so wie auch hier in unserem Beispiel.

Ebenso ist es möglich, die Variable hinunter- statt hinaufzählen zu lassen (i-- statt i++). In diesem Fall muss man aber den Anfangs- und den Endwert anpassen – denn wenn i mit dem Wert 1 anfängt und dann immer heruntergezählt wird, dann wird die Bedingung i <= 10 immer zutreffen und die Schleife wird unendlich oft durchlaufen. (Anmerkung: In Wirklichkeit wird sie nicht unendlich oft durchlaufen, sondern „nur“ einige Milliarden Male, bis es zu einem sogenannten Überlauf kommt; in der Praxis macht dies für uns hier aber keinen Unterschied.)

Für einen Countdown müsste die entsprechende Zeile also wie folgt aussehen:

8. Countdown-Zählschleife mit for
// ... Rest genau wie oben ...

for (let i = 10; i >= 1; i--) {

// ... Rest genau wie oben ...

Fallen und Fehlerquellen

  • Klammern
    Müssen es runde oder geschweifte Klammern sein?
    Alle Klammern, die geöffnet werden, müssen auch wieder geschlossen werden!
    Ist eine Klammer zuviel im Script-Code?
  • Hochkommata um Strings?
    Fehlt ein schließendes Hochkomma oder wird ein doppeltes Hochkomma durch ein einfaches Hochkomma geschlossen?
  • Bindestrich im Variablennamen?
    Wenn Sie eine Variable mit einem Bindestrich schreiben, wird aus ihrem Namen ein Ausdruck, da JavaScript den Bindestrich als Rechenoperator ansieht und versucht die beiden Teile der Variablen zu subtrahieren.
  • Groß- und Kleinschreibung
    Javascript ist case-sensitive, das heißt, dass nameVorname und namevorname unterschiedliche Variablen sind. Sie geben in der Konsole im strengen Modus eine Fehlermeldung.
    Empfehlung: Verwenden Sie das so genannte CamelCase, in dem jeweils der erste Buchstabe der einzelnen Bestandteile des Namens groß geschrieben wird.
    Beispiel:
    DiesIstEineVariable.
  • Schreibfehler in einer der DOM-Methoden?
    Vor allem Methoden wie getElementById werden oft falsch geschrieben. Hier hilft ein Editor (oder eine IDE) mit autocomplete, der Methoden und mehrmals verwendete Variablen automatisch vorschlägt.


Empfehlung: Debuggen anstatt Rumprobieren

Rumprobieren ist in der Regel allerdings nicht die effizienteste Methode um Fehlerursachen zu identifizieren. Wann immer irgendwas nicht funktioniert, sollte zunächst ein Blick auf die Konsole des Browsers geworfen werden. Dort finden Sie entsprechende Hinweise, um welche Art von Fehler es sich handelt, wie zum Beispiel …

  • ein Syntax Error, etwa wenn das Schließen einer Klammer vergessen wurde, oder
  • ein Reference Error, wenn du an einer Stelle versuchst über einen Bezeichner eine Variable zu referenzieren, die in dem jeweiligen Kontext (noch) nicht existiert (also wie mit event in deinem Frageposting), oder
  • ein Type Error, üblicherweise dadurch verursacht, dass an einer Stelle ein Objekt erwartet wird, aber statt dessen der primitive Wert undefined vorliegt, da die Übergabe vergessen wurde.

… sowie darüber hinaus auch immer eine Angabe dazu gegeben wird, an welcher Stelle, also in welcher Datei und in welcher Zeile der Fehler aufgetreten ist. Meistens genügt das schon, um den Fehler zu finden.

Manchmal funktioniert etwas aber auch nicht, ohne dass das Programm durch eine Ausnahme beendet wurde. In diesem Fall hilft die Eingrenzungstaktik, wobei man step by step diejenigen Teile des Programms auskommentiert, die wie gewünscht funktionieren, bis dann schließlich der Teil gefunden ist, der nicht funktioniert.

Eine weitere Möglichkeit ist das Setzen von Haltepunkten, die automatisch alle Variablen in der Konsole angeben.

Dann ist entsprechend immer zu prüfen, ob die tatsächlichen Werte an einer Stelle mit den erwarteten Werten übereinstimmen. So kommt man der Fehlerursache Schritt für Schritt näher. ;-)


Quellen

  1. Self-Forum: Wiki: Tutorial Grundlagen von Strings und Arrays vom 20.04.2017

Lint

Mit ESlint können Sie Ihren Code linten, d.h. auf Richtigkeit prüfen. So werden nicht initialisierte Variablen und andere Schwachstellen erkannt.