Der Abschluss ist in js ein relativ schwer zu verstehender Punkt, insbesondere für Leute ohne Programmierkenntnisse.
Tatsächlich gibt es bei Schließungen nur ein paar Dinge, auf die man achten sollte. Wenn man sie alle versteht, ist es nicht schwer, sie zu meistern. Lassen Sie uns über einige Grundprinzipien von Schließungen sprechen.
Das Konzept des Abschlusses
Ein Abschluss ist eine Kombination aus einer Funktion und dem Gültigkeitsbereichsobjekt in der erstellten Funktion. (Bereichsobjekte werden weiter unten besprochen)
Um es einfacher auszudrücken: „Solange eine oder mehrere Funktionen in einer Funktion verschachtelt sind, können wir sie als Abschluss bezeichnen.“
Ähnlich wie dies:
function A() { var i = 5; return function() { console.log('i = '+i); } } var a = A(); a(); // i = 5
Prinzip der Schließung
1 Wenn die lokale Variable der externen Funktion von der Schließungsfunktion aufgerufen wird, ist es wird nicht Es wird sofort recycelt, nachdem die externe Funktion die Ausführung abgeschlossen hat.
Wir wissen, dass das Betriebssystem unabhängig von der Sprache über einen Garbage-Collection-Mechanismus verfügt, um überschüssigen zugewiesenen Speicherplatz zu recyceln und so den Speicher zu reduzieren. Der Lebenszyklus einer Funktion beginnt mit dem Aufruf. Wenn der Funktionsaufruf abgeschlossen ist, werden die lokalen Variablen innerhalb der Funktion durch den Recyclingmechanismus recycelt.
Nehmen wir das obige Beispiel als Beispiel. Wenn unsere externe Funktion A aufgerufen wird, wird die lokale Variable i in A vom Betriebssystem recycelt und existiert nicht Das Ergebnis wird sein. Das ist nicht mehr der Fall, ich werde nicht recycelt. Stellen Sie sich vor, wenn ich recycelt würde, würde die zurückgegebene Funktion dann nicht undefiniert ausgeben?
Warum wurde ich nicht recycelt?
Wenn JavaScript eine Funktion ausführt, erstellt es ein Bereichsobjekt, speichert die lokalen Variablen in der Funktion (die formalen Parameter der Funktion sind ebenfalls lokale Variablen) und wird zusammen mit den an die Funktion übergebenen Variablen initialisiert .
Wenn also A aufgerufen wird, wird ein Gültigkeitsbereichsobjekt erstellt. Nennen wir es Aa. Dann sollte dieses Aa wie folgt aussehen: Aa { i: 5 }; Nachdem die A-Funktion eine Funktion zurückgegeben hat, wird A ausgeführt . Das Aa-Objekt hätte recycelt werden sollen, aber da die zurückgegebene Funktion das Attribut i von Aa verwendet, speichert die zurückgegebene Funktion einen Verweis auf Aa, sodass Aa nicht recycelt wird.
Wenn Sie also das Bereichsobjekt verstehen, können Sie verstehen, warum die lokalen Variablen der Funktion nicht sofort wiederverwendet werden, wenn der Funktionsaufruf abgeschlossen ist, wenn ein Abschluss auftritt.
Ein weiteres Beispiel:
function A(age) { var name = 'wind'; var sayHello = function() { console.log('hello, '+name+', you are '+age+' years old!'); }; return sayHello; } var wind = A(20); wind(); // hello, wind, you are 20 years old!
Können Sie sagen, was das Scope-Objekt Ww ist?
Ww{ Alter: 20; Name: 'Wind';
2. Bei jedem Aufruf einer externen Funktion wird ein neuer Abschluss generiert gegenseitigen Einfluss.
3. Derselbe Abschluss behält den letzten Zustand bei und basiert beim erneuten Aufruf auf dem letzten Zeitpunkt.
Das Gültigkeitsbereichsobjekt, das bei jedem Aufruf der externen Funktion generiert wird, ist so. Im obigen Beispiel ist das von Ihnen übergebene Parameteralter jedes Mal unterschiedlich, sodass das generierte Objekt unterschiedlich ist jedesmal.
Jedes Mal, wenn eine externe Funktion aufgerufen wird, wird ein neues Scope-Objekt generiert.
function A() { var num = 42; return function() { console.log(num++); } } var a = A(); a(); // 42 a(); // 43 var b = A(); // 重新调用A(),形成新闭包 b(); // 42
Mit diesem Code können wir zwei Dinge entdecken: Erstens ändert sich num automatisch basierend auf den ursprünglichen Wert addieren. Dies bedeutet, dass derselbe Abschluss den letzten Status beibehält und beim erneuten Aufruf auf dem letzten Zeitpunkt basiert. 2. Das Ergebnis unseres b(); ist 42, was darauf hinweist, dass es sich um einen neuen Abschluss handelt und nicht von anderen Abschlüssen beeinflusst wird.
Wir können es uns so vorstellen, als würden wir eine Seifenblase blasen. Jedes Mal, wenn ich sie blase (Aufruf einer externen Funktion), wird eine neue Seifenblase (Verschluss) erzeugt Gleichzeitig beeinflussen sich die beiden Seifenblasen nicht gegenseitig.
4. Mehrere in externen Funktionen vorhandene Funktionen „leben und sterben zusammen“
Die folgenden drei Funktionen werden gleichzeitig deklariert und können alle Operationen an den Eigenschaften (lokalen Variablen) ausführen Bereichsobjekt Zugriff und Operationen.
var fun1, fun2, fun3; function A() { var num = 42; fun1 = function() { console.log(num); } fun2 = function() { num++; } fun3 = function() { num--; } } A(); fun1(); // 42 fun2(); fun2(); fun1(); // 44 fun3(); fun1(); //43 var old = fun1; A(); fun1(); // 42 old(); // 43 上一个闭包的fun1()
Da Funktionen nicht mehrere Rückgabewerte haben können, habe ich globale Variablen verwendet. Auch hier können wir sehen, dass beim zweiten Aufruf von A() ein neuer Abschluss erstellt wird.
Wenn ein Abschluss auf eine Schleifenvariable trifft
Wenn wir über Abschlüsse sprechen, müssen wir über die Situation sprechen, wenn ein Abschluss auf eine Schleifenvariable trifft. Schauen Sie sich den folgenden Code an:
function buildArr(arr) { var result = []; for (var i = 0; i < arr.length; i++) { var item = 'item' + i; result.push( function() {console.log(item + ' ' + arr[i])} ); } return result; } var fnlist = buildArr([1,2,3]); fnlist[0](); // item2 undefined fnlist[1](); // item2 undefined fnlist[2](); // item2 undefined
Wie konnte das passieren? Die drei Ausgaben, die wir uns vorstellen, sollten item0 1, item1 2, item2 3 sein. Warum sind im zurückgegebenen Ergebnisarray drei undefinierte Elemente2 gespeichert?
Es stellt sich heraus, dass, wenn ein Abschluss auf eine Schleifenvariable trifft, der Variablenwert nach dem Ende der Schleife einheitlich gespeichert wird. Nehmen Sie unser Beispiel oben, i ist die Schleifenvariable, und wenn die Schleife endet, ist es zufällig i nach i++ 3. Arr[3] hat keinen Wert, daher ist es undefiniert. Manche Leute fragen sich vielleicht: Warum sollte es nicht item3 sein? Beachten Sie, dass in der letzten Schleife, d. h. wenn i = 2, der Wert von item2 ist. Wenn i++, i = 3, sind die Schleifenbedingungen nicht erfüllt und die Schleife endet , also ist arr[i] zu diesem Zeitpunkt arr[3] und item ist item2. Ist das verständlich? Es macht Sinn, wenn wir den Code wie folgt ändern:
function buildArr(arr) { var result = []; for (var i = 0; i < arr.length; i++) { result.push( function() {console.log('item' + i + ' ' + arr[i])} ); } return result; } var fnlist = buildArr([1,2,3]); fnlist[1](); // item3 undefined
Die Frage ist also, wie kann man das korrigieren? Schauen wir uns den Code an:
function buildArr(arr) { var result = []; for (var i = 0; i < arr.length; i++) { result.push( (function(n) { return function() { var item = 'item' + n; console.log(item + ' ' + arr[n]); } })(i)); } return result; } var fnlist = buildArr([1,2,3]); fnlist[0](); // item0 1 fnlist[1](); // item1 2 fnlist[2](); // item2 3
Wir können eine selbstausführende Funktion verwenden, um i zu binden, sodass jeder Zustand von i gespeichert wird und die Antwort dieselbe ist, wie wir erwartet haben.
Wenn wir also in Zukunft bei der Verwendung von Abschlüssen auf Schleifenvariablen stoßen, müssen wir gewöhnlich daran denken, eine selbstausführende Funktion zu verwenden, um sie zu binden.
Das Obige ist mein Verständnis von Schließungen. Wenn Sie Kommentare oder Vorschläge haben, hoffe ich, dass wir im Kommentarbereich mehr kommunizieren können. Danke und ermutigt euch gegenseitig.