Detaillierte Erläuterung von Beispielen für JS-Closure- und Garbage-Collection-Mechanismen

Detaillierte Erläuterung von Beispielen für JS-Closure- und Garbage-Collection-Mechanismen

Vorwort

Closures und Garbage Collection-Mechanismen sind beim Front-End-Lernen und -Entwickeln oft schwierig, und solche Probleme treten häufig bei Interviews auf. Dieser Artikel zeichnet meine Notizen zu diesem Aspekt während des Studiums und der Arbeit auf.

Text

1. Abschluss

Closures sind eine Schwierigkeit der Sprache Javascript, aber auch ihr Merkmal. Viele fortgeschrittene Anwendungen sind auf Closures angewiesen. Als JavaScript-Entwickler ist es sehr wichtig, Closures zu verstehen.

1.1 Was sind Verschlüsse?

Ein Closure ist eine Funktion, die auf die Variable einer anderen Funktion verweist. Sie wird generiert, wenn die innere Funktion nach außen zurückgegeben und gespeichert wird. (Die Scope-Chain-AO der inneren Funktion verwendet die AO der äußeren Funktion.)

Da auf die Variable verwiesen wird, wird sie nicht wiederverwendet und kann daher zum Kapseln einer privaten Variable verwendet werden. Unnötige Schließungen erhöhen jedoch nur den Speicherverbrauch.
Closure ist ein Mechanismus zum Schutz privater Variablen. Es bildet einen privaten Bereich, wenn eine Funktion ausgeführt wird, und schützt die darin enthaltenen privaten Variablen vor externen Eingriffen. Mit anderen Worten bedeutet Abschluss, dass die untergeordnete Funktion die lokalen Variablen und Parameter der übergeordneten Funktion verwenden kann.

1.2 Eigenschaften von Verschlüssen

①Funktionsverschachtelungsfunktion

② Die Funktion kann auf Parameter und Variablen außerhalb der Funktion verweisen

③Parameter und Variablen werden vom Garbage Collection-Mechanismus nicht recycelt

1.3 Verschlüsse verstehen

Basierend auf dem uns vertrauten Wissen über Bereichsketten betrachten wir nun das Problem der Zähler: Wie implementiert man eine Funktion, die den Zähler bei jedem Aufruf der Funktion um eins erhöht.

var Zähler=0;
 Funktion demo3(){
 Konsole.log(Zähler+=1); 
 }
 demo3();//1
 demo3();//2
 Var Zähler=5;
 demo3(); //6

Wenn der Wert des Zählers in der obigen Methode an irgendeiner Stelle geändert wird, wird der Zähler ungültig. JavaScript verwendet Closures, um dieses Problem zu lösen, d. h. Funktionen werden in Funktionen eingebettet. Sehen wir uns an, wie Closures zur Implementierung verwendet werden.

Funktion add() {
 Var-Zähler = 0;
 Rückgabefunktion plus() {
 Zähler += 1;
 Rückgabezähler
 } 
 }
 var Anzahl = add()
 konsole.log(Anzahl()) // 1
 var Zähler=100
 konsole.log(Anzahl()) // 2

Das Obige ist ein Beispiel für die Verwendung von Closures. Eine Plus-Funktion ist in die Add-Funktion eingebettet und die Zählvariable bezieht sich auf die zurückgegebene Funktion. Jedes Mal, wenn die externe Funktion Add ausgeführt wird, wird ein Stück Speicherplatz freigegeben. Die Adresse der externen Funktion ist anders und es wird eine neue Adresse erstellt. Die Plus-Funktion ist in die Add-Funktion eingebettet und erzeugt so einen lokalen Variablenzähler. Jedes Mal, wenn die Zählfunktion aufgerufen wird, wird der Wert der lokalen Variablen um eins erhöht, wodurch das eigentliche Zählerproblem realisiert wird.

1.4 Wichtigste Umsetzungsformen von Verschlüssen

Es gibt hier zwei Hauptformen des Lernens von Closures:

① Funktion als Rückgabewert, der im obigen Beispiel verwendet wird.

Funktion showName(){
 var name="xiaoming"
 Rückgabefunktion(){
  Rückgabename
 }
 }
 var name1=showName()
 Konsole.log(Name1())

Ein Abschluss ist eine Funktion, die Variablen aus einer anderen Funktion lesen kann. Ein Abschluss ist eine Brücke, die das Innere einer Funktion mit dem Äußeren der Funktion verbindet.

②Abschluss als Parameter übergeben

Variablennummer = 15
            var foo = Funktion(x){
                wenn(x>num){
                    console.log(x)
                }
              }
            Funktion foo2(fnc){
                Var-Nummer = 30
                fnc(25)
            }
            foo2(foo) // 25

Im obigen Code wird die Funktion foo als Parameter an die Funktion foo2 übergeben. Wenn foo2 ausgeführt wird, wird 25 als Parameter an foo übergeben. Zu diesem Zeitpunkt ist der Wert von num bei der Beurteilung von x>num die num im Bereich der erstellten Funktion, d. h. die globale num, nicht die num innerhalb von foo2, daher wird 25 ausgegeben.

1.5 Vor- und Nachteile von Closures

Vorteil:

① Schützen Sie die Sicherheit von Variablen innerhalb der Funktion, implementieren Sie eine Kapselung und verhindern Sie, dass Variablen in andere Umgebungen fließen und Namenskonflikte verursachen

② Behalten Sie eine Variable im Speicher für die Zwischenspeicherung bei (übermäßiger Gebrauch ist jedoch auch ein Nachteil, da er Speicher verbraucht).

③Anonyme selbstausführende Funktionen können den Speicherverbrauch reduzieren

Mangel:

① Einer der Punkte wurde oben bereits berücksichtigt, nämlich, dass die referenzierten privaten Variablen nicht zerstört werden können, was den Speicherverbrauch erhöht und Speicherlecks verursacht. Die Lösung besteht darin, sie nach Verwendung der Variablen manuell auf Null zuzuweisen.

② Zweitens führt Closures zu Leistungseinbußen, da sie domänenübergreifenden Zugriff beinhalten. Wir können die Auswirkungen auf die Ausführungsgeschwindigkeit verringern, indem wir bereichsübergreifende Variablen in lokalen Variablen speichern und dann direkt auf lokale Variablen zugreifen.

1.6 Verwendung von Verschlüssen

für (var i = 0; i < 5; i++) {
   setzeTimeout(Funktion() {
   konsole.log( i);
   }, 1000);
  }

  konsole.log(i);

Schauen wir uns die obige Frage an. Dies ist eine sehr häufige Frage, aber was wird die Ausgabe sein? Die meisten Leute wissen, dass das Ausgabeergebnis 5,5,5,5,5,5 ist. Wenn Sie genau hinschauen, werden Sie feststellen, dass diese Frage viele clevere Aspekte hat. Was ist die spezifische Ausgabereihenfolge dieser 6 5er? 5 -> 5,5,5,5,5. Wer sich mit Synchronisation und Asynchronität auskennt, kann diese Situation leicht verstehen. Lassen Sie uns auf der Grundlage der obigen Fragen darüber nachdenken, wie die sequentielle Ausgabe von 5 -> 0,1,2,3,4 erreicht werden kann.

für (var i = 0; i < 5; i++) {
 (Funktion(j) { // j = i
  setzeTimeout(Funktion() {
  konsole.log( j);
  }, 1000);
 })(ich);
 }
 konsole.log( i);
//5 -> 0,1,2,3,4

Auf diese Weise wird der For-Schleife eine anonyme Funktion hinzugefügt. Der Eingabeparameter der anonymen Funktion ist jedes Mal der Wert von i. Eine Sekunde nachdem die synchrone Funktion 5 ausgegeben hat, gibt sie weiterhin 01234 aus.

für (var i = 0; i < 5; i++) {
 setzeTimeout(Funktion(j) {
  konsole.log(j);
 }, 1000, i);
 }
 konsole.log( i);
 //5 -> 0,1,2,3,4

Wenn Sie sich die setTimeout-API genauer ansehen, werden Sie feststellen, dass sie über einen dritten Parameter verfügt, der das Problem der Übergabe von i über eine anonyme Funktion löst.

var Ausgabe = Funktion (i) {
   setzeTimeout(Funktion() {
   konsole.log(i);
   }, 1000);
  };

  für (var i = 0; i < 5; i++) {
   output(i); // Der hier übergebene i-Wert wird kopiert}

  konsole.log(i);
  //5 -> 0,1,2,3,4

Hier wird ein Abschluss verwendet, um den Funktionsausdruck als Parameter an die For-Schleife zu übergeben, wodurch auch der obige Effekt erzielt wird.

für (sei i = 0; i < 5; i++) {
      setzeTimeout(Funktion() {
          console.log(neues Datum, i);
      }, 1000);
  }
  console.log(neues Datum, i);
  //5 -> 0,1,2,3,4

Wer den Gültigkeitsbereich des Let-Blocks kennt, wird an die obige Methode denken. Aber was, wenn Sie einen Effekt wie 0 -> 1 -> 2 -> 3 -> 4 -> 5 erzielen möchten?

für (var i = 0; i < 5; i++) {
   (Funktion(j) {
   setzeTimeout(Funktion() {
    console.log(neues Datum, j);
   }, 1000 * j); // Ändern Sie hier die Timerzeit von 0~4 })(i);
  }

  setTimeout(function() { // Fügen Sie hier einen Timer hinzu und stellen Sie das Timeout auf 5 Sekunden ein console.log(new Date, i);
  }, 1000 * i);
  //0 -> 1 -> 2 -> 3 -> 4 -> 5

Es gibt auch den folgenden Code, der durch Promise implementiert wird.

const Aufgaben = [];
  for (var i = 0; i < 5; i++) { // Die Deklaration von i kann hier nicht in let geändert werden. Was muss ich tun, wenn ich sie ändern möchte?
   ((j) => {
   tasks.push(neues Versprechen((auflösen) => {
    setzeTimeout(() => {
    console.log(neues Datum, j);
    resolve(); // Hier muss aufgelöst werden, sonst funktioniert der Code nicht wie erwartet
    }, 1000 * j); // Das Timeout des Timers wird schrittweise erhöht }));
   })(ich);
  }

  Versprechen.alle(Aufgaben).dann(() => {
   setzeTimeout(() => {
   console.log(neues Datum, i);
   }, 1000); // Beachten Sie, dass wir das Timeout nur auf 1 Sekunde einstellen müssen });
  //0 -> 1 -> 2 -> 3 -> 4 -> 5
const tasks = []; // Hier ist das Promise für asynchrone Operationen
  const Ausgabe = (i) => neues Versprechen((Auflösung) => {
   setzeTimeout(() => {
   console.log(neues Datum, i);
   lösen();
   }, 1000 * i);
  });

  // Alle asynchronen Operationen generieren für (var i = 0; i < 5; i++) {
   Aufgaben.push(Ausgabe(i));
  }

  // Nachdem der asynchrone Vorgang abgeschlossen ist, geben Sie das letzte i aus
  Versprechen.alle(Aufgaben).dann(() => {
   setzeTimeout(() => {
   console.log(neues Datum, i);
   }, 1000);
  });
  //0 -> 1 -> 2 -> 3 -> 4 -> 5
// Schlaf in anderen Sprachen simulieren, tatsächlich kann es jede asynchrone Operation sein const sleep = (timeoutMS) => new Promise((resolve) => {
 setTimeout(auflösen, timeoutMS);
});

(async () => { // asynchroner Funktionsausdruck, der bei der Deklaration für (var i = 0; i < 5; i++) ausgeführt wird {
 wenn (i > 0) {
  warte auf Schlaf (1000);
 }
 console.log(neues Datum, i);
 }

 warte auf Schlaf (1000);
 console.log(neues Datum, i);
})();
//0 -> 1 -> 2 -> 3 -> 4 -> 5

Im obigen Code werden Closures verwendet. Kurz gesagt, Closures finden den Endwert der entsprechenden Variablen in der übergeordneten Funktion an derselben Adresse.

2. Speicherbereinigungsmechanismus

Die Speicherverwaltung in JavaScript erfolgt automatisch und unsichtbar. Wir erstellen primitive Typen, Objekte, Funktionen … all dies erfordert Speicher.

Es gibt zwei häufig verwendete Methoden zur Speicherbereinigung: Mark-Sweep und Referenzzählung.

1. Markieren und Fegen

Wenn der Garbage Collector ausgeführt wird, markiert er alle im Speicher gespeicherten Variablen. Anschließend werden die Variablen in der Umgebung und die von den Variablen in der Umgebung referenzierten Token entfernt.

Danach markierte Variablen gelten als gelöscht, da auf sie von Variablen in der Umgebung nicht mehr zugegriffen werden kann.

endlich. Der Garbage Collector führt die Speicherbereinigung durch, zerstört die markierten Werte und gibt den von ihnen belegten Speicherplatz zurück.

2. Referenzzählung

Beim Referenzzählen wird nachverfolgt, wie oft auf jeden Wert verwiesen wird. Wenn eine Variable deklariert und der Variablen ein Referenztyp zugewiesen wird, beträgt der Referenzzähler des Werts 1.

Umgekehrt, wenn die Variable, die eine Referenz auf diesen Wert enthält, einen anderen Wert erhält, wird die Anzahl der Referenzen auf diesen Wert um 1 reduziert. Wenn die Anzahl der Zitate 0 wird,

Dies bedeutet, dass auf diesen Wert nicht mehr zugegriffen werden kann und der von ihm belegte Speicherplatz zurückgefordert werden kann. Auf diese Weise wird beim nächsten Ausführen des Garbage Collector

Dadurch wird der Speicher freigegeben, der von Werten belegt ist, deren Referenzzähler 0 ist.

Zusammenfassen

Dies ist das Ende dieses Artikels über JS-Closures und Garbage Collection-Mechanismen. Weitere relevante Inhalte zu JS-Closures und Garbage Collection finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Erweiterte Closures in JavaScript erklärt
  • JavaScript-Closures erklärt
  • JavaScript-Closures erklärt
  • Javascript-Bereich und Abschlussdetails
  • Detaillierte Erläuterung der JavaScript-Closure-Probleme
  • Detaillierte Erklärung von Javascript-Closures und -Anwendungen
  • Analyse des JS-Closure-Prinzips und seiner Anwendungsszenarien
  • Detaillierte Erklärung des Prinzips und der Funktion des JavaScript-Closures

<<:  Eine kurze Diskussion über die Berechnungsmethode von key_len in MySQL erklären

>>:  Detaillierte Erläuterung der drei Möglichkeiten zum Konfigurieren virtueller Nginx-Hosts (basierend auf IP)

Artikel empfehlen

Beispiel zur MySQL-Passwortänderung – ausführliche Erklärung

Beispiel zur MySQL-Passwortänderung – ausführlich...

Lassen Sie uns über den Unterschied zwischen MyISAM und InnoDB sprechen

Die Hauptunterschiede sind folgende: 1. MySQL ver...

nginx generiert automatisch Konfigurationsdateien im Docker-Container

Wenn ein Unternehmen eine automatisierte Docker-B...

Sprechen Sie über nextTick in Vue

Wenn sich die Daten ändern, wird die DOM-Ansicht ...

So kapseln Sie die Karussellkomponente in Vue3

Zweck Kapseln Sie die Karussellkomponente und ver...

Implementierung der Änderung von Konfigurationsdateien im Docker-Container

1. Betreten Sie den Container docker run [Option]...

Wird der Index durch MySQL ungültig?

Wird MySQLs IN den Index ungültig machen? Gewohnh...

Verwenden Sie das Rem-Layout, um eine adaptive

Ich habe bereits einen Artikel über mobile Anpass...

mysql 5.7.11 winx64 anfängliche Passwortänderung

Laden Sie die komprimierte Version von MySQL-5.7....

Zusammenfassung häufiger Probleme und Anwendungskenntnisse in MySQL

Vorwort Bei der täglichen Entwicklung oder Wartun...

So schreiben Sie Objekte und Parameter, um Flash in Firefox abzuspielen

Code kopieren Der Code lautet wie folgt: <Obje...