Beginnen wir von hier aus, wie auf MDN definiert: Schließungen sind Funktionen, die sich auf unabhängige (freie) Variablen beziehen (Variablen, die lokal verwendet werden, aber in einem umschließenden Bereich definiert sind). Mit anderen Worten: Diese Funktionen "erinnern" sich an die Umgebung, in der sie erstellt wurden.
Lexikalisches Scoping
Bedenken Sie Folgendes:
function init() {
var name = 'Mozilla'; // name is a local variable created by init
function displayName() { // displayName() is the inner function, a closure
alert(name); // use variable declared in the parent function
}
displayName();
}
init();
init() erstellt eine lokale Variable namens name und eine Funktion namens displayName(). Die Funktion displayName() ist eine innere Funktion, die innerhalb von init() definiert wird und nur innerhalb des Körpers der Funktion init() verfügbar ist. Die Funktion displayName() hat keine eigenen lokalen Variablen. Da innere Funktionen jedoch Zugriff auf die Variablen äußerer Funktionen haben, kann displayName() auf den Variablennamen zugreifen, der in der übergeordneten Funktion init() deklariert ist.
function init() {
var name = "Mozilla"; // name is a local variable created by init
function displayName() { // displayName() is the inner function, a closure
alert (name); // displayName() uses variable declared in the parent function
}
displayName();
}
init();
Führen Sie den Code aus und stellen Sie fest, dass die alert()-Anweisung innerhalb der Funktion displayName() den Wert der Variablen name, die in der übergeordneten Funktion deklariert ist, erfolgreich anzeigt. Dies ist ein Beispiel für lexikalisches Scoping, das beschreibt, wie ein Parser Variablennamen auflöst, wenn Funktionen verschachtelt sind. Das Wort "lexikalisch" bezieht sich auf die Tatsache, dass das lexikalische Scoping den Ort verwendet, an dem eine Variable im Quellcode deklariert ist, um zu bestimmen, wo diese Variable verfügbar ist. Verschachtelte Funktionen haben Zugriff auf Variablen, die in ihrem äußeren Bereich deklariert sind.
Schließung
Betrachten Sie nun das folgende Beispiel:
function makeFunc() {
var name = 'Mozilla';
function displayName() {
alert(name);
}
return displayName;
}
var myFunc = makeFunc();
myFunc();
Die Ausführung dieses Codes hat genau denselben Effekt wie das vorherige Beispiel der Funktion init(): Diesmal wird die Zeichenfolge "Mozilla" in einem JavaScript-Warnfeld angezeigt. Der Unterschied - und interessant - ist, dass die innere Funktion displayName() von der äußeren Funktion zurückgegeben wird, bevor sie ausgeführt wird.
Auf den ersten Blick mag es unintuitiv erscheinen, dass dieser Code noch funktioniert. In einigen Programmiersprachen existieren die lokalen Variablen innerhalb einer Funktion nur für die Dauer der Ausführung dieser Funktion. Sobald makeFunc() die Ausführung beendet hat, könnte man erwarten, dass die Variable name nicht mehr zugänglich ist. Da der Code jedoch weiterhin wie erwartet funktioniert, ist dies in JavaScript offensichtlich nicht der Fall.
Der Grund dafür ist, dass Funktionen in JavaScript Abschlüsse bilden. Eine Closure ist die Kombination aus einer Funktion und der lexikalischen Umgebung, in der diese Funktion deklariert wurde. Diese Umgebung besteht aus allen lokalen Variablen, die zu dem Zeitpunkt, als die Closure erstellt wurde, im Gültigkeitsbereich waren. In diesem Fall ist myFunc ein Verweis auf die Instanz der Funktion displayName, die bei der Ausführung von makeFunc erstellt wurde. Die Instanz von displayName verwaltet einen Verweis auf ihre lexikalische Umgebung, in der die Variable name existiert. Aus diesem Grund bleibt der Variablenname beim Aufruf von myFunc zur Verwendung verfügbar und "Mozilla" wird an alert übergeben.
Hier ist ein etwas interessanteres Beispiel - eine makeAdder-Funktion:
function makeAdder(x) {
return function(y) {
return x + y;
};
}
var add5 = makeAdder(5);
var add10 = makeAdder(10);
console.log(add5(2)); // 7
console.log(add10(2)); // 12
In diesem Beispiel haben wir eine Funktion makeAdder(x) definiert, die ein einzelnes Argument, x, annimmt und eine neue Funktion zurückgibt. Die Funktion, die sie zurückgibt, nimmt ein einzelnes Argument, y, und gibt die Summe von x und y zurück.
Im Wesentlichen ist makeAdder eine Funktionsfabrik - sie erzeugt Funktionen, die einen bestimmten Wert zu ihrem Argument hinzufügen können. Im obigen Beispiel verwenden wir unsere Funktionsfabrik, um zwei neue Funktionen zu erstellen - eine, die zu ihrem Argument 5 addiert, und eine, die 10 addiert.
add5 und add10 sind beides Verschlüsse. Sie haben die gleiche Funktionskörperdefinition, speichern aber unterschiedliche lexikalische Umgebungen. In der lexikalischen Umgebung von add5 ist x gleich 5, während in der lexikalischen Umgebung von add10 x gleich 10 ist.
Praktische Verschlüsse
Closures sind nützlich, weil sie es ermöglichen, einige Daten (die lexikalische Umgebung) mit einer Funktion zu verknüpfen, die auf diesen Daten operiert. Dies weist offensichtliche Parallelen zur objektorientierten Programmierung auf, bei der Objekte die Möglichkeit bieten, einige Daten (die Eigenschaften des Objekts) mit einer oder mehreren Methoden zu verknüpfen.
Folglich können Sie eine Schließung überall dort verwenden, wo Sie normalerweise ein Objekt mit nur einer einzigen Methode verwenden würden.
Situationen, in denen man dies tun möchte, sind im Web besonders häufig. Ein Großteil des Codes, den wir in Front-End-JavaScript schreiben, ist ereignisbasiert - wir definieren ein bestimmtes Verhalten und hängen es dann an ein Ereignis an, das vom Benutzer ausgelöst wird (z. B. ein Klick oder ein Tastendruck). Unser Code wird im Allgemeinen als Callback angehängt: eine einzelne Funktion, die als Reaktion auf das Ereignis ausgeführt wird.
Nehmen wir zum Beispiel an, wir möchten einer Seite einige Schaltflächen hinzufügen, mit denen die Textgröße angepasst werden kann. Eine Möglichkeit, dies zu tun, besteht darin, die Schriftgröße des body-Elements in Pixeln anzugeben und dann die Größe der anderen Elemente auf der Seite (z. B. Überschriften) mit der relativen Einheit em festzulegen:
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
Unsere interaktiven Schaltflächen für die Textgröße können die Eigenschaft "font-size" des body-Elements ändern, und die Anpassungen werden dank der relativen Einheiten auch von anderen Elementen auf der Seite übernommen. Hier ist das JavaScript:
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
size12, size14 und size16 sind nun Funktionen, die die Größe des Textes auf 12, 14 bzw. 16 Pixel ändern. Wir können sie wie folgt an Schaltflächen (in diesem Fall Links) anhängen:
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
<a href="#" id="size-12">12</a>
<a href="#" id="size-14">14</a>
<a href="#" id="size-16">16</a>
function makeSizer(size) {
return function() {
document.body.style.fontSize = size + 'px';
};
}
var size12 = makeSizer(12);
var size14 = makeSizer(14);
var size16 = makeSizer(16);
document.getElementById('size-12').onclick = size12;
document.getElementById('size-14').onclick = size14;
document.getElementById('size-16').onclick = size16;
Weitere Informationen über Schließungen finden Sie auf der Link auf MDN