📦Scope, Closures, this und Prototypen

Wo ist eine Variable sichtbar? Warum „merkt“ sich eine Funktion Werte? Wer ist this? Und woher kommen Methoden, die ein Objekt gar nicht selbst hat?

🪆Scope-Kette als verschachtelte Kästen

Scopes sind lexikalisch: Wo eine Funktion im Quelltext steht, bestimmt, welche Variablen sie sieht – nicht, von wo sie aufgerufen wird.
1const app = "VisualJS";
2function aussen() {
3 const x = 1;
4 function mitte() {
5 const y = 2;
6 function innen() {
7 const z = 3;
8 console.log(app, x, y, z);
9 }
10 innen();
11 }
12 mitte();
13}
14aussen();

Klicke eine Codezeile an, um den aktuellen Scope zu wählen.

Welche Variable wird gesucht?
Suche x ab innen(): innen() → mitte() → aussen()
Gefunden in aussen() nach 3 Schritten.
🌍 Global
const appfunction aussen
3ƒ aussen()
const xfunction mitte
2ƒ mitte()
const yfunction innen
1ƒ innen()aktueller Scope
const z

Die Kästen werden aus dem Code berechnet. Gesucht wird immer von innen nach außen – nie nach innen oder zur Seite.

🎒Closures

Eine Closure ist eine Funktion zusammen mit der Umgebung, in der sie entstanden ist. Solange die Funktion existiert, bleiben die eingeschlossenen Variablen am Leben.

Zähler mit privatem Zustand

1function erzeugeZaehler() {
2 let stand = 0;
3 return function () {
4 stand++;
5 return stand;
6 };
7}
8const z1 = erzeugeZaehler();
9const z2 = erzeugeZaehler();
10console.log(z1(), z1(), z1());
11console.log(z2());
Konsole (echt ausgeführt)
wird ausgeführt …

Jeder Aufruf von erzeugeZaehler legt eine neue Umgebung mit eigenem stand an. Die zurückgegebene Funktion hält sie fest – auch nachdem erzeugeZaehler längst zurückgekehrt ist.

Schleife mit var

1for (var i = 0; i < 3; i++) {
2 setTimeout(() => console.log("var:", i), 0);
3}
Konsole (echt ausgeführt)
wird ausgeführt …

Es gibt nur ein i (Funktions- bzw. globaler Scope). Wenn die Timer laufen, ist die Schleife längst fertig und i = 3.

Schleife mit let

1for (let i = 0; i < 3; i++) {
2 setTimeout(() => console.log("let:", i), 0);
3}
Konsole (echt ausgeführt)
wird ausgeführt …

let in einem for-Kopf erzeugt pro Durchlauf eine neue Bindung. Jeder Callback schließt „sein“ i ein.

Modul-Muster: öffentlich und privat

1const konto = (() => {
2 let saldo = 0;
3 return {
4 einzahlen(betrag) { saldo += betrag; return saldo; },
5 get stand() { return saldo; },
6 };
7})();
8konto.einzahlen(50);
9console.log(konto.stand, konto.saldo);
Konsole (echt ausgeführt)
wird ausgeführt …

saldo ist nur über die Closure erreichbar – von außen gibt es keine Eigenschaft konto.saldo.

Warum var in der Schleife 3, 3, 3 ergibt

Callback 1
Callback 2
Callback 3
eine Bindung für alle: i = 3
Alle drei Callbacks zeigen auf dieselbe Variable → 3, 3, 3

👉this – wer ruft?

Bei normalen Funktionen hängt this davon ab, wie die Funktion aufgerufen wird. Wähle ein Szenario – der Code wird wirklich ausgeführt.
1const hund = {
2 name: "Bello",
3 wer() { return this; },
4};
5hund.wer();
this ist – live im Browser ausgeführt (Strict Mode):
…
Regel: Methodenaufruf obj.f() – this ist das Objekt links vom Punkt – zum Zeitpunkt des Aufrufs.
Entscheidungsbaum: Wer ist this?
Ist es eine Arrow-Funktion?nein ↓
Aufruf mit new?nein ↓
Mit bind() gebunden?nein ↓
Aufruf mit call/apply?nein ↓
Aufruf als obj.methode()?ja → Methodenaufruf obj.f()
Sonst: freier Aufruf

Die Reihenfolge ist die Vorrangregel: Arrow-Funktionen ignorieren call/apply/bind komplett, bind schlägt call, new schlägt bind.

this wird bei normalen Funktionen erst beim Aufruf festgelegt – nicht bei der Definition.

✅ Faustregel für Callbacks
Wird eine Methode als Callback weitergegeben (setTimeout(obj.f), button.addEventListener("click", obj.f)), geht der Bezug zu obj verloren. Abhilfe: Arrow-Funktion drumherum (() => obj.f()) oder obj.f.bind(obj).

🔗Die Prototypenkette

Jedes Objekt hat einen internen Verweis [[Prototype]] auf ein anderes Objekt. Fehlt eine Eigenschaft, sucht JavaScript dort weiter – bis null. Die Ebenen unten werden live mit Object.getPrototypeOf ermittelt.
1class Tier {
2 constructor(name) { this.name = name; }
3 atmen() { return this.name + " atmet"; }
4}
5class Hund extends Tier {
6 bellen() { return "Wuff!"; }
7}
8const bello = new Hund("Bello");
Kette bis null durchsucht – nicht gefunden → undefined
null – Ende der Kette