VorwortClosures stellen eine große Schwierigkeit beim Verständnis von JavaScript dar. Im Internet gibt es viele Artikel über Closures, aber nur wenige Artikel vermitteln den Leuten nach der Lektüre ein vollständiges Verständnis. Der Grund hierfür liegt meiner Meinung nach darin, dass der Abschluss eine Reihe von Wissenspunkten beinhaltet. Nur wenn Sie diese Reihe von Wissenspunkten gründlich durchschauen und ein geschlossenes Konzept erreichen, können Sie es wirklich verstehen. Heute möchte ich Closures aus einer anderen Perspektive verstehen, nämlich aus der Perspektive der Speicherzuweisung und -wiederverwendung. Ich hoffe, Ihnen dabei zu helfen, das Wissen über Closures, das Sie gesehen haben, wirklich zu verarbeiten. Gleichzeitig hoffe ich auch, dass dieser Artikel der letzte Artikel ist, den Sie über Closures lesen. Beachten Sie beim Betrachten der Bilder in diesem Artikel bitte die Richtung der Pfeile. Da dies das Prinzip ist, auf dem das Stammobjektfenster zum Durchlaufen von Speichermüll beruht, ist alles, was entlang des Pfeils gefunden werden kann, der vom Fenster ausgeht, kein Speichermüll und wird nicht wiederverwendet. Nur die Objekte, die nicht gefunden werden können, sind Speichermüll und werden zum entsprechenden Zeitpunkt von GC wiederverwendet. Einführung in ClosuresWenn Funktionen verschachtelt sind, verweist die innere Funktion auf die Variablen im äußeren Funktionsumfang, und die innere Funktion wird von den Variablen in der globalen Umgebung referenziert, wodurch ein Abschluss gebildet wird.
Bei Closures müssen wir besonders darauf achten, dass alle innerhalb einer Funktion definierten Funktionen dasselbe Closure-Objekt gemeinsam nutzen. Was bedeutet das? Sehen Sie sich den folgenden Code an: var ein Funktion b() { var c = neuer String('1') var d = neuer String('2') Funktion e() { console.log(c) } Funktion f() { console.log (d) } Rückkehr f } a = b() Im obigen Code verweist f auf die Variable d und auf f wird durch die externe Variable a verwiesen, sodass ein Abschluss gebildet wird, der bewirkt, dass die Variable d im Speicher verbleibt. Lassen Sie uns darüber nachdenken: Was ist mit der Variable c? Es scheint, dass wir C nicht verwendet haben, also sollte es nicht im Speicher bleiben. Hinzu kommt, dass c auch in Erinnerung bleiben wird. Der durch den obigen Code gebildete Abschluss enthält zwei Mitglieder, c und d. Dieses Phänomen wird als Intra-Function-Closure-Sharing bezeichnet. Warum müssen wir dieser Funktion besondere Aufmerksamkeit schenken? Aufgrund dieser Funktion ist es leicht, Code zu schreiben, der Speicherlecks verursacht, wenn wir nicht vorsichtig sind.
Sie können diese Inhalte bei Google oder Baidu suchen, um sich einen allgemeinen Überblick zu verschaffen. Als Nächstes werde ich darüber sprechen, wie man Closures aus der Perspektive des Browsers versteht, daher werde ich nicht im Detail darauf eingehen. So identifizieren Sie SpeichermüllDer Garbage Collection-Prozess moderner Browser ist relativ kompliziert. Den detaillierten Vorgang können Sie selbst googeln. Hier werde ich nur darauf eingehen, wie man Speichermüll ermittelt. Im Allgemeinen ist davon auszugehen, dass ausgehend vom Stammobjekt alles, was durch Folgen der Referenz gefunden werden kann, nicht wiederverwendet werden kann. Objekte, die durch Befolgen der Referenz nicht gefunden werden können, gelten als Müll und werden am nächsten Garbage Collection-Knoten wiederverwendet. Die Suche nach Müll kann als ein Prozess des Verfolgens von Hinweisen verstanden werden. Speicherdarstellung von ClosuresBeginnen wir mit dem einfachsten Code und schauen uns die Definition der globalen Variablen an. var a = neuer String('a') Ein solcher Code wird im Speicher wie folgt dargestellt In der globalen Umgebung wird eine Variable a definiert und ihr ein String zugewiesen. Der Pfeil zeigt eine Referenz an. Definieren wir eine andere Funktion: var a = neuer String('a') Funktion lehren() { var b = neuer String('Zeichenfolge') } Die Speicherstruktur ist wie folgt: Alles ist leicht zu verstehen. Wenn Sie genau hinschauen, werden Sie feststellen, dass es im Funktionsobjekt teach ein Attribut namens [[scopes]] gibt. Was ist das? Warum wird dieses Attribut angezeigt, nachdem die Funktion erstellt wurde? Ich bin froh, dass Sie das gefragt haben. Es ist ein wichtiger Punkt zum Verständnis von Closures. Bitte beachten Sie: Sobald eine Funktion erstellt wurde, fügt die JavaScript-Engine dem Funktionsobjekt eine Eigenschaft namens „Scope Chain“ hinzu. Diese Eigenschaft verweist auf ein Array-Objekt, das den Gültigkeitsbereich und den übergeordneten Gültigkeitsbereich der Funktion bis hin zum globalen Gültigkeitsbereich enthält. Daher kann die obige Abbildung einfach wie folgt verstanden werden: Die Teach-Funktion wird in der globalen Umgebung erstellt, sodass die Gültigkeitsbereichskette von Teach nur eine Ebene hat, nämlich den globalen Gültigkeitsbereich global
Bitte denken Sie noch einmal daran: Wenn eine Funktion ausgeführt wird, wird Platz benötigt, um einen Ausführungskontext zu erstellen. Der Ausführungskontext umfasst die Bereichskette, wenn die Funktion definiert ist, und zweitens die Variablen und Parameter, die innerhalb der Funktion definiert sind. Wenn die Funktion im aktuellen Bereich ausgeführt wird, sucht sie zuerst nach Variablen im aktuellen Bereich. Wenn sie nicht gefunden werden kann, sucht sie in der Bereichskette, wenn die Funktion definiert ist, bis sie den globalen Bereich erreicht. Wenn die Variable im globalen Bereich nicht gefunden werden kann, wird ein Fehler ausgegeben. Wir alle wissen, dass bei der Ausführung einer Funktion ein Ausführungskontext erstellt wird, der eigentlich einen Speicherplatz einer Stapelstruktur beantragt. Die lokalen Variablen in der Funktion werden in diesem Speicherplatz zugewiesen. Nachdem die Funktion ausgeführt wurde, werden die lokalen Variablen beim nächsten Garbage Collection-Knoten wiederverwendet. OK, aktualisieren wir den Code noch einmal und werfen einen Blick auf die Speicherstruktur, wenn die Funktion ausgeführt wird. var a = neuer String('a') Funktion lehren() { var b = neuer String('Zeichenfolge') } unterrichten() Der Speicher wird wie folgt dargestellt: Offensichtlich können wir sehen, dass die Funktion während der Ausführung nur eine lokale Variable zuweist und keine Beziehung zu den Variablen in der globalen Umgebung hat. Wenn wir daher vom Fensterobjekt aus entlang der Referenz (Pfeil in der Abbildung) suchen, können wir die Variable b im Ausführungskontext nicht finden. Daher wird die Variable b nach Ausführung der Funktion wiederverwendet. Lassen Sie uns den Code noch einmal aktualisieren: var a = neuer String('a') Funktion lehren() { var b = neuer String('Zeichenfolge') var say = Funktion() { console.log(b) } a = sagen } unterrichten() Der Speicher wird wie folgt dargestellt: Hinweis: Grau kennzeichnet Objekte, die nicht vom Stammobjekt aus verfolgt werden können. Reihenfolge der Funktionsausführung:
Nachdem die Funktion ausgeführt wurde, sollte die Variable b unter normalen Umständen freigegeben werden. Wir stellen jedoch fest, dass wir b finden können, indem wir entlang des Fensters suchen. Gemäß dem zuvor erwähnten Prinzip zur Ermittlung von Speichermüll ist b kein Speichermüll und kann daher nicht freigegeben werden. Aus diesem Grund behalten Closures Variablen innerhalb von Funktionen im Speicher. Aktualisieren wir den Code noch einmal und sehen wir uns die vom Abschluss gemeinsam genutzte Speicherdarstellung an: var a = neuer String('0') Funktion b() { var c = neuer String('1') var d = neuer String('2') Funktion e() { console.log(c) } Funktion f() { console.log (d) } Rückkehr f } a = b() Die grauen Grafiken sind Speichermüll und werden vom Garbage Collector wiederverwendet. Aus der obigen Abbildung ist leicht ersichtlich, dass die Funktion f zwar die Variable c nicht verwendet, c aber von der Funktion e referenziert wird, sodass die Variable c im Abschlussabschluss vorhanden ist. Die Variable c kann gefunden werden, indem man vom Fensterobjekt ausgeht, sodass die Variable c nicht freigegeben werden kann. Sie fragen sich vielleicht, wie diese Funktion zu Speicherlecks führen kann? Betrachten Sie den folgenden Code, ein klassisches Meteor-Speicherleckproblem. var t = null; var replaceThing = Funktion() { var o = t var unbenutzt = function() { wenn (o) console.log("hallo") } t = { longStr: neues Array(1000000).join('*'), irgendeineMethode: function() { console.log(1) } } } setzeInterval(Ersatzteil, 1000) Dieser Code weist einen Speicherverlust auf. Wenn Sie diesen Code im Browser ausführen, werden Sie feststellen, dass der Speicher weiter ansteigt. Obwohl GC etwas Speicher freigibt, gibt es immer noch etwas Speicher, der nicht freigegeben werden kann, und dieser steigt in einem Gradienten an. Wie unten gezeigt Diese Kurve weist darauf hin, dass ein Speicherverlust vorliegt. Mithilfe von Entwicklertools können wir analysieren, welche Objekte nicht wiederverwendet wurden. Tatsächlich kann ich Ihnen sagen, dass der Speicher, der nicht freigegeben wird, eigentlich das große Objekt ist, das wir jedes Mal erstellen. Schauen wir uns das an, indem wir ein Bild zeichnen: Das obige Bild geht davon aus, dass die Funktion replaceThing dreimal ausgeführt wird. Sie werden feststellen, dass jedes Mal, wenn wir der Variablen t ein großes Objekt zuweisen, aufgrund der gemeinsamen Nutzung von Closure das vorherige große Objekt immer noch vom Fensterobjekt aus verfolgt werden kann, sodass diese großen Objekte nicht wiederverwendet werden können. Was für uns tatsächlich wirklich nützlich ist, ist das große Objekt, das t beim letzten Mal zugewiesen wurde, sodass das vorherige Objekt einen Speicherverlust verursacht hat.
Die Lösung für dieses Problem ist auch sehr einfach. Setzen Sie bei jeder Ausführung des Codes die Variable o auf null. Sie können es versuchen. AbschlussDies ist das Ende dieses Artikels darüber, wie Browser Closures betrachten. Weitere Informationen dazu, wie Browser Closures betrachten, finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen! Das könnte Sie auch interessieren:
|
<<: display:grid in CSS3, eine Einführung in das Rasterlayout
>>: Detaillierte Erklärung zur Verwendung des Iframe-Tags (Attribute, Transparenz, adaptive Höhe)
Zabbix 12.10.2019 Chenxin siehe https://www.zabbi...
Nachdem IntelliJ IDEA ein Javaweb-Projekt mit Tom...
1. Funktionseinführung sed (Stream EDitor) ist ei...
Inhaltsverzeichnis Vorwort Einführung in Bézierku...
Inhaltsverzeichnis Projekthintergrund Start Erste...
Damit die Seitenanzeige in verschiedenen Browsern ...
Erläuterung der HTML-Tags 1. HTML-Tags Tag: !DOCT...
Das WeChat-Applet Uniapp realisiert den Löscheffe...
Konfigurieren des Startens und Herunterfahrens vo...
In diesem Artikelbeispiel wird der spezifische Co...
Die folgenden Funktionsdemonstrationen basieren a...
[mysql] Ersetzungsverwendung (Teil des Inhalts ei...
Lösung für Host „xxxx“ darf keine Verbindung zu d...
1. Bearbeiten Sie die PAM-Konfigurationsdatei sud...
Inhaltsverzeichnis 1. Kurzübersicht 2. JSON-Grund...