JavaScript-Verschlüsse in setTimeout verwenden

8

Ich verwende setTimeout, um Rendering zu emulieren, und ich kam zu der Struktur wie folgt:

%Vor%

Hat dieser Code aufgrund der unendlichen Verschachtelung von Schließungen potenzielle Speicherlecks? Oder ist alles in Ordnung? Die einzige Lösung, die ich bisher gefunden habe, ist es, es auf übliche Weise neu zu schreiben %Vor%

Aber ich möchte Mootools Events und Klassen nicht verlieren. Aus einigen Gründen kann ich kein "Singleton" (wie window.renderer = new Renderer ();) too

verwenden     
Nanako 11.10.2011, 03:51
quelle

2 Antworten

15

Ihr Code ist in Ordnung, aber Andys Antwort ist irreführend, weil sie die Scope-Kette mit Ausführungskontext und, durch Erweiterung , Aufrufstapel .

Zuerst werden setTimeout Funktionen nicht im globalen Gültigkeitsbereich ausgeführt. Sie führen immer noch in einem Closure aus und können auf Variablen aus externen Bereichen zugreifen. Dies liegt daran, dass JavaScript den statischen Bereich verwendet. Das heißt, die Scope-Kette einer Funktion wird im Moment definiert, in dem die Funktion erstellt wird und sich niemals ändert. Die Gültigkeitsbereichskette ist eine Eigenschaft der Funktion.

Der Ausführungskontext unterscheidet sich von der Bereichskette dadurch, dass er zum Zeitpunkt des Aufrufs einer Funktion (entweder direkt - func(); - oder als Ergebnis eines Browseraufrufs, z. B. ein abgelaufenes Zeitlimit). Der Ausführungskontext setzt sich aus dem Aktivierungsobjekt (den Parametern der Funktion und lokalen Variablen), einem Verweis auf die Gültigkeitsbereichskette und dem Wert von this zusammen.

Der Aufruf-Stack kann als ein Array von Ausführungskontexten betrachtet werden. Am unteren Ende des Stapels befindet sich der globale Ausführungskontext. Jedes Mal, wenn eine Funktion aufgerufen wird, werden ihre Parameter und this -Wert in einem neuen 'Objekt' auf dem Stapel gespeichert.

Wenn wir Ihre onRender -Funktion so ändern würden, dass sie sich einfach selbst nennt ( this.onRender() ), würde der Stack schnell überlaufen. Dies liegt daran, dass die Steuerung niemals jede nachfolgende onRender -Funktion verlassen würde, was es ermöglicht, dass ihr Ausführungskontext aus dem Aufrufstapel herausgesprungen wird. Stattdessen gehen wir tiefer und tiefer mit jedem onRender , das darauf wartet, dass der nächste onRender zurückkommt, in einem unendlichen Zyklus, der nur dann unterbrochen wird, wenn der Stapel überläuft.

Bei einem Aufruf von setTimeout kehrt die Steuerung jedoch sofort zurück und kann daher die Funktion onRender verlassen, wodurch der Ausführungskontext vom Stapel entfernt und verworfen wird (um vom GC aus dem Speicher freigegeben zu werden). .

Nach Ablauf der Zeitüberschreitung initiiert der Browser einen Aufruf von onRender aus dem globalen Ausführungskontext; der Aufrufstapel ist nur zwei tief. Es gibt einen neuen Ausführungskontext, der standardmäßig den globalen Bereich als this -Wert erben würde. Deshalb müssen Sie bind für Ihr Renderer -Objekt angeben - es enthält jedoch immer noch die ursprüngliche Umfangskette, die beim ersten Definieren von onRender erstellt wurde.

Wie Sie sehen, erstellen Sie keine unendlichen Closures durch Rekursion, da Closures ( Scope-Ketten ) bei der Funktionsdefinition und nicht beim Funktionsaufruf erstellt werden. Darüber hinaus erstellen Sie keine unendlichen Ausführungskontexte, da sie verworfen werden, nachdem onRender zurückgibt.

Wir können sicherstellen, dass Sie kein Gedächtnis verlieren, indem Sie es testen . Ich ließ es 500.000 Mal laufen und bemerkte keine undichte Erinnerung. Beachten Sie, dass die maximale Aufruf-Stack-Größe ungefähr 1.000 ist (variiert je nach Browser) , sodass es definitiv nicht rekursiv ist.

    
josh3736 11.10.2011, 06:16
quelle
0

setTimeout -Funktionen werden im globalen Gültigkeitsbereich ausgeführt, sie sind sich des Bereichskontextes nicht bewusst, in dem sie instanziiert werden. Bei der Javascript-Rekursion müssen Sie darauf achten, dass Sie zu tief rekursiv arbeiten, da jeder rekursive Aufruf einen neuen Kontextkontext erstellt, der sich im Speicher aufbaut. In diesem Fall setzt setTimeout es auf den globalen Bereich zurück, so dass es sich nicht um eine technische Rekursion handelt.

edit: Diese Antwort ist falsch. Siehe Kommentare.

    
Andy Ray 11.10.2011 03:59
quelle