Zurück zur Startseite

JS Closures: Lexikales Environment und Beispiele

Closures in JavaScript ermöglichen es der inneren Funktion, auf äußere Variablen zuzugreifen, nachdem die äußere Funktion beendet ist. Das Material behandelt Theorie aus dem Lambda-Kalkül, Praktiken in Schleifen, OOP-Emulation und Engine-Optimierung. Code-Beispiele für Middle/Senior-Entwickler.

Wie funktionieren Closures in JS: Code und Fallstricke bei Schleifen
Advertisement 728x90

JavaScript-Closures: Lexikalisches Umgebungsmodell und praktische Anwendungen

Ein Closure in JavaScript ist eine Kombination aus einer Funktion und ihrer lexikalischen Umgebung, wobei eine innere Funktion auch nach Abschluss der Ausführung der äußeren Funktion weiterhin Zugriff auf Variablen aus dem äußeren Gültigkeitsbereich behält. Dies ermöglicht Zustandskapselung und vorhersehbares Verhalten in asynchronen Kontexten.

Das Konzept stammt ursprünglich aus Alonzo Churchs Lambda-Kalkül. Höherwertige Funktionen geben andere Funktionen zurück oder akzeptieren sie und erfassen dabei freie Variablen – also solche, die innerhalb verwendet, aber außerhalb deklariert wurden.

Ein einfaches Zähler-Beispiel verdeutlicht das Prinzip:

Google AdInline article slot
function counter() {
  let count = 0
  return function(step) {
    count = count + step
    return count
  }
}

const myCounter = counter()
console.log(myCounter(1)) // 1
console.log(myCounter(1)) // 2
console.log(myCounter(1)) // 3

Die innere Funktion schließt über count ab und verhindert so die Garbage Collection. Es handelt sich um eine Live-Referenz: Änderungen werden in Echtzeit widergespiegelt.

Closures als OOP-Analogon

Closures emulieren private Felder und öffentliche APIs von Objekten. Ein Zähler mit Methoden:

function Counter(initialValue = 0) {
  let count = initialValue
  return {
    increment: function(step = 1) {
      return count += step
    },
    decrement: function(step = 1) {
      return count -= step
    },
    getValue: function() {
      return count
    },
    reset: function() {
      return count = initialValue
    }
  }
}

const myCounter = Counter(10)
console.log(myCounter.getValue()) // 10
console.log(myCounter.increment(5)) // 15

Eine äquivalente Implementierung mit Klassen und privaten Feldern (#count) zeigt dieselbe Logik. In Pinia/Vuex-Stores kapseln Closures reaktiven Zustand:

Google AdInline article slot
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0)
  const initValue = 0
  function increment(step) {
    count.value += step
  }
  return { count, increment }
})

Feinheiten in Schleifen und Iteratoren

Das Verhalten von Closures in Schleifen hängt von Hoisting und Blockgültigkeitsbereich ab.

Mit var – eine gemeinsame Variable für alle Iterationen:

for (var i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i) // 3, 3, 3
  }, 100)
}

Mit let – eine separate lexikalische Umgebung pro Iteration:

Google AdInline article slot
for (let i = 0; i < 3; i++) {
  setTimeout(function() {
    console.log(i) // 0, 1, 2
  }, 100)
}

Iteratoren wie forEach, map, filter erstellen automatisch Closures:

[0, 1, 2].forEach(function(i) {
  setTimeout(function() {
    console.log(i) // 0, 1, 2
  }, 100)
})

Praxis: Callbacks und Events

In asynchronem Code lösen Closures Kontextprobleme.

Ajax mit einer eindeutigen ID:

function ajaxRequest(url) {
  let requestId = Math.random().toString(36).substring(2)
  fetch(url).then(data => {
    console.log(`Request ${requestId} completed:`, data)
  }).catch(error => {
    console.log(`Request ${requestId} failed:`, error.message)
  })  
}

Event-Handler:

function clickSetup(elemId, message) {
  const elem = document.getElementById(elemId)
  elem.addEventListener('click', function() {
    console.log(message)
  })
}

Pfeilfunktionen funktionieren identisch zu regulären Funktionen.

Optimierung und Debugging

JS-Engines (V8) schließen ungenutzte Variablen aus Closures aus, um Speicher zu sparen. In DevTools (Scope → Closure) sind nur relevante Variablen sichtbar.

Wichtige Praktiken:

  • Verwenden Sie let/const in Schleifen für Blockgültigkeitsbereich.
  • Bevorzugen Sie Closures für privaten Zustand ohne Klassen.
  • Fixieren Sie in asynchronen Callbacks den Kontext frühzeitig.
  • Überwachen Sie den Speicher in langlebigen Closures.

Wichtige Erkenntnisse

  • Closure = Funktion + lexikalische Umgebung mit Live-Referenzen auf Variablen.
  • let in Schleifen erzeugt pro Iteration Closures, var nicht.
  • Emuliert OOP-private Felder, wird in Vuex/Pinia-Stores verwendet.
  • Wird von Engines optimiert: ungenutzte Variablen werden ausgeschlossen.
  • Essenziell für asynchrone Callbacks und Events.

— Editorial Team

Advertisement 728x90

Weiterlesen