In JavaScript sind Funktionen normale Werte. Du kannst sie Variablen zuweisen, als Argument übergeben und aus anderen Funktionen zurückgeben. Eine Closure entsteht, wenn eine Funktion Variablen aus dem Bereich verwendet, in dem sie definiert wurde. Diese Variablen bleiben erhalten, auch wenn die äußere Funktion schon beendet ist.
Deklaration, Ausdruck, Arrow Function
Funktionsdeklarationen werden gehoistet. Du kannst sie also schon vor ihrer Definition aufrufen. Bei Funktionsausdrücken und Arrow Functions geht das nicht. Arrow Functions haben außerdem kein eigenes this und kein arguments.
function add(a, b) { return a + b; }
const mul = function (a, b) { return a * b; };
const square = (x) => x * x; // impliziter Rückgabewert
const toObj = (id) => ({ id }); // Objektliteral in Klammern!
Parameter: Defaults, Rest, Destrukturierung
Standardwerte greifen nur bei undefined, nicht bei null. Rest-Parameter sammeln die übrigen Argumente in einem echten Array.
const greet = (name = 'Welt', ...rest) =>
`Hallo ${name}! (${rest.length} weitere)`;
const connect = ({ host = 'localhost', port = 80 } = {}) =>
`${host}:${port}`;
connect({ port: 8080 }); // "localhost:8080"
connect(); // "localhost:80"
Closures
Die innere Funktion behält eine Referenz auf die Variablen der äußeren, keine Kopie. So lässt sich privater Zustand ohne Klassen umsetzen:
const createCounter = (start = 0) => {
let count = start;
return {
inc: () => ++count,
reset: () => { count = start; },
};
};
const counter = createCounter();
counter.inc(); counter.inc(); // 2
// count ist von außen nicht erreichbar
Closures sind auch die Grundlage von Callbacks, Event-Handlern, Memoisierung und Currying.
Fallstricke
varin Schleifen: Alle Closures teilen sich dieselbe Variable. Mitletbekommt jeder Durchlauf eine eigene Bindung.- Arrow Functions als Methoden:
thiszeigt dann nicht auf das Objekt. Nutze dafür die Methodenkurzschreibweiseobj = { run() { … } }. - Speicher: Closures halten referenzierte Objekte am Leben. Entferne Event-Listener, die du nicht mehr brauchst.
for (var i = 0; i < 3; i++) setTimeout(() => console.log(i)); // 3 3 3
for (let j = 0; j < 3; j++) setTimeout(() => console.log(j)); // 0 1 2