Detaillierte Erklärung des JavaScript-Stacks und der Kopie

Detaillierte Erklärung des JavaScript-Stacks und der Kopie

1. Definition des Stapels

1. Ein Stapel ist eine spezielle lineare Liste. Die Besonderheit besteht darin, dass die Operationen zum Einfügen und Löschen von Datenelementen nur an einem Ende der linearen Liste durchgeführt werden können.

Fazit: Last In First Out (LIFO), abgekürzt als LIFO-lineare Liste.

Zu den Anwendungen von Stapeln gehören: Konvertierung von Zahlensystemen, Grammatik- und lexikalische Analyse, Ausdrucksauswertung usw.

2. Eine Warteschlange ist ebenfalls eine lineare Tabelle mit eingeschränkten Operationen. Ihre Operationsbeschränkungen unterscheiden sich von denen des Stapels. Es gibt an beiden Enden Beschränkungen. Einfügungen können nur an einem Ende der Tabelle vorgenommen werden (nur hinein, nicht hinaus), und Löschungen können nur am anderen Ende der Tabelle vorgenommen werden (nur hinaus, nicht hinein). Das Ende, das Löschungen zulässt, wird als Rückseite bezeichnet, und das Ende, das Einfügungen zulässt, wird als Vorderseite bezeichnet. Das Funktionsprinzip der Warteschlange ist First-In, First-Out, daher wird die Warteschlange auch als FIFO-Tabelle (First In First Out) bezeichnet.

Da Stapel und Warteschlangen ebenfalls lineare Listen sind, verfügen sie über zwei Speicherstrukturen: sequentielle Stapel und verknüpfte Stapel. Der Unterschied zwischen diesen beiden Speicherstrukturen führt dazu, dass auch die Algorithmen zur Implementierung der grundlegenden Operationen des Stapels unterschiedlich sind.

2. JS-Stack-Recherche

1. Stapeln und stapeln

Dem Stapel wird automatisch Speicherplatz zugewiesen, der vom System automatisch freigegeben wird; dem Heap hingegen wird dynamisch Speicher zugewiesen, dessen Größe jedoch ungewiss ist und der nicht automatisch freigegeben wird.

2. Basistypen und Referenztypen

(1) Basistyp: ein einfaches, im Stapelspeicher abgelegtes Datensegment mit fester Datengröße und zuweisbarer Speicherplatzgröße.
Die fünf grundlegenden Datentypen sind Undefined, Null, Boolean, Number und String. Sie werden direkt nach Wert gespeichert, sodass auf sie direkt zugegriffen werden kann.

(2) Referenztyp: Ein im Heap-Speicher gespeichertes Objekt. Die Variable speichert tatsächlich einen Zeiger, der auf einen anderen Ort zeigt. Jeder Raum hat eine andere Größe und muss situationsbedingt individuell eingeteilt werden.

Wenn wir auf den Wert eines Referenztyps (z. B. eines Objekts, Arrays, einer Funktion usw.) zugreifen müssen, erhalten wir zuerst den Adresszeiger des Objekts vom Stapel und dann die erforderlichen Daten aus dem Heap-Speicher.

3. Übergabe per Wert und Übergabe per Referenz

Der Grund, warum wir erklärt haben, was Heap, Stack und Variablentypen im Speicher sind, besteht eigentlich darin, besser zu verstehen, was „Shallow Copy“ und „Deep Copy“ sind.
Der größte Unterschied zwischen Basistypen und Referenztypen ist eigentlich der Unterschied zwischen der Übergabe per Wert und der Übergabe per Adresse. Testfälle:

var a = [1,2,3,4,5];
var b = a;
var c = a[0];
alarm(b); //1,2,3,4,5 
alarm(c); //1 
//Ändere den Wert b[4] = 6;
c = 7;
alarm(a[4]); //6
alarm(a[0]); //1

Aus dem Obigen können wir ersehen, dass, wenn ich die Daten in b ändere, sich auch die Daten in a ändern; wenn ich aber den Datenwert von c ändere, ändert sich a nicht.
Dies ist der Unterschied zwischen der Übergabe per Wert und der Übergabe per Referenz. Da a ein Array und ein Referenztyp ist, wird bei der Zuweisung an b die Adresse im Stapel übergeben (entspricht der Erstellung eines neuen „Zeigers“ mit einem anderen Namen) und nicht das Objekt im Heap-Speicher. Und c ist lediglich ein Datenwert, der aus einem Heap-Speicher abgerufen und im Stapel gespeichert wird. Wenn b geändert wird, wird es daher gemäß der Änderungsadresse zum Heap a zurückkehren, während c direkt im Stapel geändert wird und nicht auf den Heap-Speicher a verweisen kann.

3. Kopieren

1. Oberflächliche Kopie

Wie bereits erwähnt, speichert die Variable beim Definieren eines Objekts oder Arrays häufig nur eine Adresse. Wenn wir eine Objektkopie verwenden und das Attribut ein Objekt oder ein Array ist, übergeben wir lediglich eine Adresse. Wenn das untergeordnete Objekt auf diese Eigenschaft zugreift, wird es daher basierend auf der Adresse zum Heap-Speicher zurückverfolgt, auf den das übergeordnete Objekt zeigt. Das heißt, das übergeordnete und das untergeordnete Objekt sind verknüpft und die Eigenschaftswerte der beiden zeigen auf denselben Speicherplatz.

var a = {
         Schlüssel1:"11111"
    }
Funktion Kopieren(p) {
var c = {};
für (var i in p) { 
           c[i] = p[i];
        }
Rückkehr c;
  }
     a.key2 = ['Xiaohui','Xiaohui'];
var b = Kopie(a);
   b.Schlüssel3 = "33333";
     alarm(b.schlüssel1); //1111111
     Alarm (b.Schlüssel3); //33333
     alarm(a.key3); //undefiniert

Das Attribut key1 im Objekt ist ein String und das Attribut key2 ist ein Array. a wird nach b kopiert und alle 12 Attribute werden erfolgreich kopiert. Wenn dem b-Objekt ein String-Attribut key3 hinzugefügt wird, kann b normal geändert werden, ist aber in a nicht definiert. Dies bedeutet, dass Schlüssel3 (Basistyp) des untergeordneten Objekts nicht mit dem übergeordneten Objekt verknüpft und daher undefiniert ist.

b.key2.push("Dahui");
Alarm (b.Taste2); //Xiaohui, Xiaohui, Dahui Alarm (a.Taste2); //Xiaohui, Xiaohui, Dahui

Wenn die geänderte Eigenschaft jedoch zu einem Objekt oder Array wird, entsteht eine Zuordnung zwischen den übergeordneten und untergeordneten Objekten. Aus den obigen Popup-Ergebnissen können wir ersehen, dass sich die Schlüssel2-Attributwerte (Arrays) von a und b geändert haben, als ich das b-Objekt geändert habe. Sein Status im Speicher kann durch die folgende Abbildung dargestellt werden.

Der Grund dafür ist, dass der Wert von key1 vom Basistyp ist, sodass das Datensegment beim Kopieren übergeben wird; der Wert von key2 ist jedoch ein Objekt im Heap-Speicher, sodass beim Kopieren von key2 die Adresse, die auf das key2-Objekt zeigt, übergeben wird. Unabhängig davon, wie viele key2s kopiert werden, zeigt ihr Wert immer auf den Speicherplatz des key2-Objekts des übergeordneten Objekts.

2. Tiefe Kopie

Vielleicht ist das obige nicht das Ergebnis, das wir bei der tatsächlichen Codierung erreichen möchten. Wir möchten nicht, dass die übergeordneten und untergeordneten Objekte miteinander verknüpft werden. In diesem Fall kann Deep Copy verwendet werden. Da nur die Adresse übergeben wird, wenn der Attributwerttyp ein Array oder ein Objekt ist, können wir dieses Problem durch Rekursion lösen. Wir können alle zum Objekt gehörenden Attributtypen im übergeordneten Objekt durchlaufen und sie dem untergeordneten Objekt zuweisen. Der Testcode lautet wie folgt:

Funktion Kopieren(p, c) {
var c = c || {};
für (var i in p) {
wenn (Typ von p[i] === 'Objekt') {
              c[i] = (p[i].Konstruktor === Array) ? [] : {};
             Kopieren(p[i], c[i]);
           } anders {
              c[i] = p[i];
          }
        }
Rückkehr c;
  }    
     a.key2 = ['Xiaohui','Xiaohui'];
var b={};
     b = Kopie(a,b);        
     b.key2.push("Dahui");
     Alarm (b.Taste2); //Xiaohui, Xiaohui, Dahui Alarm (a.Taste2); //Xiaohui, Xiaohui

Aus dem Obigen können wir ersehen, dass, wenn das key2-Array von b geändert wird, dem key2-Array im übergeordneten Objekt a kein neuer Wert hinzugefügt wird, d. h., das untergeordnete Objekt hat keinen Einfluss auf key2 im übergeordneten Objekt a. Der Speichermodus ist wie folgt:

Oben finden Sie eine ausführliche Erläuterung des JavaScript-Stapels und -Kopierens. Weitere Informationen zum JS-Stapelkopieren finden Sie in den anderen verwandten Artikeln auf 123WORDPRESS.COM!

Das könnte Sie auch interessieren:
  • Zusammenfassung des monotonen LeetCode-Stacks
  • Eine kurze Diskussion über monotone Warteschlangen und monotone Stapel
  • JVM-Speicherstruktur: Programmzähler, virtueller Maschinenstapel, lokaler Methodenstapel
  • Detaillierte Erläuterung des JVM-Stapelüberlaufs und des Heapüberlaufs
  • Einführung in die grundlegenden Eigenschaften des monotonen Stapels in C++

<<:  Eine Fallstudie zum gründlichen Verständnis der korrekten Verwendung des MySQL-InnDB-Joint-Index

>>:  Beispiel für die Konfiguration der domänenübergreifenden Fehlerbehebung in nginx

Artikel empfehlen

Zusammenfassung der Verwendung von Datetime und Timestamp in MySQL

Inhaltsverzeichnis 1. Wie wird die aktuelle Uhrze...

So legen Sie Listenstilattribute in CSS fest (lesen Sie einfach diesen Artikel)

Eigenschaften des Listenstils Es gibt 2 Arten von...

Vue implementiert die Anmeldung mit grafischem Bestätigungscode

In diesem Artikelbeispiel wird der spezifische Co...

Detaillierte Erläuterung der Verwaltung und Verwendung von Docker Secrets

1. Was ist Docker Secret 1. Szenariodarstellung W...

Details der MySQL-Berechnungsfunktion

Inhaltsverzeichnis 2. Feldverkettung 2. Geben Sie...

So binden Sie einen Domänennamen an den Nginx-Dienst

Konfigurieren Sie mehrere Server in nginx.conf: B...

Best Practices zur Implementierung einfacher Jira-Projekte mit React+TS

Eine Reihe von Projekten für die Ausbildung reagi...

Warum ist es langsam, wenn Limit- und Offset-Paging-Szenarien verwendet werden?

Beginnen wir mit einer Frage Als ich vor fünf Jah...

Detaillierter Installationsprozess der MySQL 8.0 Windows-ZIP-Paketversion

Der Installationsprozess von MySQL 8.0 Windows Zi...

Installationsschritte der Ubuntu 20.04-Doppelpinyin-Eingabemethode

1. Chinesische Eingabemethode einrichten 2. Stell...

Einführung in die reaktive Funktion toRef-Funktion ref-Funktion in Vue3

Inhaltsverzeichnis Reaktive Funktion Verwendung: ...