Kurz und bündig Javascript-Schließungen eine Funktion ermöglichen, die Zugriff auf eine Variable das ist deklariert in einer lexikalischen Elternfunktion .
Lassen Sie sich eine genauere Erklärung geben. Um Closures zu verstehen, ist es wichtig zu wissen, wie JavaScript Variablen in den Gültigkeitsbereich bringt.
Geltungsbereiche
In JavaScript werden Geltungsbereiche mit Funktionen definiert. Jede Funktion definiert einen neuen Bereich.
Betrachten Sie das folgende Beispiel;
function f()
{//begin of scope f
var foo='hello'; //foo is declared in scope f
for(var i=0;i<2;i++){//i is declared in scope f
//the for loop is not a function, therefore we are still in scope f
var bar = 'Am I accessible?';//bar is declared in scope f
console.log(foo);
}
console.log(i);
console.log(bar);
}//end of scope f
Aufruf von f druckt
hello
hello
2
Am I Accessible?
Betrachten wir nun den Fall, dass wir eine Funktion haben g
innerhalb einer anderen Funktion definiert f
.
function f()
{//begin of scope f
function g()
{//being of scope g
/*...*/
}//end of scope g
/*...*/
}//end of scope f
Wir werden anrufen f
el lexikalische Muttergesellschaft de g
. Wie bereits erklärt, haben wir jetzt 2 Bereiche; der Bereich f
und der Umfang g
.
Aber ein Bereich liegt "innerhalb" des anderen Bereichs, ist also der Bereich der untergeordneten Funktion Teil des Bereichs der übergeordneten Funktion? Was geschieht mit den Variablen, die im Bereich der übergeordneten Funktion deklariert sind; kann ich aus dem Bereich der untergeordneten Funktion auf sie zugreifen? Genau an dieser Stelle kommen Schließungen ins Spiel.
Schließungen
In JavaScript wird die Funktion g
kann nicht nur auf alle im Geltungsbereich deklarierten Variablen zugreifen g
sondern auch auf alle im Bereich der übergeordneten Funktion deklarierten Variablen zugreifen f
.
Beachten Sie Folgendes;
function f()//lexical parent function
{//begin of scope f
var foo='hello'; //foo declared in scope f
function g()
{//being of scope g
var bar='bla'; //bar declared in scope g
console.log(foo);
}//end of scope g
g();
console.log(bar);
}//end of scope f
Aufruf von f druckt
hello
undefined
Schauen wir uns die Zeile an console.log(foo);
. An diesem Punkt sind wir in Reichweite g
und wir versuchen, auf die Variable foo
die im Geltungsbereich deklariert ist f
. Aber wie bereits erwähnt, können wir auf jede Variable zugreifen, die in einer lexikalischen Elternfunktion deklariert ist, was hier der Fall ist; g
ist der lexikalische Elternteil von f
. Daher hello
gedruckt wird.
Schauen wir uns nun die Zeile console.log(bar);
. An diesem Punkt sind wir in Reichweite f
und wir versuchen, auf die Variable bar
die im Geltungsbereich deklariert ist g
. bar
nicht im aktuellen Geltungsbereich deklariert ist und die Funktion g
ist nicht der Elternteil von f
daher bar
ist undefiniert
Tatsächlich können wir auch auf die Variablen zugreifen, die im Bereich einer lexikalischen "Großelternfunktion" deklariert sind. Wenn es also eine Funktion gibt h
die in der Funktion g
function f()
{//begin of scope f
function g()
{//being of scope g
function h()
{//being of scope h
/*...*/
}//end of scope h
/*...*/
}//end of scope g
/*...*/
}//end of scope f
entonces h
auf alle Variablen zugreifen kann, die im Geltungsbereich der Funktion h
, g
et f
. Dies geschieht mit Verschlüsse . In JavaScript Verschlüsse ermöglicht den Zugriff auf jede Variable, die in der lexikalischen übergeordneten Funktion, in der lexikalischen großelterlichen Funktion, in der lexikalischen großelterlichen Funktion usw. deklariert ist. Dies kann man als eine Umfangskette ; scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ...
bis zur letzten übergeordneten Funktion, die keinen lexikalischen Vorgänger hat.
Das Fensterobjekt
Die Kette endet jedoch nicht bei der letzten übergeordneten Funktion. Es gibt noch einen speziellen Bereich; die globale Reichweite . Jede Variable, die nicht in einer Funktion deklariert ist, gilt als im globalen Bereich deklariert. Der globale Bereich hat zwei Besonderheiten;
- jede im globalen Bereich deklarierte Variable ist zugänglich überall
- die im globalen Bereich deklarierten Variablen entsprechen den Eigenschaften der
window
Objekt.
Es gibt also genau zwei Möglichkeiten, eine Variable zu deklarieren foo
im globalen Bereich; entweder indem sie nicht in einer Funktion deklariert wird oder indem die Eigenschaft foo
des Fensterobjekts.
Beide Versuche verwenden Verschlüsse
Nachdem Sie nun eine ausführlichere Erklärung gelesen haben, dürfte klar sein, dass beide Lösungen Verschlüsse verwenden. Aber um sicher zu gehen, wollen wir einen Beweis antreten.
Lassen Sie uns eine neue Programmiersprache erstellen: JavaScript-No-Closure. Wie der Name schon sagt, ist JavaScript-No-Closure identisch mit JavaScript, außer dass es keine Closures unterstützt.
Mit anderen Worten;
var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello
Schauen wir mal, was bei der ersten Lösung mit JavaScript-No-Closure passiert;
for(var i = 0; i < 10; i++) {
(function(){
var i2 = i;
setTimeout(function(){
console.log(i2); //i2 is undefined in JavaScript-No-Closure
}, 1000)
})();
}
Daher wird Folgendes gedruckt undefined
10 Mal in JavaScript-No-Closure.
Bei der ersten Lösung wird daher die Schließung verwendet.
Schauen wir uns die zweite Lösung an;
for(var i = 0; i < 10; i++) {
setTimeout((function(i2){
return function() {
console.log(i2); //i2 is undefined in JavaScript-No-Closure
}
})(i), 1000);
}
Daher wird Folgendes gedruckt undefined
10 Mal in JavaScript-No-Closure.
Beide Lösungen verwenden Verschlüsse.
Bearbeiten: Es wird davon ausgegangen, dass diese 3 Codeschnipsel nicht im globalen Bereich definiert sind. Andernfalls werden die Variablen foo
y i
wäre an den window
Objekt und daher über die window
Objekt sowohl in JavaScript als auch in JavaScript-No-Closure.