Kurz gesagt, mit Javascript Closures kann eine Funktion auf eine Variable zugreifen , die in einer lexikalisch übergeordneten Funktion deklariert ist .
Sehen wir uns eine detailliertere Erklärung an. Um Abschlüsse zu verstehen, ist es wichtig zu verstehen, wie JavaScript Variablen abdeckt.
Geltungsbereich
In JavaScript werden Bereiche 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
Aufrufen von f-Drucken
hello
hello
2
Am I Accessible?
Betrachten wir nun den Fall, dass eine Funktion g
in einer anderen Funktion definiert ist f
.
function f()
{//begin of scope f
function g()
{//being of scope g
/*...*/
}//end of scope g
/*...*/
}//end of scope f
Wir werden f
das lexikalische Elternteil von nennen g
. Wie zuvor erklärt, haben wir jetzt 2 Bereiche; den Umfang f
und den Umfang g
.
Ein Bereich liegt jedoch "innerhalb" des anderen Bereichs. Ist der Bereich der untergeordneten Funktion Teil des Bereichs der übergeordneten Funktion? Was passiert mit den Variablen, die im Bereich der übergeordneten Funktion deklariert sind? Kann ich über den Bereich der untergeordneten Funktion auf sie zugreifen? Genau hier setzen Verschlüsse an.
Verschlüsse
In JavaScript kann die Funktion g
nicht nur auf Variablen zugreifen, die im Gültigkeitsbereich deklariert sind, g
sondern auch auf Variablen, die im Gültigkeitsbereich der übergeordneten Funktion deklariert sind f
.
Betrachten 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
Aufrufen von f-Drucken
hello
undefined
Schauen wir uns die Linie an console.log(foo);
. Zu diesem Zeitpunkt befinden wir uns im Gültigkeitsbereich g
und versuchen, auf die foo
im Gültigkeitsbereich deklarierte Variable zuzugreifen f
. Wie bereits erwähnt, können wir auf jede Variable zugreifen, die in einer lexikalischen übergeordneten Funktion deklariert ist, was hier der Fall ist. g
ist das lexikalische Elternteil von f
. Daher hello
wird gedruckt.
Schauen wir uns jetzt die Linie an console.log(bar);
. Zu diesem Zeitpunkt befinden wir uns im Gültigkeitsbereich f
und versuchen, auf die bar
im Gültigkeitsbereich deklarierte Variable zuzugreifen g
. bar
wird im aktuellen Bereich nicht deklariert und die Funktion g
ist nicht das übergeordnete Element von f
, ist daher bar
undefiniert
Tatsächlich können wir auch auf die Variablen zugreifen, die im Rahmen einer lexikalischen "Grand Parent" -Funktion deklariert wurden. Daher, wenn h
innerhalb der Funktion eine Funktion definiert wäreg
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
dann h
im Rahmen der Funktion deklariert alle Variablen zuzugreifen wäre in der Lage h
, g
und f
. Dies geschieht mit Verschlüssen . In JavaScript- Schließungen können wir auf alle Variablen zugreifen, die in der lexikalischen übergeordneten Funktion, in der lexikalischen Großelternfunktion, in der lexikalischen Großelternfunktion usw. deklariert sind. Dies kann als Bereichskette angesehen werden . scope of current function -> scope of lexical parent function -> scope of lexical grand parent function -> ...
bis zur letzten übergeordneten Funktion, die kein lexikalisches übergeordnetes Element hat.
Das Fensterobjekt
Tatsächlich stoppt die Kette nicht bei der letzten übergeordneten Funktion. Es gibt noch einen besonderen Bereich; der globale Umfang . Jede Variable, die nicht in einer Funktion deklariert ist, gilt als im globalen Bereich deklariert. Der globale Geltungsbereich hat zwei Besonderheiten;
- Auf jede im globalen Bereich deklarierte Variable kann überall zugegriffen werden
- Die im globalen Bereich deklarierten Variablen entsprechen den Eigenschaften des
window
Objekts.
Daher gibt es genau zwei Möglichkeiten, eine Variable foo
im globalen Bereich zu deklarieren . entweder indem Sie es nicht in einer Funktion deklarieren oder indem Sie die Eigenschaft foo
des Fensterobjekts festlegen.
Bei beiden Versuchen werden Verschlüsse verwendet
Nachdem Sie eine detailliertere Erklärung gelesen haben, kann es offensichtlich sein, dass beide Lösungen Verschlüsse verwenden. Aber um sicher zu gehen, machen wir einen Beweis.
Lassen Sie uns eine neue Programmiersprache erstellen. JavaScript-No-Closure. Wie der Name schon sagt, ist JavaScript-No-Closure mit JavaScript identisch, außer dass Closures nicht unterstützt werden.
Mit anderen Worten;
var foo = 'hello';
function f(){console.log(foo)};
f();
//JavaScript-No-Closure prints undefined
//JavaSript prints hello
Okay, mal sehen, was mit 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 dies undefined
10 Mal in JavaScript-No-Closure gedruckt .
Daher verwendet die erste Lösung den Verschluss.
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 dies undefined
10 Mal in JavaScript-No-Closure gedruckt .
Beide Lösungen verwenden Verschlüsse.
Bearbeiten: Es wird davon ausgegangen, dass diese 3 Codefragmente nicht im globalen Bereich definiert sind. Andernfalls wären die Variablen foo
und i
an das window
Objekt gebunden und daher über das window
Objekt sowohl in JavaScript als auch in JavaScript-No-Closure zugänglich .