-
-
diff --git a/courses/javascript/02_funktionen_und_arrays/musterloesung.js b/courses/javascript/02_funktionen_und_arrays/musterloesung.js
deleted file mode 100644
index b2d5129..0000000
--- a/courses/javascript/02_funktionen_und_arrays/musterloesung.js
+++ /dev/null
@@ -1,11 +0,0 @@
-function verdoppeln(zahlen) {
- return zahlen.map(x => x * 2);
-}
-
-function filterGeradeZahlen(zahlen) {
- return zahlen.filter(x => x % 2 === 0);
-}
-
-function arraySumme(zahlen) {
- return zahlen.reduce((acc, curr) => acc + curr, 0);
-}
diff --git a/courses/javascript/02_funktionen_und_arrays/test_aufgabe.js b/courses/javascript/02_funktionen_und_arrays/test_aufgabe.js
deleted file mode 100644
index 302882c..0000000
--- a/courses/javascript/02_funktionen_und_arrays/test_aufgabe.js
+++ /dev/null
@@ -1,5 +0,0 @@
-assert.deepStrictEqual(verdoppeln([1, 2, 3]), [2, 4, 6], "verdoppeln([1, 2, 3]) sollte [2, 4, 6] sein");
-assert.deepStrictEqual(filterGeradeZahlen([1, 2, 3, 4, 5, 6]), [2, 4, 6], "filterGeradeZahlen() filtert unvollständig");
-assert.strictEqual(arraySumme([10, 20, 30]), 60, "arraySumme([10, 20, 30]) sollte 60 sein");
-assert.strictEqual(arraySumme([]), 0, "arraySumme([]) sollte 0 sein");
-console.log("✅ Alle 4 JavaScript Tests in JS 02 erfolgreich bestanden!");
diff --git a/courses/javascript/02_kontrollfluss_und_schleifen/README.md b/courses/javascript/02_kontrollfluss_und_schleifen/README.md
new file mode 100644
index 0000000..23d340b
--- /dev/null
+++ b/courses/javascript/02_kontrollfluss_und_schleifen/README.md
@@ -0,0 +1,200 @@
+# JS 02: Kontrollfluss, Verzweigungen & Schleifen 🚦
+
+Willkommen zu **Modul 02** des JavaScript-Kurses!
+
+In realen Programmen läuft Code selten nur starr von oben nach unten ab. Ein Programm muss Entscheidungen treffen ("Ist der Benutzer eingeloggt?", "Hat der Kunde einen Rabatt?") und Aktionen wiederholen ("Durchsuche 1000 Produkte", "Summiere Zahlen").
+
+In diesem Modul lernst du alle Werkzeuge kennen, mit denen du den Kontrollfluss deines JavaScript-Codes präzise und elegant steuerst.
+
+---
+
+## 💡 1. Das Wichtigste in Kürze
+
+### A. Verzweigungen: `if`, `else if`, `else`
+Mit Verzweigungen führst du Codeblöcke nur dann aus, wenn bestimmte Bedingungen erfüllt (`true`) sind:
+
+```javascript
+const punkte = 85;
+
+if (punkte >= 90) {
+ console.log("Note: Sehr gut");
+} else if (punkte >= 75) {
+ console.log("Note: Gut");
+} else if (punkte >= 50) {
+ console.log("Note: Bestanden");
+} else {
+ console.log("Note: Nicht bestanden");
+}
+```
+
+#### ⚠️ Strikter Vergleich: `===` vs. `==`
+Verwende in modernem JavaScript **immer den strikten Gleichheitsoperator `===`** (und Ungleichheit `!==`):
+- `===` prüft Wert **und Datentyp** (ohne implizite Typkonvertierung).
+- `==` führt vorher eine automatische Typumwandlung (*Type Coercion*) durch, was zu tückischen Bugs führt (`0 == ""` ist `true`, aber `0 === ""` ist `false`).
+
+```javascript
+console.log(5 === "5"); // false (Zahl vs. String)
+console.log(5 == "5"); // true (Achtung: Gefährlich!)
+console.log(null === undefined); // false
+```
+
+---
+
+### B. Die `switch-case`-Anweisung
+Wenn eine einzelne Variable gegen viele diskrete Werte geprüft werden soll, ist `switch` oft übersichtlicher als endlose Ketten von `else if`:
+
+```javascript
+const status = "geliefert";
+
+switch (status) {
+ case "bestellt":
+ console.log("Paket wird verpackt.");
+ break; // Verhindert 'Fall-Through' zum nächsten Case!
+ case "unterwegs":
+ console.log("Paket ist im Zustellfahrzeug.");
+ break;
+ case "geliefert":
+ console.log("Paket erfolgreich zugestellt!");
+ break;
+ default:
+ console.log("Unbekannter Status.");
+}
+```
+
+> **Wichtig:** Vergiss niemals das `break;` am Ende jedes Case-Blocks, außer du möchtest bewusst mehrere Cases zusammenfassen (*Fall-Through*).
+
+---
+
+### C. Der Ternäre Operator (`? :`)
+Für einfache Entweder-Oder-Zuweisungen bietet der ternäre Operator eine kompakte Einzeiler-Syntax:
+
+$$\text{Bedingung} \ ? \ \text{Wert wenn wahr} \ : \ \text{Wert wenn falsch}$$
+
+```javascript
+const alter = 20;
+const status = alter >= 18 ? "Erwachsen" : "Minderjährig";
+console.log(status); // "Erwachsen"
+
+// Auch kombinierbar für Rabatte:
+const istPremium = true;
+const rabatt = istPremium ? 0.2 : 0.0;
+```
+
+---
+
+### D. Schleifen in JavaScript
+
+#### 1. Die klassische Zählschleife: `for (let i = 0; i < n; i++)`
+Ideal, wenn du die genaue Anzahl der Durchläufe kennst oder den Index benötigst:
+
+```javascript
+for (let i = 1; i <= 5; i++) {
+ console.log(`Durchlauf Nummer: ${i}`);
+}
+```
+
+#### 2. Die moderne Iterationsschleife: `for (const element of array)`
+Perfekt zum Durchlaufen von Arrays und iterierbaren Datenstrukturen:
+
+```javascript
+const fruechte = ["Apfel", "Banane", "Mango"];
+for (const frucht of fruechte) {
+ console.log(`Lecker: ${frucht}`);
+}
+```
+
+#### 3. Die Objekt-Schlüsselschleife: `for (const key in object)`
+Durchläuft alle Schlüssel (*Keys*) eines Objekts:
+
+```javascript
+const auto = { marke: "BMW", baujahr: 2023, ps: 190 };
+for (const eigenschaft in auto) {
+ console.log(`${eigenschaft}: ${auto[eigenschaft]}`);
+}
+```
+
+#### 4. Bedingungsgesteuerte Schleifen: `while` & `do...while`
+- `while (bedingung)`: Prüft die Bedingung **vor** dem ersten Durchlauf.
+- `do { ... } while (bedingung)`: Führt den Codeblock **mindestens einmal** aus, bevor geprüft wird.
+
+```javascript
+let energie = 100;
+while (energie > 0) {
+ console.log(`Akku bei ${energie}%`);
+ energie -= 25;
+}
+```
+
+---
+
+### E. Schleifensteuerung: `break` und `continue`
+
+- **`break`**: Bricht die gesamte Schleife sofort ab und springt hinter die Schleife.
+- **`continue`**: Bricht nur den *aktuellen Durchlauf* ab und springt direkt zur nächsten Iteration.
+
+```javascript
+// Beispiel: Nur ungerade Zahlen überspringen (continue) und bei 8 stoppen (break)
+for (let i = 1; i <= 10; i++) {
+ if (i % 2 !== 0) {
+ continue; // Ungerade Zahl -> Weiter zur nächsten Zahl
+ }
+ if (i > 8) {
+ break; // Beende Schleife vorzeitig
+ }
+ console.log(i); // Gibt 2, 4, 6, 8 aus
+}
+```
+
+---
+
+## 🚦 Die didaktische Analogie: "Die Verkehrsampeln & Kreisverkehre im Code"
+
+Stelle dir den Programmablauf wie eine Autofahrt durch eine moderne Stadt vor:
+
+- 🟢 **Die Ampelkreuzung (`if` / `else` / `switch`):**
+ Dein Auto rollt auf eine Kreuzung zu. Steht die Ampel auf Grün (`true`), fährst du geradeaus weiter. Zeigt sie Rot (`false`), biegst du in die Ausweichroute (`else`) ab. Eine `switch`-Kreuzung ist ein mehrspuriger Verteiler: Jede Spur führt zu einem anderen Ziel (`case`), und wenn keine passt, nimmst du die Notspur (`default`).
+
+- 🔄 **Der Kreisverkehr (`while` / `for`):**
+ Schleifen sind wie ein Kreisverkehr. Du drehst solange deine Runden, bis eine bestimmte Bedingung nicht mehr erfüllt ist oder du deine gewünschte Ausfahrt entdeckst.
+ - Mit **`continue`** bleibst du auf der Innenbahn und fährst ohne Anhalten direkt in die nächste Runde.
+ - Mit **`break`** nimmst du sofort die Ausfahrt und verlässt den Kreisverkehr endgültig.
+
+---
+
+## 🎯 Aufgaben & Teilziele in `aufgabe.js`
+
+Öffne die Datei `aufgabe.js` und implementiere folgende 5 Funktionen:
+
+1. **TODO 1: `kategorisiereAlter(alter)`**
+ Kategorisiert ein Alter in String-Gruppen:
+ - `< 13` $\rightarrow$ `"Kind"`
+ - `13` bis `17` $\rightarrow$ `"Jugendlicher"`
+ - `18` bis `64` $\rightarrow$ `"Erwachsener"`
+ - $\ge 65$ $\rightarrow$ `"Senior"`
+
+2. **TODO 2: `wochentagName(tagNummer)`**
+ Wandelt eine Zahl `1` bis `7` in den Wochentag `"Montag"` bis `"Sonntag"` um. Alle anderen Eingaben liefern `"Ungültig"`. Verwende `switch(tagNummer)`.
+
+3. **TODO 3: `berechneRabattpreis(preis, istPremium, hatGutschein)`**
+ Berechnet den rabattierten Endpreis:
+ - Premium + Gutschein $\rightarrow$ 30% Rabatt (`preis * 0.7`)
+ - Nur Premium $\rightarrow$ 20% Rabatt (`preis * 0.8`)
+ - Nur Gutschein $\rightarrow$ 10% Rabatt (`preis * 0.9`)
+ - Weder noch $\rightarrow$ kein Rabatt (`preis`)
+
+4. **TODO 4: `summeGeraderZahlen(start, ende)`**
+ Summiert alle geraden Zahlen im Intervall $[start, ende]$. Verwende eine `for`-Schleife und `continue` für ungerade Zahlen.
+
+5. **TODO 5: `findeErstesVielfaches(zahlen, teiler)`**
+ Durchsucht das Array `zahlen` mit `for...of` nach der ersten Zahl, für die `zahl % teiler === 0` gilt. Beende die Schleife mit `break` und gib die gefundene Zahl zurück (oder `null`, wenn kein Vielfaches existiert).
+
+---
+
+## 🧪 Tests ausführen
+
+Öffne die Web-IDE im Browser oder starte den Testrunner:
+
+```bash
+# Im Browser:
+workspace.html?course=javascript&track=track_1_grundlagen&chapter=02_kontrollfluss_und_schleifen
+```
diff --git a/courses/javascript/02_kontrollfluss_und_schleifen/aufgabe.js b/courses/javascript/02_kontrollfluss_und_schleifen/aufgabe.js
new file mode 100644
index 0000000..1a56aa7
--- /dev/null
+++ b/courses/javascript/02_kontrollfluss_und_schleifen/aufgabe.js
@@ -0,0 +1,59 @@
+/**
+ * 🌐 JS 02: KONTROLLFLUSS, VERZWEIGUNGEN & SCHLEIFEN 🌐
+ * ====================================================
+ * Entscheidungen treffen und Wiederholungen steuern in JavaScript.
+ */
+
+// 🎯 TEILZIEL 1 (TODO 1): kategorisiereAlter(alter)
+// Bestimme die Altersgruppe anhand folgender Kriterien:
+// - kleiner als 13: "Kind"
+// - 13 bis 17 (inklusive): "Jugendlicher"
+// - 18 bis 64 (inklusive): "Erwachsener"
+// - ab 65 (inklusive): "Senior"
+// Verwende if, else if und else mit striktem Vergleich.
+function kategorisiereAlter(alter) {
+ // TODO: Implementiere die Alterskategorisierung
+ return "";
+}
+
+// 🎯 TEILZIEL 2 (TODO 2): wochentagName(tagNummer)
+// Wandle eine Zahl von 1 bis 7 in den passenden Wochentag um:
+// 1 -> "Montag", 2 -> "Dienstag", 3 -> "Mittwoch", 4 -> "Donnerstag",
+// 5 -> "Freitag", 6 -> "Samstag", 7 -> "Sonntag"
+// Für alle anderen Zahlen/Eingaben soll "Ungültig" zurückgegeben werden.
+// Verwende dafür eine switch-case Anweisung mit break und default.
+function wochentagName(tagNummer) {
+ // TODO: Nutze switch(tagNummer) { case ...: return ...; default: ... }
+ return "";
+}
+
+// 🎯 TEILZIEL 3 (TODO 3): berechneRabattpreis(preis, istPremium, hatGutschein)
+// Berechne den rabattierten Endpreis:
+// - Wenn istPremium UND hatGutschein: 30% Rabatt (zahlt 70%, also preis * 0.7)
+// - Wenn nur istPremium: 20% Rabatt (zahlt 80%, also preis * 0.8)
+// - Wenn nur hatGutschein: 10% Rabatt (zahlt 90%, also preis * 0.9)
+// - Sonst: kein Rabatt (zahlt 100%, also preis)
+// Tipp: Nutze if/else oder den ternären Operator.
+function berechneRabattpreis(preis, istPremium, hatGutschein) {
+ // TODO: Berechne den reduzierten Preis
+ return 0;
+}
+
+// 🎯 TEILZIEL 4 (TODO 4): summeGeraderZahlen(start, ende)
+// Berechne die Summe aller GERADEN Zahlen im Bereich von start bis einschließlich ende.
+// Nutze eine for-Schleife (for (let i = start; i <= ende; i++)) und
+// überspringe ungerade Zahlen mit 'continue'.
+function summeGeraderZahlen(start, ende) {
+ // TODO: for-Schleife mit continue für ungerade Zahlen
+ return 0;
+}
+
+// 🎯 TEILZIEL 5 (TODO 5): findeErstesVielfaches(zahlen, teiler)
+// Durchsuche das Array 'zahlen' nach der ersten Zahl, die ohne Rest durch 'teiler'
+// teilbar ist (zahl % teiler === 0).
+// Verwende eine for...of Schleife und beende die Schleife vorzeitig mit 'break' (oder direktem return).
+// Wird kein Vielfaches gefunden oder ist das Array leer, gib null zurück.
+function findeErstesVielfaches(zahlen, teiler) {
+ // TODO: for (const zahl of zahlen) mit Prüfung und break
+ return null;
+}
diff --git a/courses/javascript/02_kontrollfluss_und_schleifen/index.html b/courses/javascript/02_kontrollfluss_und_schleifen/index.html
new file mode 100644
index 0000000..4c5dde3
--- /dev/null
+++ b/courses/javascript/02_kontrollfluss_und_schleifen/index.html
@@ -0,0 +1,186 @@
+
+
+
+
+
+ JS 02: Kontrollfluss & Schleifen
+
+
+
+
+
+
+
+
🌐 JavaScript Grundlagen
+
JS 02: Kontrollfluss, Verzweigungen & Schleifen
+
Lerne Entscheidungslogik mit if/else, switch und den mächtigen Schleifenkonstrukten in modernem JavaScript.
+
+
+
+
+
+
+
💡 1. Verzweigungen & Strikter Vergleich (===)
+
Bedingte Anweisungen steuern, welche Codeblöcke bei bestimmten Bedingungen ausgeführt werden. In modernem JavaScript verwenden wir ausnahmslos den strikten Vergleich === (bzw. !==), um ungewollte Typumwandlungen zu verhindern:
Wenn du eine Variable gegen viele konkrete Werte prüfen möchtest, sorgt switch für sauberen Code. Für schnelle Zuweisungen ist der ternäre Operator (? :) die beste Wahl:
Wiederhole Operationen präzise. Mit break brichst du eine Schleife vorzeitig ab, mit continue springst du sofort zum nächsten Durchlauf:
+
+
// 1. Zählschleife:
+for (let i = 1; i <= 5; i++) {
+ if (i === 3) continue; // Überspringt die 3
+ console.log(i); // 1, 2, 4, 5
+}
+
+// 2. Iteration über Arrays (for...of):
+const zutaten = ["Mehl", "Zucker", "Eier"];
+for (const zutat of zutaten) {
+ console.log("Zutat:", zutat);
+}
+
+// 3. Bedingungsschleife:
+let countdown = 3;
+while (countdown > 0) {
+ console.log(countdown--);
+}
+
+
+
🚦 Didaktische Analogie: Die Verkehrsampeln & Kreisverkehre im Code
+
Die Ampelkreuzung (if/else/switch): Je nachdem, welches Signal aufleuchtet, nimmt dein Datenstrom eine andere Abbiegung. switch ist der mehrspurige Kreisverkehr mit festen Ausfahrten.
+
Der Kreisverkehr (while/for): Dein Code kreist so lange, wie die Bedingung wahr ist. Mit continue fährst du ohne Halt in die nächste Runde, mit break nimmst du sofort die nächste Ausfahrt ins Freie!
+
+
+
+
+
+
📑 4. Cheat-Sheet: Kontrollfluss & Schleifen
+
+
+
+
+
Konstrukt
+
Beispiel
+
Zweck
+
+
+
+
+
if / else if / else
+
if (x > 0) { ... }
+
Verzweigung nach Wahrheitswerten
+
+
+
switch(wert) { case ...: }
+
case 1: return "Mo"; break;
+
Gezielte Prüfung auf diskrete Werte
+
+
+
bed ? a : b
+
const s = a >= 18 ? 'Erw' : 'Kind';
+
Kompakter ternärer Ausdruck
+
+
+
for (let i = 0; i < n; i++)
+
for (let i = 0; i < 10; i++)
+
Zählschleife mit Index-Schritten
+
+
+
for (const x of array)
+
for (const item of items)
+
Direkte Element-Iteration
+
+
+
while (bed) / do..while
+
while (n > 0) n--;
+
Bedingungsgesteuerte Schleifen
+
+
+
break / continue
+
if (i % 2 !== 0) continue;
+
Schleifenabbruch / Nächster Durchlauf
+
+
+
+
+
+
+
+
+
🎯 Aufgaben & Checkliste in aufgabe.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
💡 Tipp für Teilziel 4 & 5
+
Für ungerade Zahlen kannst du if (i % 2 !== 0) continue; schreiben. Bei findeErstesVielfaches beende die Schleife mit break, sobald zahl % teiler === 0 zutrifft!
+
+
diff --git a/courses/javascript/02_kontrollfluss_und_schleifen/musterloesung.js b/courses/javascript/02_kontrollfluss_und_schleifen/musterloesung.js
new file mode 100644
index 0000000..fce8525
--- /dev/null
+++ b/courses/javascript/02_kontrollfluss_und_schleifen/musterloesung.js
@@ -0,0 +1,66 @@
+function kategorisiereAlter(alter) {
+ if (alter < 13) {
+ return "Kind";
+ } else if (alter <= 17) {
+ return "Jugendlicher";
+ } else if (alter <= 64) {
+ return "Erwachsener";
+ } else {
+ return "Senior";
+ }
+}
+
+function wochentagName(tagNummer) {
+ switch (tagNummer) {
+ case 1:
+ return "Montag";
+ case 2:
+ return "Dienstag";
+ case 3:
+ return "Mittwoch";
+ case 4:
+ return "Donnerstag";
+ case 5:
+ return "Freitag";
+ case 6:
+ return "Samstag";
+ case 7:
+ return "Sonntag";
+ default:
+ return "Ungültig";
+ }
+}
+
+function berechneRabattpreis(preis, istPremium, hatGutschein) {
+ let rabattSatz = 0;
+ if (istPremium && hatGutschein) {
+ rabattSatz = 0.3;
+ } else if (istPremium) {
+ rabattSatz = 0.2;
+ } else if (hatGutschein) {
+ rabattSatz = 0.1;
+ }
+ return preis * (1 - rabattSatz);
+}
+
+function summeGeraderZahlen(start, ende) {
+ let summe = 0;
+ for (let i = start; i <= ende; i++) {
+ if (i % 2 !== 0) {
+ continue;
+ }
+ summe += i;
+ }
+ return summe;
+}
+
+function findeErstesVielfaches(zahlen, teiler) {
+ let ergebnis = null;
+ for (const zahl of zahlen) {
+ if (zahl % teiler === 0) {
+ ergebnis = zahl;
+ break;
+ }
+ }
+ return ergebnis;
+}
diff --git a/courses/javascript/02_kontrollfluss_und_schleifen/test_aufgabe.js b/courses/javascript/02_kontrollfluss_und_schleifen/test_aufgabe.js
new file mode 100644
index 0000000..6a286be
--- /dev/null
+++ b/courses/javascript/02_kontrollfluss_und_schleifen/test_aufgabe.js
@@ -0,0 +1,42 @@
+// 🧪 Testsuite: JS 02 - Kontrollfluss, Verzweigungen & Schleifen
+
+// Test 1: kategorisiereAlter
+assert.strictEqual(kategorisiereAlter(5), "Kind", "Alter 5 sollte 'Kind' sein");
+assert.strictEqual(kategorisiereAlter(12), "Kind", "Alter 12 sollte 'Kind' sein");
+assert.strictEqual(kategorisiereAlter(13), "Jugendlicher", "Alter 13 sollte 'Jugendlicher' sein");
+assert.strictEqual(kategorisiereAlter(17), "Jugendlicher", "Alter 17 sollte 'Jugendlicher' sein");
+assert.strictEqual(kategorisiereAlter(18), "Erwachsener", "Alter 18 sollte 'Erwachsener' sein");
+assert.strictEqual(kategorisiereAlter(64), "Erwachsener", "Alter 64 sollte 'Erwachsener' sein");
+assert.strictEqual(kategorisiereAlter(65), "Senior", "Alter 65 sollte 'Senior' sein");
+assert.strictEqual(kategorisiereAlter(80), "Senior", "Alter 80 sollte 'Senior' sein");
+
+// Test 2: wochentagName
+assert.strictEqual(wochentagName(1), "Montag", "Tag 1 sollte 'Montag' sein");
+assert.strictEqual(wochentagName(3), "Mittwoch", "Tag 3 sollte 'Mittwoch' sein");
+assert.strictEqual(wochentagName(7), "Sonntag", "Tag 7 sollte 'Sonntag' sein");
+assert.strictEqual(wochentagName(0), "Ungültig", "Tag 0 sollte 'Ungültig' sein");
+assert.strictEqual(wochentagName(8), "Ungültig", "Tag 8 sollte 'Ungültig' sein");
+assert.strictEqual(wochentagName(-2), "Ungültig", "Tag -2 sollte 'Ungültig' sein");
+
+// Test 3: berechneRabattpreis
+assert.strictEqual(berechneRabattpreis(100, true, true), 70, "100€ mit Premium & Gutschein (30%) sollte 70€ ergeben");
+assert.strictEqual(berechneRabattpreis(100, true, false), 80, "100€ nur Premium (20%) sollte 80€ ergeben");
+assert.strictEqual(berechneRabattpreis(100, false, true), 90, "100€ nur Gutschein (10%) sollte 90€ ergeben");
+assert.strictEqual(berechneRabattpreis(100, false, false), 100, "100€ ohne Rabatte sollte 100€ ergeben");
+assert.strictEqual(berechneRabattpreis(50, true, true), 35, "50€ mit 30% Rabatt sollte 35€ ergeben");
+
+// Test 4: summeGeraderZahlen
+assert.strictEqual(summeGeraderZahlen(1, 10), 30, "Summe gerader Zahlen 1 bis 10 (2+4+6+8+10) sollte 30 sein");
+assert.strictEqual(summeGeraderZahlen(1, 1), 0, "Summe gerader Zahlen von 1 bis 1 sollte 0 sein");
+assert.strictEqual(summeGeraderZahlen(2, 2), 2, "Summe gerader Zahlen von 2 bis 2 sollte 2 sein");
+assert.strictEqual(summeGeraderZahlen(4, 8), 18, "Summe gerader Zahlen 4 bis 8 (4+6+8) sollte 18 sein");
+assert.strictEqual(summeGeraderZahlen(5, 5), 0, "Summe gerader Zahlen 5 bis 5 sollte 0 sein");
+
+// Test 5: findeErstesVielfaches
+assert.strictEqual(findeErstesVielfaches([7, 14, 21], 7), 7, "Erstes Vielfaches von 7 in [7, 14, 21] sollte 7 sein");
+assert.strictEqual(findeErstesVielfaches([5, 11, 18, 25], 6), 18, "Erstes Vielfaches von 6 in [5, 11, 18, 25] sollte 18 sein");
+assert.strictEqual(findeErstesVielfaches([1, 3, 5, 7], 2), null, "Kein Vielfaches von 2 vorhanden, sollte null sein");
+assert.strictEqual(findeErstesVielfaches([], 5), null, "Leeres Array sollte null liefern");
+assert.strictEqual(findeErstesVielfaches([10, 20, 30], 5), 10, "Erstes Vielfaches von 5 in [10, 20, 30] sollte 10 sein");
+
+console.log("✅ Alle 28 JavaScript Tests in JS 02 erfolgreich bestanden!");
diff --git a/courses/javascript/03_funktionen_und_scope/README.md b/courses/javascript/03_funktionen_und_scope/README.md
new file mode 100644
index 0000000..dcd06e0
--- /dev/null
+++ b/courses/javascript/03_funktionen_und_scope/README.md
@@ -0,0 +1,173 @@
+# JS 03: Funktionen, Gültigkeitsbereiche (Scope) & Closures 🔐
+
+Willkommen zu **Modul 03** des JavaScript-Kurses!
+
+In JavaScript sind Funktionen sogenannte **First-Class Citizens**: Sie können wie gewöhnliche Variablen gespeichert, als Argumente übergeben und von anderen Funktionen zurückgegeben werden (*Higher-Order Functions*).
+
+In diesem Modul meisterst du moderne Funktionsarten, Gültigkeitsbereiche (*Scopes*) und eines der faszinierendsten Kernkonzepte von JavaScript: **Closures**!
+
+---
+
+## 💡 1. Das Wichtigste in Kürze
+
+### A. Funktionsarten im Vergleich
+
+#### 1. Function Declaration (Klassische Funktionsdeklaration)
+Wird vollständig gehoistet (kann bereits vor ihrer Definition im Code aufgerufen werden):
+
+```javascript
+console.log(quadriere(4)); // 16 (funktioniert dank Hoisting!)
+
+function quadriere(x) {
+ return x * x;
+}
+```
+
+#### 2. Function Expression (Funktionsausdruck)
+Wird einer Variablen zugewiesen und ist erst ab der Zuweisungszeile verfügbar:
+
+```javascript
+const verdopple = function(x) {
+ return x * 2;
+};
+```
+
+#### 3. Arrow Function (Pfeilfunktion / ES6)
+Kompakte Syntax, kein eigenes `this` oder `arguments`-Objekt:
+
+```javascript
+// Ausführliche Arrow Function:
+const addiere = (a, b) => {
+ return a + b;
+};
+
+// Einzeilige Arrow Function mit implizitem Return:
+const multipliziere = (a, b) => a * b;
+```
+
+---
+
+### B. Moderne Parameter: Default- & Rest-Parameter
+
+#### 1. Default-Parameter (Standardwerte)
+Verhindert `undefined`, wenn kein Wert übergeben wird:
+
+```javascript
+function begruesse(name = "Gast", sprache = "de") {
+ return sprache === "de" ? `Hallo, ${name}!` : `Hello, ${name}!`;
+}
+
+console.log(begruesse()); // "Hallo, Gast!"
+console.log(begruesse("Anna", "en")); // "Hello, Anna!"
+```
+
+#### 2. Rest-Parameter (`...rest`)
+Sammelt eine beliebige Anzahl von Argumenten in einem echten JavaScript-Array:
+
+```javascript
+function maxZahl(...zahlen) {
+ // 'zahlen' ist ein echtes Array!
+ return Math.max(...zahlen);
+}
+
+console.log(maxZahl(3, 9, 21, 4)); // 21
+```
+
+---
+
+### C. Gültigkeitsbereiche (Scopes) & Hoisting
+
+JavaScript kennt drei Ebenen von Scopes:
+
+```
+┌─────────────────────────────────────────────────────────┐
+│ 🌍 GLOBAL SCOPE (Überall im Programm sichtbar) │
+│ const globalVar = "Welt"; │
+│ │
+│ ┌─────────────────────────────────────────────────┐ │
+│ │ 📦 FUNCTION SCOPE (Nur innerhalb der Funktion) │ │
+│ │ function meineFunktion() { │ │
+│ │ const funcVar = "Funktion"; │ │
+│ │ │ │
+│ │ ┌───────────────────────────────────────┐ │ │
+│ │ │ 🔒 BLOCK SCOPE (if, for, { let/const})│ │ │
+│ │ │ if (true) { │ │ │
+│ │ │ const blockVar = "Block"; │ │ │
+│ │ │ } │ │ │
+│ │ └───────────────────────────────────────┘ │ │
+│ │ } │ │
+│ └─────────────────────────────────────────────────┘ │
+└─────────────────────────────────────────────────────────┘
+```
+
+- **`let` und `const`** besitzen **Block-Scope** (gelten nur innerhalb der `{ ... }` geschweiften Klammern).
+- **`var`** besitzt nur **Function-Scope** (ignoriert `if`- und `for`-Blöcke) und sollte in modernem JS vermieden werden.
+- **Lexical Scope (Statische Bindung):** Innere Funktionen können auf Variablen ihrer äußeren Eltern-Scopes zugreifen, aber nicht umgekehrt!
+
+---
+
+### D. Closures: Das Super-Feature
+
+> **Definition Closure:** Eine Closure entsteht, wenn eine innere Funktion Zugriff auf Variablen ihres äußeren Gültigkeitsbereichs behält – **selbst nachdem die äußere Funktion ihre Ausführung längst beendet hat**.
+
+```javascript
+function erstelleSparschwein() {
+ let guthaben = 0; // Private Variable, von außen nicht direkt änderbar!
+
+ return function(einzahlung) {
+ guthaben += einzahlung;
+ return `Aktuelles Guthaben: ${guthaben} €`;
+ };
+}
+
+const meinKonto = erstelleSparschwein();
+console.log(meinKonto(50)); // "Aktuelles Guthaben: 50 €"
+console.log(meinKonto(100)); // "Aktuelles Guthaben: 150 €"
+
+// Wichtig: 'guthaben' kann von außen nicht manipuliert werden (z.B. kein meinKonto.guthaben = 0)!
+```
+
+---
+
+## 🔐 Die didaktische Analogie: "Der Tresor mit Geheimzahl"
+
+Stelle dir eine Closure wie einen **verschlossenen Tresor mit einem Einwurf-Schlitz und Zahlenrad** vor:
+
+- **Der Erbauer (Die äußere Factory-Funktion):**
+ Die äußere Funktion baut den Tresor und legt die interne Mechanik (die geschützten Variablen `let zaehler`) im Inneren des Gehäuses an.
+- **Der Schlüsselanhänger (Die zurückgegebene innere Funktion):**
+ Du erhältst von der Funktion lediglich den Schlüssel (die Referenz auf die innere Funktion).
+- **Geschützte Kapselung (Data Encapsulation):**
+ Niemand von außen kann die Tresorwand aufbrechen oder direkt an die internen Zahnräder fassen. Nur wenn du den Schlüssel drehst (`zaehler()`), verändert sich der interne Zählerstand kontrolliert.
+- **Unabhängige Instanzen:**
+ Jeder neue Aufruf der äußeren Funktion baut einen komplett eigenständigen Tresor. Tresor A und Tresor B beeinflussen sich gegenseitig zu keinem Zeitpunkt!
+
+---
+
+## 🎯 Aufgaben & Teilziele in `aufgabe.js`
+
+Öffne die Datei `aufgabe.js` und implementiere folgende 4 Funktionen:
+
+1. **TODO 1: `erstelleZaehler(startWert = 0)`**
+ Gibt eine Zählerfunktion (Closure) zurück. Jeder Aufruf erhöht den internen Zähler um 1 und gibt den neuen Stand zurück.
+
+2. **TODO 2: `multiplizierer(faktor)`**
+ Erzeugt eine Multiplikator-Funktion (Closure): `(x) => x * faktor`.
+ *Beispiel:* `const verdopple = multiplizierer(2); verdopple(5); // 10`
+
+3. **TODO 3: `summiereAlles(...zahlen)`**
+ Nimmt beliebig viele Zahlen per Rest-Parameter entgegen und gibt deren Gesamtsumme zurück. Bei leerem Aufruf wird `0` zurückgegeben.
+
+4. **TODO 4: `erstellePraefixierer(praefix)`**
+ Gibt eine Funktion (Closure) zurück, die vor jeden übergebenen String das vorgegebene `praefix` anhängt: `(text) => `${praefix}${text}``.
+
+---
+
+## 🧪 Tests ausführen
+
+Öffne die Web-IDE oder führe die Testsuite aus:
+
+```bash
+# Im Browser:
+workspace.html?course=javascript&track=track_1_grundlagen&chapter=03_funktionen_und_scope
+```
diff --git a/courses/javascript/03_funktionen_und_scope/aufgabe.js b/courses/javascript/03_funktionen_und_scope/aufgabe.js
new file mode 100644
index 0000000..61b969f
--- /dev/null
+++ b/courses/javascript/03_funktionen_und_scope/aufgabe.js
@@ -0,0 +1,60 @@
+/**
+ * 🌐 JS 03: FUNKTIONEN, SCOPE & CLOSURES 🌐
+ * ==========================================
+ * Funktionsarten, Gültigkeitsbereiche, Rest-Parameter und Closures.
+ */
+
+// 🎯 TEILZIEL 1 (TODO 1): erstelleZaehler(startWert = 0)
+// Erstelle eine Funktion, die einen internen Zähler kapselt (Closure).
+// Die zurückgegebene Funktion soll bei jedem Aufruf den Zähler um 1 erhöhen
+// und den neuen Wert zurückgeben.
+// Standardwert für startWert ist 0.
+// Beispiel:
+// const zaehler = erstelleZaehler(0);
+// zaehler(); // 1
+// zaehler(); // 2
+function erstelleZaehler(startWert = 0) {
+ // TODO: Innere Zählerfunktion (Closure) zurückgeben
+ return function() {
+ return 0;
+ };
+}
+
+// 🎯 TEILZIEL 2 (TODO 2): multiplizierer(faktor)
+// Erstelle eine Factory-Funktion (Closure), die eine Multiplikationsfunktion zurückgibt.
+// Die zurückgegebene Funktion nimmt eine Zahl x entgegen und gibt (x * faktor) zurück.
+// Beispiel:
+// const verdopple = multiplizierer(2);
+// verdopple(5); // 10
+// const verdreifache = multiplizierer(3);
+// verdreifache(4); // 12
+function multiplizierer(faktor) {
+ // TODO: Arrow-Funktion oder anonyme Funktion zurückgeben
+ return (x) => 0;
+}
+
+// 🎯 TEILZIEL 3 (TODO 3): summiereAlles(...zahlen)
+// Berechne die Summe beliebig vieler übergebener Zahlen unter Verwendung des
+// Rest-Parameters (...zahlen).
+// Werden keine Argumente übergeben, soll 0 zurückgegeben werden.
+// Beispiel:
+// summiereAlles(1, 2, 3, 4); // 10
+// summiereAlles(5, -2); // 3
+// summiereAlles(); // 0
+function summiereAlles(...zahlen) {
+ // TODO: Rest-Parameter aufsummieren
+ return 0;
+}
+
+// 🎯 TEILZIEL 4 (TODO 4): erstellePraefixierer(praefix)
+// Erstelle eine Funktion (Closure), die einen String als Präfix vor jeden
+// übergebenen Text setzt.
+// Beispiel:
+// const mitEuro = erstellePraefixierer("€ ");
+// mitEuro("100"); // "€ 100"
+// const https = erstellePraefixierer("https://");
+// https("google.com"); // "https://google.com"
+function erstellePraefixierer(praefix) {
+ // TODO: Closure-Funktion zurückgeben
+ return (text) => "";
+}
diff --git a/courses/javascript/03_funktionen_und_scope/index.html b/courses/javascript/03_funktionen_und_scope/index.html
new file mode 100644
index 0000000..e2e9175
--- /dev/null
+++ b/courses/javascript/03_funktionen_und_scope/index.html
@@ -0,0 +1,167 @@
+
+
+
+
+
+ JS 03: Funktionen, Scope & Closures
+
+
+
+
+
+
+
+
🌐 JavaScript Grundlagen
+
JS 03: Funktionen, Scope & Closures
+
Meistere moderne Funktionsdeklarationen, Arrow Functions, Rest-Parameter, Gültigkeitsbereiche und Closures.
+
+
+
+
+
+
+
💡 1. Funktionsarten & Moderne Parameter
+
JavaScript bietet klassische Deklarationen, Funktionsausdrücke und moderne Arrow Functions. Mit Default- und Rest-Parametern schreibst du flexiblen Code:
+
+
// 🎯 1. Arrow Function mit implizitem Return:
+const verdopple = x => x * 2;
+
+// 🎯 2. Default-Parameter:
+function begruesse(name = "Gast") {
+ return `Willkommen, ${name}!`;
+}
+
+// 🎯 3. Rest-Parameter (...sammelt alle Argumente als Array):
+function summiere(...zahlen) {
+ return zahlen.reduce((sum, n) => sum + n, 0);
+}
+console.log(summiere(10, 20, 30)); // 60
+
+
+
+
+
🌍 2. Gültigkeitsbereiche (Scope Hierarchy)
+
Variablen sind immer nur in ihrem deklarierten Bereich gültig. let und const erzeugen echte Block-Scopes:
+
+
+ 🌍 Global Scope: Überall im Skript verfügbar
+ └── 📦 Function Scope: Nur innerhalb der Funktion sichtbar
+ └── 🔒 Block Scope ({ let x = 1; }): Nur innerhalb des { }-Blocks
+
+
+
function scopeTest() {
+ const inside = "Ich lebe nur in der Funktion";
+ if (true) {
+ const block = "Ich lebe nur im if-Block";
+ }
+ // console.log(block); // ❌ ReferenceError: block is not defined
+}
+
+
+
+
+
🔐 3. Closures: Funktionen mit Gedächtnis
+
Eine Closure entsteht, wenn eine innere Funktion auf Variablen ihrer umgebenden Funktion zugreift – auch nachdem die äußere Funktion beendet ist:
Die äußere Funktion ist der Tresorbauer: Sie konstruiert ein geschütztes Gehäuse (die privaten Variablen) und händigt dir lediglich einen Schlüsselanhänger (die zurückgegebene innere Funktion) aus.
+
Niemand kann von außen in den Tresor greifen – nur durch Drehen des Schlüssels (Funktionsaufruf) wird das interne Zahnrad weitergedreht. Jeder neue Aufruf der äußeren Funktion baut einen neuen, isolierten Tresor!
+
+
+
+
+
+
📑 4. Cheat-Sheet: Funktionen & Scopes
+
+
+
+
+
Konzept
+
Syntax / Beispiel
+
Besonderheit
+
+
+
+
+
Arrow Function
+
const f = (x) => x * 2;
+
Kompakt, lexikalisches this
+
+
+
Default-Parameter
+
function f(a = 10) { ... }
+
Greift bei fehlendem Argument / undefined
+
+
+
Rest-Parameter
+
function f(...args) { ... }
+
Sammelt alle Parameter in ein Array
+
+
+
Block Scope
+
{ const x = 42; }
+
Gültigkeit beschränkt auf geschweifte Klammern
+
+
+
Closure
+
return () => ++counter;
+
Behält Zugriff auf Variablen des Erzeugungs-Scopes
+
+
+
+
+
+
+
+
+
🎯 Aufgaben & Checkliste in aufgabe.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
💡 Tipp für Teilziel 1 & 4
+
Für erstelleZaehler deklariere let zaehler = startWert; in der äußeren Funktion und gib eine innere Funktion zurück, die zaehler++ rechnet und den neuen Wert zurückliefert!
Array (Die Fotogalerie): Ein Fotoalbum mit festen Seiten-Nummern (Indizes 0, 1, 2...). Du kannst dasselbe Bild mehrfach einkleben und Filterfolien darüber legen.
+
Set (Der VIP-Club): Die Gästeliste an der Tür: Ein Gast kann nicht zweimal auf der Liste stehen. Neue Duplikate werden sofort abgewiesen.
+
Map (Das moderne Telefonbuch): Schnelles Nachschlagen: Du nennst den Namen (Key) und bekommst sofort die Nummer (Value) zurück.
+
+
+
+
+
+
📑 4. Cheat-Sheet: Array, Set & Map
+
+
+
+
+
Typ
+
Syntax / Methode
+
Beschreibung
+
+
+
+
+
Array
+
arr.map(fn)
+
Erzeugt neues transformiertes Array
+
+
+
Array
+
arr.filter(fn)
+
Filtert Elemente nach Wahrheitsbedingung
+
+
+
Array
+
arr.reduce(fn, start)
+
Aggregiert alle Werte zu einem Gesamtwert
+
+
+
Set
+
new Set(arr) / [...set]
+
Menge ohne Duplikate; Konvertierung zu Array
+
+
+
Set
+
set.has(val) / set.add(val)
+
Element prüfen / hinzufügen (Größe: .size)
+
+
+
Map
+
map.set(k, v) / map.get(k)
+
Schlüssel-Wert-Paar setzen / abrufen
+
+
+
Map
+
new Map(entries)
+
Initialisierung aus [[k, v], ...]
+
+
+
+
+
+
+
+
+
🎯 Aufgaben & Checkliste in aufgabe.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
💡 Tipp für Teilziel 4 & 6
+
Für entferneDuplikate: return [...new Set(liste)];. Für zaehleHaeufigkeiten: Iteriere mit for (const w of woerter) und setze map.set(w, (map.get(w) || 0) + 1);.
diff --git a/courses/javascript/03_objekte_und_destructuring/musterloesung.js b/courses/javascript/05_objekte_und_destructuring/musterloesung.js
similarity index 100%
rename from courses/javascript/03_objekte_und_destructuring/musterloesung.js
rename to courses/javascript/05_objekte_und_destructuring/musterloesung.js
diff --git a/courses/javascript/03_objekte_und_destructuring/test_aufgabe.js b/courses/javascript/05_objekte_und_destructuring/test_aufgabe.js
similarity index 100%
rename from courses/javascript/03_objekte_und_destructuring/test_aufgabe.js
rename to courses/javascript/05_objekte_und_destructuring/test_aufgabe.js
diff --git a/courses/javascript/06_klassen_und_oop/README.md b/courses/javascript/06_klassen_und_oop/README.md
new file mode 100644
index 0000000..d553fd0
--- /dev/null
+++ b/courses/javascript/06_klassen_und_oop/README.md
@@ -0,0 +1,153 @@
+# 🌐 JS 06: Klassen & Objektorientierung (OOP)
+
+Willkommen zu **Modul 06** des JavaScript-Kurses! In diesem Modul lernst du, wie moderne objektorientierte Programmierung (OOP) in JavaScript mit ES6-Klassensyntax funktioniert.
+
+---
+
+## 🎯 Lernziele
+
+Nach diesem Modul kannst du:
+1. **ES6-Klassen** mit `class`, `constructor` und Instanzmethoden definieren.
+2. Den **`this`**-Kontext innerhalb von Klasseninstanzen verstehen und sicher anwenden.
+3. Daten mit **Gettern (`get`) und Settern (`set`)** kapseln und vor fehlerhaften Werten schützen.
+4. **Vererbung (`extends`)** nutzen und den Elternkonstruktor mit **`super(...)`** aufrufen.
+5. **Statische Methoden & Fabrikfunktionen (`static`)** definieren und anwenden.
+6. Objekt-Typen zur Laufzeit mit **`instanceof`** überprüfen.
+
+---
+
+## 💡 Theoretische Grundlagen
+
+### 1. Der Klassen-Bauplan (`class`, `constructor`, `this`)
+
+Vor ECMAScript 2015 (ES6) basierte OOP in JavaScript rein auf Prototypen-Funktionen (`function Person() { ... } Person.prototype.sayHi = ...`). Die ES6-Klassensyntax bietet dafür eine moderne, lesbare und aus anderen Sprachen gewohnte Syntax:
+
+```javascript
+class Person {
+ constructor(vorname, nachname) {
+ this.vorname = vorname;
+ this.nachname = nachname;
+ }
+
+ vollstaendigerName() {
+ return `${this.vorname} ${this.nachname}`;
+ }
+}
+
+const user = new Person("Max", "Mustermann");
+console.log(user.vollstaendigerName()); // "Max Mustermann"
+```
+
+### 2. Kapselung & Getter/Setter
+
+Kapselung (Encapsulation) ist eines der 4 Grundprinzipien der OOP. Interne Zustände sollten nicht ungeschützt direkt verändert werden.
+
+```javascript
+class Bankkonto {
+ constructor(inhaber, startSaldo = 0) {
+ this.inhaber = inhaber;
+ this._saldo = startSaldo; // Konvention: _ kennzeichnet private/interne Felder
+ }
+
+ // Getter ermöglicht das Lesen wie ein Attribut: konto.saldo
+ get saldo() {
+ return this._saldo;
+ }
+
+ einzahlen(betrag) {
+ if (typeof betrag !== "number" || betrag <= 0) {
+ throw new Error("Einzahlungsbetrag muss positiv sein.");
+ }
+ this._saldo += betrag;
+ return this._saldo;
+ }
+}
+```
+
+### 3. Vererbung mit `extends` und `super`
+
+Kindklassen erben alle Methoden und Eigenschaften ihrer Basisklasse:
+
+```javascript
+class Fahrzeug {
+ constructor(marke, modell, baujahr) {
+ this.marke = marke;
+ this.modell = modell;
+ this.baujahr = baujahr;
+ }
+
+ beschreibung() {
+ return `${this.marke} ${this.modell} (${this.baujahr})`;
+ }
+}
+
+class ElektroAuto extends Fahrzeug {
+ constructor(marke, modell, baujahr, batterieKapazitaet) {
+ // ⚠️ WICHTIG: super() MUSS vor dem ersten Zugriff auf 'this' aufgerufen werden!
+ super(marke, modell, baujahr);
+ this.batterieKapazitaet = batterieKapazitaet;
+ }
+
+ // Überschreiben (Method Overriding):
+ beschreibung() {
+ return `${super.beschreibung()} [Elektro: ${this.batterieKapazitaet} kWh]`;
+ }
+
+ reichweiteBerechnen(verbrauchPro100km) {
+ return (this.batterieKapazitaet / verbrauchPro100km) * 100;
+ }
+}
+```
+
+### 4. Statische Methoden (`static`)
+
+Statische Methoden werden direkt an der Klasse aufgerufen, ohne eine Instanz mit `new` erstellen zu müssen. Sie eignen sich perfekt als Fabrikmethoden (Factory Pattern):
+
+```javascript
+class ElektroAuto extends Fahrzeug {
+ // ...
+ static erstelleStandardTesla() {
+ return new ElektroAuto("Tesla", "Model 3", 2024, 75);
+ }
+}
+
+const auto = ElektroAuto.erstelleStandardTesla();
+```
+
+---
+
+## 🏭 Didaktische Analogie: Der Bauplan & die Fabrikstraße
+
+- **Klasse (`class`)**: Der technische CAD-Bauplan in den Köpfen der Ingenieure.
+- **Instanz (`new`)**: Das fertig montierte Auto, das die Fabrikhalle verlässt.
+- **Konstruktor (`constructor`)**: Das Fließband, das Rohkarosserien mit Motor und Farbe ausstattet.
+- **Methoden (`this.fahren()`)**: Die Pedale und Funktionen des Fahrzeugs.
+- **Statische Methoden (`static`)**: Die Roboterwerkzeuge in der Werkshalle.
+
+---
+
+## 🎯 Aufgabenübersicht in `aufgabe.js`
+
+1. **TODO 1: Bankkonto**:
+ - `constructor(inhaber, startSaldo = 0)`
+ - `get saldo`
+ - `einzahlen(betrag)`
+ - `abheben(betrag)`
+2. **TODO 2: Fahrzeug**:
+ - `constructor(marke, modell, baujahr)`
+ - `beschreibung()`
+3. **TODO 3: ElektroAuto extends Fahrzeug**:
+ - `constructor(marke, modell, baujahr, batterieKapazitaet)`
+ - `reichweiteBerechnen(verbrauchPro100km)`
+ - `beschreibung()`
+4. **TODO 4: ElektroAuto.erstelleStandardTesla()**:
+ - Statische Fabrikmethode zur schnellen Erstellung eines Tesla Model 3.
+
+---
+
+## 🧪 Tests ausführen
+
+Führe die Testsuite in der Web-IDE oder lokal aus:
+```bash
+# In der Web-IDE: Klicke auf 'Code ausführen & testen'
+```
diff --git a/courses/javascript/06_klassen_und_oop/aufgabe.js b/courses/javascript/06_klassen_und_oop/aufgabe.js
new file mode 100644
index 0000000..5ec157a
--- /dev/null
+++ b/courses/javascript/06_klassen_und_oop/aufgabe.js
@@ -0,0 +1,98 @@
+/**
+ * 🌐 JS 06: KLASSEN & OBJEKTORIENTIERUNG (OOP) 🌐
+ * ==================================================
+ * Moderne ES6-Klassen, Konstruktoren, Getter/Setter,
+ * Vererbung mit extends/super und statische Methoden.
+ */
+
+// 🎯 TEILZIEL 1 (TODO 1): Klasse Bankkonto
+// Erstelle eine Klasse Bankkonto mit Kapselung:
+// - constructor(inhaber, startSaldo = 0):
+// - Weist this.inhaber zu
+// - Weist this._saldo das startSaldo zu (Default: 0)
+// - Getter get saldo(): Gibt this._saldo zurück
+// - Methode einzahlen(betrag):
+// - Wirft einen Fehler (throw new Error("...")), wenn betrag <= 0
+// - Erhöht this._saldo um betrag und gibt den neuen Saldo zurück
+// - Methode abheben(betrag):
+// - Wirft einen Fehler, wenn betrag <= 0
+// - Wirft einen Fehler, wenn betrag > this._saldo ("Nicht genügend Guthaben")
+// - Verringert this._saldo um betrag und gibt den neuen Saldo zurück
+class Bankkonto {
+ constructor(inhaber, startSaldo = 0) {
+ // TODO: Initialisiere inhaber und _saldo
+ this.inhaber = inhaber;
+ this._saldo = 0;
+ }
+
+ get saldo() {
+ // TODO: Getter für _saldo implementieren
+ return 0;
+ }
+
+ einzahlen(betrag) {
+ // TODO: Validierung und Erhöhung von _saldo
+ return 0;
+ }
+
+ abheben(betrag) {
+ // TODO: Validierung, Guthabenprüfung und Reduzierung von _saldo
+ return 0;
+ }
+}
+
+// 🎯 TEILZIEL 2 (TODO 2): Basisklasse Fahrzeug
+// Erstelle eine Basisklasse Fahrzeug:
+// - constructor(marke, modell, baujahr):
+// - Weist this.marke, this.modell und this.baujahr zu
+// - Methode beschreibung():
+// - Gibt einen String im Format "${marke} ${modell} (${baujahr})" zurück
+class Fahrzeug {
+ constructor(marke, modell, baujahr) {
+ // TODO: Eigenschaften initialisieren
+ this.marke = marke;
+ this.modell = modell;
+ this.baujahr = baujahr;
+ }
+
+ beschreibung() {
+ // TODO: String-Repräsentation zurückgeben
+ return "";
+ }
+}
+
+// 🎯 TEILZIEL 3 (TODO 3): Kindklasse ElektroAuto extends Fahrzeug
+// Erstelle eine Kindklasse ElektroAuto, die von Fahrzeug erbt:
+// - constructor(marke, modell, baujahr, batterieKapazitaet):
+// - Ruft super(marke, modell, baujahr) auf
+// - Weist this.batterieKapazitaet (in kWh) zu
+// - Methode reichweiteBerechnen(verbrauchPro100km):
+// - Wirft einen Fehler, wenn verbrauchPro100km <= 0
+// - Berechnet die Reichweite in km: (batterieKapazitaet / verbrauchPro100km) * 100
+// - Überschreibe beschreibung():
+// - Gibt "${super.beschreibung()} [Elektro: ${batterieKapazitaet} kWh]" zurück
+// 🎯 TEILZIEL 4 (TODO 4): Statische Fabrikmethode ElektroAuto.erstelleStandardTesla()
+// - static erstelleStandardTesla():
+// - Gibt eine neue Instanz von ElektroAuto mit ("Tesla", "Model 3", 2024, 75) zurück
+class ElektroAuto extends Fahrzeug {
+ constructor(marke, modell, baujahr, batterieKapazitaet) {
+ // TODO: super(...) aufrufen und batterieKapazitaet initialisieren
+ super(marke, modell, baujahr);
+ this.batterieKapazitaet = 0;
+ }
+
+ beschreibung() {
+ // TODO: super.beschreibung() nutzen und um Batterie erweitern
+ return "";
+ }
+
+ reichweiteBerechnen(verbrauchPro100km) {
+ // TODO: Reichweite anhand von batterieKapazitaet und Verbrauch berechnen
+ return 0;
+ }
+
+ static erstelleStandardTesla() {
+ // TODO: Neues ElektroAuto-Objekt mit Standard-Parametern erzeugen und zurückgeben
+ return null;
+ }
+}
diff --git a/courses/javascript/06_klassen_und_oop/index.html b/courses/javascript/06_klassen_und_oop/index.html
new file mode 100644
index 0000000..22e0925
--- /dev/null
+++ b/courses/javascript/06_klassen_und_oop/index.html
@@ -0,0 +1,334 @@
+
+
+
+
+
+ JS 06: Klassen & Objektorientierung (OOP)
+
+
+
+
+
+
+
+
+
🌐 Lehrpfad 2: Moderne Datenmodelle & OOP
+
JS 06: Klassen & Objektorientierung (OOP)
+
Lerne professionelle Datenmodellierung mit ES6-Klassen, Kapselung, Vererbung und statischen Fabrikmethoden.
+
+
+
+
+
+
+
🏗️ 1. Der Klassen-Bauplan: class, constructor & this
+
+ Mit ECMAScript 2015 (ES6) führte JavaScript eine elegante, standardisierte Klassensyntax ein. Eine Klasse ist wie ein technischer Bauplan, aus dem mit dem Schlüsselwort new beliebig viele konkrete Objekte (Instanzen) gefertigt werden:
+
+
+
class Benutzer {
+ // Der Konstruktor wird beim Erzeugen mit 'new' aufgerufen:
+ constructor(name, email) {
+ this.name = name; // Instanzvariable zuweisen
+ this.email = email;
+ this.erstelltAm = new Date();
+ }
+
+ // Methoden werden direkt im Klassenkörper definiert (ohne 'function'):
+ begruessen() {
+ return `Hallo, mein Name ist ${this.name}!`;
+ }
+}
+
+// Instanziierung:
+const user1 = new Benutzer("Anna Schmidt", "anna@dev.io");
+console.log(user1.begruessen()); // "Hallo, mein Name ist Anna Schmidt!"
+ Objektorientierte Kapselung schützt interne Zustände vor unkontrollierten Änderungen von außen. Mit get und set steuerst du Lese- und Schreibzugriffe und validierst eingehende Werte:
+
🏭 Didaktische Analogie: Der Bauplan & die Fabrikstraße
+
+ Stelle dir eine Klasse wie den CAD-Konstruktionsplan eines Autos in einer Autofabrik vor.
+ Jedes Mal, wenn du new Auto(...) ausführst, rollt ein eigenständiges Fahrzeug vom Fließband auf die Straße (die Instanz).
+ Das Auto besitzt eigene Farben und Seriennummern (Instanzvariablen), bedient sich über Pedale und Lenkrad (Methoden), und die Maschinen in der Fabrikhalle selbst sind die statischen Werkzeuge.
+
+
+
+
+
+
+
🧬 3. Vererbung mit extends & Elternaufruf super()
+
+ Klassen können Eigenschaften und Methoden von Basisklassen erben. Mit extends leitet man eine Kindklasse ab und mit super(...) ruft man den Konstruktor oder die Methoden der Elternklasse auf:
+
+ Statische Methoden gehören zur Klasse selbst und nicht zu einer einzelnen Instanz. Man ruft sie direkt über KlassenName.methode() auf. Sie eignen sich hervorragend für Hilfsfunktionen und Fabrikmuster (Factory Methods):
+
+
+
class ElektroAuto extends Fahrzeug {
+ // ... Konstruktor und Methoden ...
+
+ // Statische Fabrikmethode erzeugt vorkonfigurierte Instanzen:
+ static erstelleStandardTesla() {
+ return new ElektroAuto("Tesla", "Model 3", 2024, 75);
+ }
+}
+
+// Aufruf direkt an der Klasse, ohne vorheriges 'new':
+const meinTesla = ElektroAuto.erstelleStandardTesla();
+console.log(meinTesla.beschreibung());
+// "Tesla Model 3 (2024) [Elektro: 75 kWh]"
+
+
+
+
+
📑 5. Cheat-Sheet: JavaScript OOP & ES6 Klassen
+
+
+
+
+
Konzept
+
Syntax-Beispiel
+
Bedeutung
+
+
+
+
+
Klassendeklaration
+
class Auto { ... }
+
Definiert einen neuen Objekttypen-Bauplan
+
+
+
Konstruktor
+
constructor(farbe) { this.farbe = farbe; }
+
Initialisiert neue Objekt-Instanzen
+
+
+
Getter / Setter
+
get saldo() { return this._saldo; }
+
Kontrollierter Lese- und Schreibzugriff
+
+
+
Vererbung
+
class Kind extends Basis { ... }
+
Übernimmt Attribute & Methoden der Elternklasse
+
+
+
Super-Aufruf
+
super(arg1, arg2);
+
Führt den Eltern-Konstruktor oder Eltern-Methoden aus
+
+
+
Statische Methode
+
static erzeuge() { return new X(); }
+
Klassenbezogene Hilfs- oder Fabrikmethode
+
+
+
Typ-Prüfung
+
obj instanceof Klasse
+
Prüft, ob obj aus Klasse oder deren Vererbungslinie stammt
+
+
+
+
+
+
+
+
+
🎯 6. Aufgaben & Checkliste in aufgabe.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
💡 Tipp für Teilziel 3 & 4
+
+ Vergiss nicht, in der Kindklasse ElektroAuto als allererste Anweisung im Konstruktor super(marke, modell, baujahr) aufzurufen, bevor du auf this.batterieKapazitaet zugreifst!
+
+
+
diff --git a/courses/javascript/06_klassen_und_oop/musterloesung.js b/courses/javascript/06_klassen_und_oop/musterloesung.js
new file mode 100644
index 0000000..0159f45
--- /dev/null
+++ b/courses/javascript/06_klassen_und_oop/musterloesung.js
@@ -0,0 +1,74 @@
+/**
+ * 🌟 MUSTERLÖSUNG: JS 06 – KLASSEN & OBJEKTORIENTIERUNG (OOP) 🌟
+ * =============================================================
+ */
+
+class Bankkonto {
+ constructor(inhaber, startSaldo = 0) {
+ if (!inhaber || typeof inhaber !== "string") {
+ throw new Error("Inhaber muss als gültiger Text angegeben werden.");
+ }
+ if (typeof startSaldo !== "number" || startSaldo < 0 || isNaN(startSaldo)) {
+ throw new Error("Start-Saldo muss eine nicht-negative Zahl sein.");
+ }
+ this.inhaber = inhaber;
+ this._saldo = startSaldo;
+ }
+
+ get saldo() {
+ return this._saldo;
+ }
+
+ einzahlen(betrag) {
+ if (typeof betrag !== "number" || betrag <= 0 || isNaN(betrag)) {
+ throw new Error("Einzahlungsbetrag muss größer als 0 sein.");
+ }
+ this._saldo += betrag;
+ return this._saldo;
+ }
+
+ abheben(betrag) {
+ if (typeof betrag !== "number" || betrag <= 0 || isNaN(betrag)) {
+ throw new Error("Abhebebetrag muss größer als 0 sein.");
+ }
+ if (betrag > this._saldo) {
+ throw new Error("Nicht genügend Guthaben vorhanden.");
+ }
+ this._saldo -= betrag;
+ return this._saldo;
+ }
+}
+
+class Fahrzeug {
+ constructor(marke, modell, baujahr) {
+ this.marke = marke;
+ this.modell = modell;
+ this.baujahr = baujahr;
+ }
+
+ beschreibung() {
+ return `${this.marke} ${this.modell} (${this.baujahr})`;
+ }
+}
+
+class ElektroAuto extends Fahrzeug {
+ constructor(marke, modell, baujahr, batterieKapazitaet) {
+ super(marke, modell, baujahr);
+ this.batterieKapazitaet = batterieKapazitaet;
+ }
+
+ beschreibung() {
+ return `${super.beschreibung()} [Elektro: ${this.batterieKapazitaet} kWh]`;
+ }
+
+ reichweiteBerechnen(verbrauchPro100km) {
+ if (typeof verbrauchPro100km !== "number" || verbrauchPro100km <= 0 || isNaN(verbrauchPro100km)) {
+ throw new Error("Verbrauch pro 100 km muss größer als 0 sein.");
+ }
+ return (this.batterieKapazitaet / verbrauchPro100km) * 100;
+ }
+
+ static erstelleStandardTesla() {
+ return new ElektroAuto("Tesla", "Model 3", 2024, 75);
+ }
+}
diff --git a/courses/javascript/06_klassen_und_oop/test_aufgabe.js b/courses/javascript/06_klassen_und_oop/test_aufgabe.js
new file mode 100644
index 0000000..aaaf3e9
--- /dev/null
+++ b/courses/javascript/06_klassen_und_oop/test_aufgabe.js
@@ -0,0 +1,103 @@
+// 🧪 JS 06: Testsuite für Klassen, OOP, Vererbung & Methoden
+
+if (typeof assert === "undefined") {
+ var assert = require("assert");
+}
+
+function pruefeFehler(fn, beschreibung) {
+ let threw = false;
+ try {
+ fn();
+ } catch (e) {
+ threw = true;
+ }
+ assert.ok(threw, `${beschreibung} sollte einen Fehler werfen!`);
+}
+
+// ==========================================
+// 1. Tests für Bankkonto
+// ==========================================
+const konto1 = new Bankkonto("Alice Schmidt");
+assert.strictEqual(konto1.inhaber, "Alice Schmidt", "Bankkonto sollte korrekten Inhaber besitzen");
+assert.strictEqual(konto1.saldo, 0, "Standard-Startsaldo sollte 0 sein");
+
+const konto2 = new Bankkonto("Bob Berger", 250);
+assert.strictEqual(konto2.saldo, 250, "Bankkonto sollte initialen Saldo von 250 haben");
+
+// Einzahlen
+const nachEinzahlung = konto2.einzahlen(150);
+assert.strictEqual(nachEinzahlung, 400, "einzahlen(150) sollte neuen Saldo 400 zurückgeben");
+assert.strictEqual(konto2.saldo, 400, "konto.saldo sollte nach Einzahlung 400 betragen");
+
+pruefeFehler(() => konto2.einzahlen(0), "einzahlen(0)");
+pruefeFehler(() => konto2.einzahlen(-50), "einzahlen(-50)");
+
+// Abheben
+const nachAbhebung = konto2.abheben(100);
+assert.strictEqual(nachAbhebung, 300, "abheben(100) sollte neuen Saldo 300 zurückgeben");
+assert.strictEqual(konto2.saldo, 300, "konto.saldo sollte nach Abhebung 300 betragen");
+
+pruefeFehler(() => konto2.abheben(0), "abheben(0)");
+pruefeFehler(() => konto2.abheben(-20), "abheben(-20)");
+pruefeFehler(() => konto2.abheben(999), "abheben(999) bei Saldo 300");
+
+// ==========================================
+// 2. Tests für Fahrzeug
+// ==========================================
+const fahrzeug = new Fahrzeug("Volkswagen", "Golf 8", 2021);
+assert.strictEqual(fahrzeug.marke, "Volkswagen", "Fahrzeug sollte Marke 'Volkswagen' besitzen");
+assert.strictEqual(fahrzeug.modell, "Golf 8", "Fahrzeug sollte Modell 'Golf 8' besitzen");
+assert.strictEqual(fahrzeug.baujahr, 2021, "Fahrzeug sollte Baujahr 2021 besitzen");
+assert.strictEqual(
+ fahrzeug.beschreibung(),
+ "Volkswagen Golf 8 (2021)",
+ "Fahrzeug.beschreibung() sollte 'Marke Modell (Baujahr)' zurückgeben"
+);
+
+// ==========================================
+// 3. Tests für ElektroAuto (Vererbung & Methoden)
+// ==========================================
+const eAuto = new ElektroAuto("Hyundai", "Ioniq 5", 2023, 77.4);
+
+assert.ok(eAuto instanceof ElektroAuto, "eAuto sollte Instanz von ElektroAuto sein");
+assert.ok(eAuto instanceof Fahrzeug, "eAuto sollte via Vererbung Instanz von Fahrzeug sein");
+
+assert.strictEqual(eAuto.marke, "Hyundai", "ElektroAuto erbt marke");
+assert.strictEqual(eAuto.modell, "Ioniq 5", "ElektroAuto erbt modell");
+assert.strictEqual(eAuto.baujahr, 2023, "ElektroAuto erbt baujahr");
+assert.strictEqual(eAuto.batterieKapazitaet, 77.4, "ElektroAuto besitzt batterieKapazitaet");
+
+assert.strictEqual(
+ eAuto.beschreibung(),
+ "Hyundai Ioniq 5 (2023) [Elektro: 77.4 kWh]",
+ "ElektroAuto.beschreibung() sollte erweiterte Beschreibung liefern"
+);
+
+// Reichweitenberechnung: (77.4 kWh / 15.48 kWh/100km) * 100 = 500 km
+const reichweite = eAuto.reichweiteBerechnen(15.48);
+assert.strictEqual(Math.round(reichweite), 500, "reichweiteBerechnen(15.48) sollte 500 km ergeben");
+
+pruefeFehler(() => eAuto.reichweiteBerechnen(0), "reichweiteBerechnen(0)");
+pruefeFehler(() => eAuto.reichweiteBerechnen(-10), "reichweiteBerechnen(-10)");
+
+// ==========================================
+// 4. Tests für Statische Fabrikmethode
+// ==========================================
+const standardTesla = ElektroAuto.erstelleStandardTesla();
+assert.ok(standardTesla instanceof ElektroAuto, "erstelleStandardTesla() muss ein ElektroAuto zurückgeben");
+assert.strictEqual(standardTesla.marke, "Tesla", "Tesla Marke sollte 'Tesla' sein");
+assert.strictEqual(standardTesla.modell, "Model 3", "Tesla Modell sollte 'Model 3' sein");
+assert.strictEqual(standardTesla.baujahr, 2024, "Tesla Baujahr sollte 2024 sein");
+assert.strictEqual(standardTesla.batterieKapazitaet, 75, "Tesla Batteriekapazität sollte 75 sein");
+assert.strictEqual(
+ standardTesla.beschreibung(),
+ "Tesla Model 3 (2024) [Elektro: 75 kWh]",
+ "Tesla beschreibung() prüfen"
+);
+assert.strictEqual(
+ standardTesla.reichweiteBerechnen(15),
+ 500,
+ "Tesla Reichweite bei 15 kWh/100km sollte (75/15)*100 = 500 km sein"
+);
+
+console.log("✅ Alle Tests in JS 06 (Klassen & OOP) erfolgreich bestanden!");
diff --git a/courses/javascript/07_regex_und_textverarbeitung/README.md b/courses/javascript/07_regex_und_textverarbeitung/README.md
new file mode 100644
index 0000000..e2f3347
--- /dev/null
+++ b/courses/javascript/07_regex_und_textverarbeitung/README.md
@@ -0,0 +1,98 @@
+# 🌐 JS 07: Reguläre Ausdrücke & Textverarbeitung
+
+Willkommen zu **Modul 07** des JavaScript-Kurses! In diesem Modul lernst du die Kunst des **Pattern Matchings** und der **Textanalyse** mit Regulären Ausdrücken (RegExp).
+
+---
+
+## 🎯 Lernziele
+
+Nach diesem Modul kannst du:
+1. **Reguläre Ausdrücke (RegExp)** in JavaScript sowohl als Literal (`/muster/flags`) als auch über den Konstruktor (`new RegExp(...)`) erstellen.
+2. **Flags** wie `g` (global), `i` (case-insensitive) und `m` (multiline) gezielt steuern.
+3. **Metazeichen** (`\d`, `\w`, `\s`, `.`, `^`, `$`) und **Quantifizierer** (`+`, `*`, `?`, `{min,max}`) souverän kombinieren.
+4. Strings mit **`regex.test()`** validieren und mit **`str.match()`** Fundstellen extrahieren.
+5. Texte mit **`str.replace()`** und **Capturing Groups (`$1`, `$2`)** formatieren und sensible Daten maskieren.
+6. Komplexe Validierungsregeln wie **Passwort- und E-Mail-Prüfungen** sauber umsetzen.
+
+---
+
+## 💡 Theoretische Grundlagen
+
+### 1. Die zwei Wege zur RegExp-Erstellung
+
+```javascript
+// 1. Literal (schnell, statisch kompiliert, Standardfall):
+const regexLiteral = /^[A-Z][a-z]+$/;
+
+// 2. RegExp-Objekt (dynamisch, erlaubt Variablen im Suchmuster):
+const userKeyword = "admin";
+const regexDynamic = new RegExp(`^${userKeyword}$`, "i");
+```
+
+### 2. Metazeichen-Übersicht
+
+| Metazeichen | Bedeutung | Beispiel-Treffer |
+| :--- | :--- | :--- |
+| `\d` | Ziffer (0-9) | `"5"`, `"9"` |
+| `\D` | Keine Ziffer | `"a"`, `"!"` |
+| `\w` | Wortzeichen (`[a-zA-Z0-9_]`) | `"x"`, `"7"`, `"_"` |
+| `\W` | Kein Wortzeichen | `" "`, `"@"`, `"-"` |
+| `\s` | Whitespace (Leerzeichen, Tab, Umbruch) | `" "`, `"\t"`, `"\n"` |
+| `.` | Beliebiges Zeichen (außer Umbruch) | `"a"`, `"8"`, `"%"` |
+| `^` | Textanfang | `/^Start/` |
+| `$` | Textende | `/Ende$/` |
+
+### 3. Quantifizierer (Häufigkeiten)
+
+| Quantifizierer | Bedeutung | Beispiel |
+| :--- | :--- | :--- |
+| `+` | Mindestens 1 Mal (1 bis unendlich) | `\d+` matcht `"1234"` |
+| `*` | 0 bis unendlich (optional) | `a*` matcht `""` oder `"aaa"` |
+| `?` | 0 oder 1 Mal (optional) | `https?` matcht `"http"` & `"https"` |
+| `{n}` | Exakt `n` Mal | `\d{4}` matcht `"2024"` |
+| `{min,max}` | Zwischen `min` und `max` Mal | `\d{2,4}` |
+| `{min,}` | Mindestens `min` Mal | `.{8,}` (mind. 8 Zeichen) |
+
+### 4. Capturing Groups & Maskierung mit `replace`
+
+Runde Klammern `( ... )` bilden Gruppen. Im Ersetzungs-String kann mit `$1`, `$2` usw. darauf zugegriffen werden:
+
+```javascript
+// Telefonnummer formatieren:
+const nummer = "01711234567";
+const formatiert = nummer.replace(/(\d{4})(\d+)/, "$1-$2");
+console.log(formatiert); // "0171-1234567"
+```
+
+---
+
+## 🕵️ Didaktische Analogie: Der Muster-Detektiv & Schablonen-Scanner
+
+Ein Regulärer Ausdruck ist wie ein **Röntgengerät mit Lochmaske**:
+- Du legst das Dokument (den Fließtext) auf den Scanner.
+- Die Lochmaske blendet alle irrelevanten Wörter aus.
+- Nur Abschnitte, die exakt in die Aussparungen passen (z. B. 4er-Zahlenblöcke für Kreditkarten oder das `@`-Zeichen mit Domain), leuchten grün auf.
+
+---
+
+## 🎯 Aufgabenübersicht in `aufgabe.js`
+
+1. **TODO 1: `istGueltigeEmail(email)`**:
+ - Prüft E-Mails auf das Format `name@domain.tld` (mind. 2-stellige TLD).
+2. **TODO 2: `findeTelefonnummern(text)`**:
+ - Findet Telefonnummern wie `+49 170 1234567`, `0171-1234567` etc.
+3. **TODO 3: `extrahiereHashtags(text)`**:
+ - Findet alle Social-Media-Tags (`#hashtag`).
+4. **TODO 4: `maskiereKreditkarte(text)`**:
+ - Anonymisiert 16-stellige Kreditkartennummern zu `****-****-****-1234`.
+5. **TODO 5: `istStarkesPasswort(passwort)`**:
+ - Prüft Passwortstärke (mind. 8 Zeichen, Klein-, Großbuchstaben, Ziffer, Sonderzeichen).
+
+---
+
+## 🧪 Tests ausführen
+
+Führe die Testsuite in der Web-IDE oder lokal aus:
+```bash
+# In der Web-IDE: Klicke auf 'Code ausführen & testen'
+```
diff --git a/courses/javascript/07_regex_und_textverarbeitung/aufgabe.js b/courses/javascript/07_regex_und_textverarbeitung/aufgabe.js
new file mode 100644
index 0000000..b7e5bae
--- /dev/null
+++ b/courses/javascript/07_regex_und_textverarbeitung/aufgabe.js
@@ -0,0 +1,61 @@
+/**
+ * 🌐 JS 07: REGULÄRE AUSDRÜCKE & TEXTVERARBEITUNG 🌐
+ * ==================================================
+ * RegExp-Muster, Flags, Metazeichen, Quantifizierer
+ * und praktische Methoden (test, match, replace).
+ */
+
+// 🎯 TEILZIEL 1 (TODO 1): istGueltigeEmail(email)
+// Prüfe, ob der gegebene String eine syntaktisch gültige E-Mail-Adresse ist.
+// Kriterien:
+// - Startet mit mind. 1 Zeichen (Buchstaben, Ziffern, ., _, %, +, -)
+// - Enthält genau ein '@'
+// - Domain-Teil mit mind. 1 Zeichen (Buchstaben, Ziffern, ., -)
+// - Top-Level-Domain (TLD) mit mind. 2 Buchstaben am Ende (z. B. .de, .com, .info)
+// Rückgabe: true wenn gültig, sonst false.
+function istGueltigeEmail(email) {
+ // TODO: Regex-Muster definieren und mit .test() prüfen
+ return false;
+}
+
+// 🎯 TEILZIEL 2 (TODO 2): findeTelefonnummern(text)
+// Finde alle Telefonnummern in einem beliebigen Fließtext.
+// Unterstütze Formate wie "+49 170 1234567", "0171-1234567", "030 / 123456" oder "+49(0)30 123456".
+// Rückgabe: Ein Array mit allen gefundenen Telefonnummern-Strings (oder [] wenn keine gefunden).
+function findeTelefonnummern(text) {
+ // TODO: Regex mit Global-Flag 'g' erstellen und text.match(regex) nutzen
+ return [];
+}
+
+// 🎯 TEILZIEL 3 (TODO 3): extrahiereHashtags(text)
+// Extrahiere alle Social-Media-Hashtags aus einem Text (z. B. "#javascript", "#webdev2024").
+// Ein Hashtag beginnt mit '#' gefolgt von einem oder mehreren Wortzeichen (Buchstaben, Zahlen, Unterstrich).
+// Rückgabe: Array aller Hashtag-Strings inklusive Raute '#' (oder [] wenn keine vorhanden).
+function extrahiereHashtags(text) {
+ // TODO: Hashtags mit Regex matchen und als Array zurückgeben
+ return [];
+}
+
+// 🎯 TEILZIEL 4 (TODO 4): maskiereKreditkarte(text)
+// Maskiere 16-stellige Kreditkartennummern aus Sicherheitsgründen im Text.
+// Die Nummern können mit Bindestrichen ("1234-5678-9012-3456"), Leerzeichen ("1234 5678 9012 3456")
+// oder durchgehend ("1234567890123456") geschrieben sein.
+// Ersetze jede 16-stellige Kreditkarte durch das Format: "****-****-****-XXXX" (wobei XXXX die letzten 4 Ziffern sind).
+// Rückgabe: Der bereinigte/maskierte Text-String.
+function maskiereKreditkarte(text) {
+ // TODO: Mit regex.replace() und Capturing Group $1 für die letzten 4 Ziffern maskieren
+ return "";
+}
+
+// 🎯 TEILZIEL 5 (TODO 5): istStarkesPasswort(passwort)
+// Validiere, ob ein Passwort den modernen Sicherheitsanforderungen entspricht:
+// 1. Mindestens 8 Zeichen lang
+// 2. Mindestens ein Kleinbuchstabe (a-z)
+// 3. Mindestens ein Großbuchstabe (A-Z)
+// 4. Mindestens eine Zahl (0-9)
+// 5. Mindestens ein Sonderzeichen (z. B. ! @ # $ % ^ & * ( ) _ + - = [ ] { } ; ' : " \ | , . < > / ? ~)
+// Rückgabe: true wenn stark, sonst false.
+function istStarkesPasswort(passwort) {
+ // TODO: Bedingungen mit Regex prüfen
+ return false;
+}
diff --git a/courses/javascript/07_regex_und_textverarbeitung/index.html b/courses/javascript/07_regex_und_textverarbeitung/index.html
new file mode 100644
index 0000000..2f2483e
--- /dev/null
+++ b/courses/javascript/07_regex_und_textverarbeitung/index.html
@@ -0,0 +1,239 @@
+
+
+
+
+
+ JS 07: Reguläre Ausdrücke & Textverarbeitung
+
+
+
+
+
+
+
+
+
🌐 Lehrpfad 2: Moderne Datenmodelle & OOP
+
JS 07: Reguläre Ausdrücke & Textverarbeitung
+
Lerne mächtiges Pattern-Matching, Text-Mining, Datenvalidierung und Maskierung mit JavaScript RegExp.
+
+
+
+
+
+
+
🔍 1. Grundlagen: RegExp-Syntax & Flags
+
+ Ein Regulärer Ausdruck (RegExp) ist ein Suchmuster zur Textanalyse, Validierung und Transformation. In JavaScript erstellst du Regex entweder als Literal mit Schrägstrichen /muster/flags oder über das RegExp-Objekt:
+
g (global): Findet alle Vorkommen im gesamten Text (nicht nur das erste).
+
i (case-insensitive): Ignoriert Unterschiede zwischen Groß- und Kleinschreibung.
+
m (multiline): Behandelt Zeilenumbrüche für ^ (Zeilenanfang) und $ (Zeilenende).
+
+
+
+
+
+
🧩 2. Metazeichen & Quantifizierer
+
RegExp nutzt spezielle Steuerzeichen, um Zeichenklassen und Wiederholungen zu beschreiben:
+
+
+
+
\d
+
Beliebige Ziffer (0-9)
+
+
+
\w
+
Wortzeichen (a-z, A-Z, 0-9, _)
+
+
+
\s
+
Leerzeichen, Tabulator, Zeilenumbruch
+
+
+
^ & $
+
Textanfang (^) & Textende ($)
+
+
+
+
+
1 oder mehrmals (mind. 1)
+
+
+
*
+
0 oder mehrmals (optional)
+
+
+
?
+
0 oder 1 Mal (optional)
+
+
+
{min,max}
+
Wiederholungsanzahl z. B. {8,} (mind. 8)
+
+
+
+
+
🕵️ Didaktische Analogie: Der Muster-Detektiv & Schablonen-Scanner
+
+ Stelle dir einen Regulären Ausdruck wie eine transparente Loch-Schablone vor, die du über eine riesige Zeitungswand legst.
+ Die Löcher der Schablone sind so gestanzt, dass überall dort, wo eine E-Mail-Adresse, IBAN oder Telefonnummer steht, der Text sofort ins Auge springt, während der restliche Fließtext ausgeblendet wird.
+
+
+
+
+
+
+
⚙️ 3. RegExp-Methoden in JavaScript
+
Je nachdem, ob du prüfen, extrahieren oder ersetzen willst, bietet JS passende Methoden:
+
+
const text = "Support: info@code.de oder sales@code.de. Tel: 0171-1234567.";
+
+// 1. regex.test(str) -> boolean (perfekt für Validierung):
+const hatEmail = /@code\.de/.test(text); // true
+
+// 2. str.match(regex) -> Array mit allen Treffern (mit Flag 'g'):
+const emails = text.match(/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g);
+console.log(emails); // ["info@code.de", "sales@code.de"]
+
+// 3. str.replace(regex, neu) -> String ersetzen mit Capturing Groups:
+const maskiert = text.replace(/(\d{4})-(\d+)/g, "$1-XXXX");
+console.log(maskiert); // "... Tel: 0171-XXXX."
+
+
+
+
+
📑 4. Cheat-Sheet: RegExp Syntax & Methoden
+
+
+
+
+
Syntax / Methode
+
Beispiel
+
Beschreibung
+
+
+
+
+
/muster/flags
+
/\d+/g
+
Regex Literal mit Flags
+
+
+
regex.test(str)
+
/^[A-Z]/.test("Hallo") // true
+
Prüft, ob Muster im String vorkommt (liefert true/false)
+
+
+
str.match(regex)
+
"12 a 34".match(/\d+/g) // ["12", "34"]
+
Liefert Array aller Fundstellen
+
+
+
str.replace(regex, sub)
+
"Hi".replace(/i/, "allo")
+
Ersetzt Treffer durch Ersatz-String oder Funktion
+
+
+
[a-z0-9]
+
[A-Z] // Großbuchstabe
+
Zeichenklasse (Matcht ein beliebiges Zeichen im Bereich)
+
+
+
(gruppe) & $1
+
str.replace(/(\d+)/, "Nr: $1")
+
Capturing Group zum Wiederverwenden von Teiltreffern
+
+
+
+
+
+
+
+
+
🎯 5. Aufgaben & Checkliste in aufgabe.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
💡 Tipp für Teilziel 4
+
+ In str.replace(regex, replacement) kannst du runde Klammern (\d{4}) für die letzten 4 Ziffern verwenden und diese mit $1 im Ersatz-String einfügen!
+
+
+
diff --git a/courses/javascript/07_regex_und_textverarbeitung/musterloesung.js b/courses/javascript/07_regex_und_textverarbeitung/musterloesung.js
new file mode 100644
index 0000000..66fca55
--- /dev/null
+++ b/courses/javascript/07_regex_und_textverarbeitung/musterloesung.js
@@ -0,0 +1,42 @@
+/**
+ * 🌟 MUSTERLÖSUNG: JS 07 – REGEX & TEXTVERARBEITUNG 🌟
+ * ====================================================
+ */
+
+function istGueltigeEmail(email) {
+ if (!email || typeof email !== "string") return false;
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
+ return emailRegex.test(email);
+}
+
+function findeTelefonnummern(text) {
+ if (!text || typeof text !== "string") return [];
+ const telefonRegex = /(?:\+49|\+?\d{1,3}|0)[\d\s\/\-\(\)]{6,}\d/g;
+ const matches = text.match(telefonRegex);
+ return matches ? matches.map((m) => m.trim()) : [];
+}
+
+function extrahiereHashtags(text) {
+ if (!text || typeof text !== "string") return [];
+ const hashtagRegex = /#[a-zA-Z0-9_äöüÄÖÜß]+/g;
+ const matches = text.match(hashtagRegex);
+ return matches || [];
+}
+
+function maskiereKreditkarte(text) {
+ if (!text || typeof text !== "string") return "";
+ const ccRegex = /\b\d{4}[ -]?\d{4}[ -]?\d{4}[ -]?(\d{4})\b/g;
+ return text.replace(ccRegex, "****-****-****-$1");
+}
+
+function istStarkesPasswort(passwort) {
+ if (!passwort || typeof passwort !== "string" || passwort.length < 8) {
+ return false;
+ }
+ const hatKlein = /[a-z]/.test(passwort);
+ const hatGross = /[A-Z]/.test(passwort);
+ const hatZahl = /\d/.test(passwort);
+ const hatSonderzeichen = /[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~`§]/.test(passwort);
+
+ return hatKlein && hatGross && hatZahl && hatSonderzeichen;
+}
diff --git a/courses/javascript/07_regex_und_textverarbeitung/test_aufgabe.js b/courses/javascript/07_regex_und_textverarbeitung/test_aufgabe.js
new file mode 100644
index 0000000..9ab2d52
--- /dev/null
+++ b/courses/javascript/07_regex_und_textverarbeitung/test_aufgabe.js
@@ -0,0 +1,93 @@
+// 🧪 JS 07: Testsuite für Reguläre Ausdrücke & Textverarbeitung
+
+if (typeof assert === "undefined") {
+ var assert = require("assert");
+}
+
+// ==========================================
+// 1. Tests für istGueltigeEmail
+// ==========================================
+assert.strictEqual(istGueltigeEmail("max@mustermann.de"), true, "max@mustermann.de sollte gültig sein");
+assert.strictEqual(istGueltigeEmail("user.name+tag@sub.domain.org"), true, "E-Mail mit Subdomain und Plus-Tag sollte gültig sein");
+assert.strictEqual(istGueltigeEmail("dev123@code.io"), true, "dev123@code.io sollte gültig sein");
+
+assert.strictEqual(istGueltigeEmail("max@domain"), false, "E-Mail ohne TLD (.de, .com) sollte ungültig sein");
+assert.strictEqual(istGueltigeEmail("@domain.com"), false, "E-Mail ohne Benutzername sollte ungültig sein");
+assert.strictEqual(istGueltigeEmail("keine-email"), false, "Reiner Text sollte ungültig sein");
+assert.strictEqual(istGueltigeEmail(""), false, "Leerer String sollte ungültig sein");
+assert.strictEqual(istGueltigeEmail(null), false, "null sollte ungültig sein");
+
+// ==========================================
+// 2. Tests für findeTelefonnummern
+// ==========================================
+const textMitNummern = "Kontakt: +49 170 1234567 oder per Festnetz 030 / 123456. Kein Datum 2024.";
+const nummern = findeTelefonnummern(textMitNummern);
+assert.deepStrictEqual(
+ nummern,
+ ["+49 170 1234567", "030 / 123456"],
+ "Sollte Mobil- und Festnetznummern finden"
+);
+
+const mobilText = "Mobil: 0171-1234567 und 089 98765432.";
+assert.deepStrictEqual(
+ findeTelefonnummern(mobilText),
+ ["0171-1234567", "089 98765432"],
+ "Sollte Bindestrich- und Leerzeichen-Format erkennen"
+);
+
+assert.deepStrictEqual(findeTelefonnummern("Keine Telefonnummer hier im Jahr 2025!"), [], "Sollte leeres Array bei keinen Treffern liefern");
+assert.deepStrictEqual(findeTelefonnummern(""), [], "Leerer Text sollte [] liefern");
+
+// ==========================================
+// 3. Tests für extrahiereHashtags
+// ==========================================
+const post = "Lerne #javascript und #webdev mit modernen #es6_features!";
+const tags = extrahiereHashtags(post);
+assert.deepStrictEqual(
+ tags,
+ ["#javascript", "#webdev", "#es6_features"],
+ "Sollte alle Hashtags im Text finden"
+);
+
+assert.deepStrictEqual(extrahiereHashtags("Ein normaler Satz ohne Tags."), [], "Ohne Hashtags sollte [] zurückkommen");
+assert.deepStrictEqual(extrahiereHashtags(""), [], "Leerer String sollte [] zurückgeben");
+
+// ==========================================
+// 4. Tests für maskiereKreditkarte
+// ==========================================
+const kkText = "Meine Karte ist 1234-5678-9012-3456.";
+assert.strictEqual(
+ maskiereKreditkarte(kkText),
+ "Meine Karte ist ****-****-****-3456.",
+ "16-stellige Karte mit Bindestrichen sollte maskiert werden"
+);
+
+const kkText2 = "Karten: 1111 2222 3333 4444 und 5555666677778888";
+assert.strictEqual(
+ maskiereKreditkarte(kkText2),
+ "Karten: ****-****-****-4444 und ****-****-****-8888",
+ "Mehrere Karten mit Leerzeichen und durchgehend sollten maskiert werden"
+);
+
+assert.strictEqual(
+ maskiereKreditkarte("Keine Kreditkarte vorhanden."),
+ "Keine Kreditkarte vorhanden.",
+ "Text ohne Kreditkarten darf nicht verändert werden"
+);
+
+// ==========================================
+// 5. Tests für istStarkesPasswort
+// ==========================================
+assert.strictEqual(istStarkesPasswort("Sicher123!"), true, "Sicher123! sollte stark sein");
+assert.strictEqual(istStarkesPasswort("P@ssw0rd2024"), true, "P@ssw0rd2024 sollte stark sein");
+assert.strictEqual(istStarkesPasswort("Mein#Geheimnis9"), true, "Mein#Geheimnis9 sollte stark sein");
+
+assert.strictEqual(istStarkesPasswort("S1!a"), false, "Zu kurzes Passwort (< 8 Zeichen) sollte false liefern");
+assert.strictEqual(istStarkesPasswort("sicher123!"), false, "Ohne Großbuchstaben sollte false liefern");
+assert.strictEqual(istStarkesPasswort("SICHER123!"), false, "Ohne Kleinbuchstaben sollte false liefern");
+assert.strictEqual(istStarkesPasswort("SicheresPasswort!"), false, "Ohne Zahl sollte false liefern");
+assert.strictEqual(istStarkesPasswort("Sicher12345"), false, "Ohne Sonderzeichen sollte false liefern");
+assert.strictEqual(istStarkesPasswort(""), false, "Leerer String sollte false liefern");
+assert.strictEqual(istStarkesPasswort(null), false, "null sollte false liefern");
+
+console.log("✅ Alle Tests in JS 07 (Regex & Textverarbeitung) erfolgreich bestanden!");
diff --git a/courses/javascript/08_fehlerbehandlung_und_debugging/README.md b/courses/javascript/08_fehlerbehandlung_und_debugging/README.md
new file mode 100644
index 0000000..d5a2214
--- /dev/null
+++ b/courses/javascript/08_fehlerbehandlung_und_debugging/README.md
@@ -0,0 +1,106 @@
+# 🌐 JS 08: Fehlerbehandlung & Debugging
+
+Willkommen zu **Modul 08** des JavaScript-Kurses! In diesem Modul lernst du defensive Programmiertechniken, strukturierte Ausnahmebehandlung mit `try...catch...finally` und den Aufbau professioneller Fehlerklassen-Hierarchien.
+
+---
+
+## 🎯 Lernziele
+
+Nach diesem Modul kannst du:
+1. Das **`try...catch...finally`** Kontrollmuster verstehen und für fehlertolerante Anwendungen einsetzen.
+2. Gezielte Fehler mit **`throw new Error(...)`** auslösen.
+3. Eigene Fehlerklassen von **`Error`** ableiten (`class ValidierungsFehler extends Error`).
+4. Fehlerklassen hierarchisch strukturieren und mit **Zusatzattributen** (wie betroffenes Formularfeld oder Saldo) ausstatten.
+5. Im `catch`-Block unterschiedliche Fehlerursachen mittels **`instanceof`** unterscheiden und behandeln.
+
+---
+
+## 💡 Theoretische Grundlagen
+
+### 1. Das `try...catch...finally` Muster
+
+```javascript
+try {
+ // Kritischer Code:
+ const daten = JSON.parse(roheEingabe);
+ verarbeite(daten);
+} catch (error) {
+ // Behandlung:
+ console.error("Fehler abgefangen:", error.message);
+} finally {
+ // Läuft garantiert IMMER:
+ cleanup();
+}
+```
+
+### 2. Eigene Fehlerklassen definieren
+
+Standard-Fehler (`new Error(...)`) enthalten oft nicht genügend Kontext für granulare UI-Reaktionen. Durch das Ableiten eigener Klassen schaffen wir Klarheit:
+
+```javascript
+// Basis für alle Fehler dieser Domäne:
+class AnwendungsFehler extends Error {
+ constructor(message) {
+ super(message);
+ this.name = "AnwendungsFehler";
+ }
+}
+
+// Spezifischer Validierungsfehler mit Feldbezug:
+class ValidierungsFehler extends AnwendungsFehler {
+ constructor(message, feld = null) {
+ super(message);
+ this.name = "ValidierungsFehler";
+ this.feld = feld;
+ }
+}
+```
+
+### 3. Fehler differenzieren mit `instanceof`
+
+```javascript
+try {
+ fuehreOperationAus();
+} catch (err) {
+ if (err instanceof ValidierungsFehler) {
+ console.warn(`Ungültige Eingabe im Feld ${err.feld}: ${err.message}`);
+ } else if (err instanceof GuthabenFehler) {
+ console.warn(`Guthaben (${err.saldo} €) reicht nicht für ${err.betrag} € aus.`);
+ } else {
+ // Unbekannter Fehler -> Neu werfen
+ throw err;
+ }
+}
+```
+
+---
+
+## 🪂 Didaktische Analogie: Die Notbremse & der Auffang-Fallschirm
+
+- **`throw`**: Das Ziehen der Notbremse im Zug – der normale Ablauf wird sofort unterbrochen.
+- **`catch`**: Der Fallschirm, der die Passagiere vor dem Absturz bewahrt und einen sicheren Ausstieg ermöglicht.
+- **`finally`**: Der Flughafen-Boden, der am Ende des Flugs immer erreicht wird, egal wie turbulent die Reise war.
+
+---
+
+## 🎯 Aufgabenübersicht in `aufgabe.js`
+
+1. **TODO 1: Error-Klassenhierarchie**:
+ - `AnwendungsFehler extends Error`
+ - `ValidierungsFehler extends AnwendungsFehler` (mit `feld`)
+ - `GuthabenFehler extends AnwendungsFehler` (mit `saldo`, `betrag`)
+2. **TODO 2: `sichereDivision(a, b)`**:
+ - Prüft Zahlen und fängt Division durch 0 mit `AnwendungsFehler` ab.
+3. **TODO 3: `parseBenutzerJson(jsonString)`**:
+ - Fängt JSON-Syntaxfehler ab und validiert das Feld `name`.
+4. **TODO 4: `pruefeTransaktion(kontoSaldo, abhebeBetrag)`**:
+ - Prüft positive Beträge und ausreichendes Guthaben mit `GuthabenFehler`.
+
+---
+
+## 🧪 Tests ausführen
+
+Führe die Testsuite in der Web-IDE oder lokal aus:
+```bash
+# In der Web-IDE: Klicke auf 'Code ausführen & testen'
+```
diff --git a/courses/javascript/08_fehlerbehandlung_und_debugging/aufgabe.js b/courses/javascript/08_fehlerbehandlung_und_debugging/aufgabe.js
new file mode 100644
index 0000000..e31bb34
--- /dev/null
+++ b/courses/javascript/08_fehlerbehandlung_und_debugging/aufgabe.js
@@ -0,0 +1,85 @@
+/**
+ * 🌐 JS 08: FEHLERBEHANDLUNG & DEBUGGING 🌐
+ * ============================================
+ * Defensive Programmierung mit try/catch/finally,
+ * benutzerdefinierten Error-Hierarchien und instanceof.
+ */
+
+// 🎯 TEILZIEL 1 (TODO 1): Eigene Fehlerklassen ableiten
+// Erstelle eine modulare Fehler-Hierarchie:
+//
+// 1. AnwendungsFehler extends Error:
+// - constructor(message)
+// - Setzt super(message) und this.name = "AnwendungsFehler"
+//
+// 2. ValidierungsFehler extends AnwendungsFehler:
+// - constructor(message, feld = null)
+// - Setzt super(message), this.name = "ValidierungsFehler" und this.feld = feld
+//
+// 3. GuthabenFehler extends AnwendungsFehler:
+// - constructor(message, saldo = 0, betrag = 0)
+// - Setzt super(message), this.name = "GuthabenFehler", this.saldo = saldo und this.betrag = betrag
+
+class AnwendungsFehler extends Error {
+ constructor(message) {
+ // TODO: super(message) aufrufen und this.name setzen
+ super(message);
+ this.name = "AnwendungsFehler";
+ }
+}
+
+class ValidierungsFehler extends AnwendungsFehler {
+ constructor(message, feld = null) {
+ // TODO: super(message) aufrufen, this.name und this.feld setzen
+ super(message);
+ this.name = "ValidierungsFehler";
+ this.feld = feld;
+ }
+}
+
+class GuthabenFehler extends AnwendungsFehler {
+ constructor(message, saldo = 0, betrag = 0) {
+ // TODO: super(message) aufrufen, this.name, this.saldo und this.betrag setzen
+ super(message);
+ this.name = "GuthabenFehler";
+ this.saldo = saldo;
+ this.betrag = betrag;
+ }
+}
+
+// 🎯 TEILZIEL 2 (TODO 2): sichereDivision(a, b)
+// Führe eine sichere mathematische Division aus:
+// - Wenn a oder b keine gültige Zahl ist (typeof !== 'number' oder isNaN):
+// -> Wirft ValidierungsFehler("Ungültige Zahlenwerte für Division.", "parameter")
+// - Wenn b === 0:
+// -> Wirft AnwendungsFehler("Division durch Null ist nicht erlaubt.")
+// - Rückgabe: Das Ergebnis von a / b
+function sichereDivision(a, b) {
+ // TODO: Validieren, Fehler werfen oder Division berechnen
+ return 0;
+}
+
+// 🎯 TEILZIEL 3 (TODO 3): parseBenutzerJson(jsonString)
+// Parse einen JSON-String und validiere das Ergebnis mit try...catch:
+// - Fange eventuelle JSON-SyntaxError mit try...catch ab und wirf stattdessen:
+// -> ValidierungsFehler("Ungültiges JSON-Format: ...", "jsonString")
+// - Validiere das geparste Objekt:
+// - Wenn kein Objekt oder wenn die Eigenschaft 'name' (nicht-leerer String) fehlt:
+// -> Wirf ValidierungsFehler("Pflichtfeld 'name' fehlt oder ist ungültig.", "name")
+// - Rückgabe: Das validierte Benutzerobjekt
+function parseBenutzerJson(jsonString) {
+ // TODO: JSON.parse in try...catch ausführen und Felder validieren
+ return null;
+}
+
+// 🎯 TEILZIEL 4 (TODO 4): pruefeTransaktion(kontoSaldo, abhebeBetrag)
+// Prüfe und simuliere eine Kontotransaktion:
+// - Wenn kontoSaldo oder abhebeBetrag keine gültigen Zahlen sind oder abhebeBetrag <= 0:
+// -> Wirft ValidierungsFehler("Ungültiger Abhebebetrag.", "abhebeBetrag")
+// - Wenn kontoSaldo < abhebeBetrag:
+// -> Wirft GuthabenFehler("Nicht genügend Guthaben vorhanden.", kontoSaldo, abhebeBetrag)
+// - Rückgabe: Der verbleibende Saldo (kontoSaldo - abhebeBetrag)
+function pruefeTransaktion(kontoSaldo, abhebeBetrag) {
+ // TODO: Betrag & Guthaben prüfen und neuen Saldo zurückgeben
+ return 0;
+}
diff --git a/courses/javascript/08_fehlerbehandlung_und_debugging/index.html b/courses/javascript/08_fehlerbehandlung_und_debugging/index.html
new file mode 100644
index 0000000..e65a801
--- /dev/null
+++ b/courses/javascript/08_fehlerbehandlung_und_debugging/index.html
@@ -0,0 +1,251 @@
+
+
+
+
+
+ JS 08: Fehlerbehandlung & Debugging
+
+
+
+
+
+
+
+
+
🌐 Lehrpfad 2: Moderne Datenmodelle & OOP
+
JS 08: Fehlerbehandlung & Debugging
+
Schreibe robusten und defensiven JavaScript-Code mit try/catch/finally, benutzerdefinierten Fehler-Klassen und instanceof.
+
+
+
+
+
+
+
🛑 1. Das try...catch...finally Kontrollmuster
+
+ Laufzeitfehler bringen ungesicherte Programme sofort zum Absturz. Mit try...catch fängst du Ausnahmen sicher ab und ermöglichst deiner Webanwendung eine kontrollierte Fehlerbehandlung:
+
+
+
try {
+ // 1. Code ausführen, der fehlschlagen könnte:
+ const daten = JSON.parse(roheEingabe);
+ verarbeite(daten);
+} catch (error) {
+ // 2. Wird nur ausgeführt, wenn im try-Block ein Fehler aufgetreten ist:
+ console.error("Fehler aufgetreten:", error.name, error.message);
+} finally {
+ // 3. Wird IMMER ausgeführt (z. B. für Aufräumarbeiten, Lade-Spinner stoppen):
+ schliesseLadeAnzeige();
+}
+
+
+
📊 Kontrollfluss-Diagramm
+
+
1. try { ... } (Code ausführen)
+
↓ Tritt Fehler auf?
+
+
✅ Nein: Normal weiter
+
❌ Ja: catch(err) { ... }
+
+
↓
+
3. finally { ... } (Aufräumen – läuft immer!)
+
+
+
+
+
+
💣 2. Fehler werfen mit throw & Eigene Error-Klassen
+
+ Mit dem throw-Befehl löst du gezielt Fehler aus. Statt generischer Strings wirft man in professionellem JavaScript immer Instanzen von Error (oder abgeleiteten Klassen):
+
+
+
// Basisklasse für alle Domänenfehler der App:
+class AnwendungsFehler extends Error {
+ constructor(message) {
+ super(message);
+ this.name = "AnwendungsFehler";
+ }
+}
+
+// Spezifische Kindklasse mit Zusatzinformationen:
+class ValidierungsFehler extends AnwendungsFehler {
+ constructor(message, feld = null) {
+ super(message);
+ this.name = "ValidierungsFehler";
+ this.feld = feld; // Zusätzliche Metadaten für das UI-Highlighting
+ }
+}
+
+
+
🪂 Didaktische Analogie: Die Notbremse & der Auffang-Fallschirm
+
+ Einen Fehler mit throw new ValidierungsFehler() zu werfen, ist wie das Ziehen der Sicherheits-Notbremse im fahrenden Zug: Der normale Ablauf stoppt sofort.
+ Der catch-Block ist der Auffang-Fallschirm, der den Sturz abfedert und dafür sorgt, dass die Passagiere (deine Web-App-Benutzer) sicher landen, statt ins Nichts abzustürzen.
+ Der finally-Block ist die Landestation, die in jedem Fall erreicht wird.
+
+
+
+
+
+
+
🎯 3. Fehlerarten gezielt unterscheiden mit instanceof
+
+ Im catch-Block können unterschiedliche Fehlerarten auftreten. Mit dem instanceof-Operator kannst du gezielt auf Validierungsfehler anders reagieren als auf System- oder Netzwerkfehler:
+
+
+
try {
+ sendeZahlung(konto, 500);
+} catch (error) {
+ if (error instanceof GuthabenFehler) {
+ zeigeNutzerHinweis(`Nicht genügend Guthaben! Es fehlen ${error.betrag - error.saldo} €.`);
+ } else if (error instanceof ValidierungsFehler) {
+ markiereFormularFeldRot(error.feld, error.message);
+ } else {
+ // Unbekannter Systemfehler -> Erneut werfen oder an Monitoring-Service melden:
+ throw error;
+ }
+}
+
+
+
+
+
📑 4. Cheat-Sheet: Error Handling & Debugging
+
+
+
+
+
Befehl / Syntax
+
Beispiel
+
Wirkung
+
+
+
+
+
throw new Error(...)
+
throw new Error("Ungültig!");
+
Löst Ausnahme aus und bricht aktuellen Stack ab
+
+
+
try { } catch(e) { }
+
try { ... } catch (e) { ... }
+
Fängt Laufzeitfehler ab und verhindert Crash
+
+
+
finally { }
+
finally { cleanup(); }
+
Wird garantiert ausgeführt (mit oder ohne Fehler)
+
+
+
class X extends Error
+
class MeinFehler extends Error { }
+
Eigene typisierte Fehlerklasse mit Stacktrace
+
+
+
err instanceof X
+
if (e instanceof ValidierungsFehler)
+
Prüft die Fehlerklasse zur differenzierten Behandlung
+
+
+
+
+
+
+
+
+
🎯 5. Aufgaben & Checkliste in aufgabe.js
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
💡 Tipp für Teilziel 3
+
+ In parseBenutzerJson kannst du JSON.parse(jsonString) in einem inneren try...catch-Block aufrufen. Fange den SyntaxError ab und wirf stattdessen einen new ValidierungsFehler(..., "jsonString")!
+
Moderne Browser stellen zwei Schlüssel-Wert-Speicher zur Verfügung, die rein clientseitig ohne Server-Datenbank arbeiten:
+
+
+
+
+
+
Merkmal
+
localStorage
+
sessionStorage
+
+
+
+
+
Lebensdauer
+
Dauerhaft (überlebt Browser-Neustarts)
+
Nur für die Dauer des Browser-Tabs
+
+
+
Scope
+
Same-Origin (über alle Tabs derselben Domain)
+
Isoliert auf den einzelnen Tab
+
+
+
Speicherplatz
+
ca. 5–10 MB pro Origin
+
ca. 5 MB pro Tab
+
+
+
Typischer Einsatz
+
Theme (Dark Mode), Highscores, Entwürfe
+
Mehrschritt-Formulare, temporärer Tab-Status
+
+
+
+
+
+
+
🔐 Didaktische Analogie: Das Schließfach & das Reisetagebuch
+
localStorage ist wie ein Bankschließfach: Was du dort ablegst, wartet auch nach Jahren treu auf dich. sessionStorage ist wie ein Reisetagebuch im Zugabteil: Sobald du den Zug verlässt (Tab schließen), verbleibt es im alten Zug und eine neue Fahrt beginnt mit leeren Blättern.
Da Web Storage nur Strings akzeptiert, müssen Objekte und Arrays vor dem Speichern mit JSON.stringify() serialisiert und beim Laden mit JSON.parse() deserialisiert werden:
Wenn du localStorage.setItem("obj", { a: 1 }) ohne JSON.stringify aufrufst, ruft JavaScript automatisch .toString() auf. Im Storage landet dann der unbrauchbare String "[object Object]"!
+
+
+
+
+
+
3. 🛡️ Robuste Fehlerbehandlung mit try / catch
+
Im professionellen Umfeld kann der Inhalt von Storage durch manuelle Benutzer-Eingaben oder veraltete Formate korrupt sein. Defektes JSON wirft beim Parsen einen SyntaxError:
diff --git a/courses/javascript/05_callbacks_und_promises/musterloesung.js b/courses/javascript/11_callbacks_und_promises/musterloesung.js
similarity index 100%
rename from courses/javascript/05_callbacks_und_promises/musterloesung.js
rename to courses/javascript/11_callbacks_und_promises/musterloesung.js
diff --git a/courses/javascript/05_callbacks_und_promises/test_aufgabe.js b/courses/javascript/11_callbacks_und_promises/test_aufgabe.js
similarity index 100%
rename from courses/javascript/05_callbacks_und_promises/test_aufgabe.js
rename to courses/javascript/11_callbacks_und_promises/test_aufgabe.js
diff --git a/courses/javascript/06_async_await_und_fetch/README.md b/courses/javascript/12_async_await_und_fetch/README.md
similarity index 100%
rename from courses/javascript/06_async_await_und_fetch/README.md
rename to courses/javascript/12_async_await_und_fetch/README.md
diff --git a/courses/javascript/06_async_await_und_fetch/aufgabe.js b/courses/javascript/12_async_await_und_fetch/aufgabe.js
similarity index 100%
rename from courses/javascript/06_async_await_und_fetch/aufgabe.js
rename to courses/javascript/12_async_await_und_fetch/aufgabe.js
diff --git a/courses/javascript/06_async_await_und_fetch/index.html b/courses/javascript/12_async_await_und_fetch/index.html
similarity index 99%
rename from courses/javascript/06_async_await_und_fetch/index.html
rename to courses/javascript/12_async_await_und_fetch/index.html
index e1fc739..c1d64a7 100644
--- a/courses/javascript/06_async_await_und_fetch/index.html
+++ b/courses/javascript/12_async_await_und_fetch/index.html
@@ -178,7 +178,7 @@
Monolithischer Code gleicht einer in Stein gemeißelten Statue: Ändert man einen Arm, bricht die gesamte Skulptur. Modulare Software ist wie ein Container-Hafen: Jeder Container hat genormte Maße (Exports/Imports), arbeitet innen autark und kann beliebig ausgetauscht oder transportiert werden.
+
+
+
+
+
+
2. 🏗️ Separation of Concerns (SoC)
+
Jedes Modul hat genau eine Verantwortung. Das verhindert Nebeneffekte und macht Unittests zum Kinderspiel:
+
+
+
🏛️ Schichten-Architektur: Rechnungs-System
+
+ 1. Controller / Orchestrator (RechnungsEngine): Steuert den Ablauf, kennt aber keine Formatierungsdetails.
+
+
+ 2. Business Logic (MathModule): Rechnet mathematisch exakt, wirft Fehler bei 0-Division.
+
+
+ 3. Presentation Layer (FormatterModule): Bereitet Zahlen als "1.250,50 EUR" und Datumsangaben für Menschen auf.
+
🩻 Didaktische Analogie: Der Röntgenapparat & der Maßanzug
+
Type Guards sind wie der Flughafen-Röntgenapparat: Unbekannte Daten werden gründlich durchleuchtet, bevor sie in geschützte Bereiche gelassen werden. Utility Types sind wie der Präzisions-Maßanzug: Aus einem Basis-Schnittmuster schneidest du passgenau eine Weste (Pick) oder ein flexibles Übergangshemd (Partial).
+
+
+
+
+
+
📑 3. Cheat-Sheet: TypeScript Utility Types
+
+
+
+
+
Utility Type
+
Syntax
+
Wirkung & Einsatzgebiet
+
+
+
+
+
Partial<T>
+
Partial<UserProfile>
+
Alle Eigenschaften werden optional (?). Ideal für Updates.