Wenn Sie neu in JavaScript sind, sind Sie wahrscheinlich bereits auf einige der lustigen Verhaltensweisen gestoßen, die diese Sprache zu bieten hat (Ausstellung A). Zunaechst, Diese seltsamen Macken mögen lächerlich und frustrierend erscheinen, Aber ich verspreche, dass hinter all diesem Wahnsinn eine Methode steckt.
Eine der schwierigsten Hürden, die es meiner bescheidenen Meinung nach zu überwinden gilt, ist der Unterschied zwischen dem Übergeben nach Wert und dem Übergeben nach Referenz. Warum ist dieses Konzept so schwierig? Für den Anfang können Sie sicherlich ziemlich weit kommen, ohne wirklich zu verstehen, wie JavaScript mit primitiven Werten und Referenzwerten interagiert. Dies kann und wird in den meisten Fällen zu einer Menge von Fehlern führen, die schwer aufzuspüren und ärgerlich zu beheben sind. Zweitens ist dies ein Konzept, das in technischen Interviews auftauchen wird, also wird es als eine riesige rote Fahne gegen Sie zählen, wenn Sie es nicht verstehen.
Fürchte dich nicht, Mitleser! Lassen Sie die Ausbildung beginnen …
Primitive Datentypen
In JavaScript können wir Datentypen in zwei verschiedene Buckets unterteilen, primitive Datentypen und Objekte.
In JavaScript gibt es sechs primitive Datentypen: string
, number
, boolean
, undefined
, null
, und symbol
ab ES6.
Primitive Datentypen werden nach Wert übergeben oder kopiert und sind unveränderlich, was bedeutet, dass der vorhandene Wert nicht wie ein Array oder ein Objekt geändert werden kann. Schauen wir uns den folgenden Code an, um dies in Aktion zu sehen.
Hier haben wir zwei Variablen erstellt, x = 10
und y = x
. Da 10
eine Zahl und ein primitiver Wert ist, kopieren wir beim Setzen von y = x
tatsächlich den Wert, dh 10
, und weisen ihn y
zu. Wir können dies auch anhand der folgenden Tabelle visualisieren.
Wenn wir den Wert von x
ändern würden, würden wir sehen, dass y
seinen Wert von 10
behält. Dies liegt wiederum daran, dass primitive Werte kopiert werden, sodass der Wert von y
unabhängig vom Wert von x
ist. Betrachten Sie es als eine Fotokopie eines Bildes. Nachdem Sie die Kopie erstellt haben, haben Sie zwei identische Bilder: ein Original und ein Faksimile. Wenn Sie das Original halbieren würden, würde nur das Original geändert und das Faksimile würde genau gleich bleiben.
Referenzobjekte
Objekte hingegen werden als Referenz übergeben und zeigen auf einen Speicherort für den Wert, nicht auf den Wert selbst. Schauen wir uns das in unserem Code an.
In diesem Beispiel ist x
jetzt ein Objekt, das auf {dog: "poodle"}
zeigt. Wenn wir die Variable y
erstellen und ihr den Wert x
zuweisen, können wir jetzt auf die verschiedenen Eigenschaften von x
zugreifen, einschließlich des Werts für dog
, dh "poodle"
. Dies scheint genau die gleiche Logik zu sein, die für primitive Werte verwendet wird, aber werfen wir einen Blick auf unser Handy-Dandy-Diagramm unten, um den subtilen, aber wichtigen Unterschied zu sehen.
Nun sieht dieses Diagramm ein wenig anders aus, als wenn unsere Variablen x
und y
primitive Datentypen enthielten. In dieser Version sehen wir, dass die Werte für x
und y
keine Datentypen sind, sondern Verweise auf eine Adresse im Speicher, tatsächlich dieselbe Adresse! Schauen wir uns nun an, was mit x
passiert, wenn wir eine neue Eigenschaft von size
hinzufügen y
…
x
gibt immer noch ein Objekt zurück, aber jetzt hat es auch eine zusätzliche Eigenschaft von size
! Dies liegt wiederum daran, dass sowohl x
als auch y
auf dasselbe Referenzobjekt zeigen, sodass alle an einer Variablen vorgenommenen Änderungen in der anderen sichtbar sind.
Um mir zu helfen, mich an dieses Konzept zu erinnern, stelle ich mir Referenzwerte gerne als Haus und die Variablen als Personen vor, die in diesem Haus leben. Alle Bewohner (Variablen) können „Ich habe ein Haus“ sagen und auf dasselbe Haus zeigen. Wenn ein einzelner Bewohner beschließt, das Haus gelb zu streichen, haben jetzt alle Bewohner ein gelbes Haus, weil es geteilt wird.
Schauen wir uns ein weiteres Beispiel an, das eine Vielzahl von Referenzobjekten enthält.
In diesem Code beginnen wir mit einer Variablen person
, die Eigenschaften von name
, age
und hobbies
enthält. Wenn wir dieses Objekt auf die Konsole drucken, erhalten wir genau das, was wir erwarten — dasselbe Objekt, das wir gerade erstellt haben.
Als nächstes haben wir eine Funktion namens changePerson
, die ein Argument aufnimmt, einige Änderungen vornimmt und dann ein Objekt zurückgibt. Wenn wir die Variable thirdPerson
erstellen, rufen wir die FunktionchangePerson
auf, indem wir unser ursprüngliches Objekt person
übergeben. Das Interessante ist, was passiert, wenn wir wieder auf die Konsole thirdPerson
und person
drucken.
Beachten Sie, dass console.log(thirdPerson)
ein ganz neues Objekt mit neuen Eigenschaften zurückgibt. Sehen Sie sich nun an, was console.log(person)
zurückgibt. Dies ähnelt unserem ursprünglichen Objekt, enthält jedoch neue Eigenschaftswerte, die in unserer Funktion changePerson
eingeführt wurden.
Auf Gleichheit prüfen
Schauen wir uns abschließend an, wie sich primitive Datentypen und Referenzobjekte mit Gleichheitsoperatoren verhalten.
Wenn es um primitive Datentypen geht, spielt es keine Rolle, was sich rechts vom Zeichen =
befindet, solange die Werte gleich sind. Wir können dies oben mit den Variablen a
und b
sehen, die unterschiedlich geschrieben sind, aber denselben Wert erhalten, wenn wir den ===
, den strikten Gleichheitsoperator, verwenden.
Das Gegenteil gilt im zweiten Beispiel für dog
und cat
. Obwohl es den Anschein hat, dass sie identische Werte enthalten, ist ein Array und ein Referenzobjekt, was bedeutet, dass
===
überprüft, ob sowohl dog
als auch cat
denselben Verweis auf den Wert im Speicher haben. Andererseits ist bird === dog
wahr, weil sie dasselbe Referenzobjekt haben.
Fazit
Und damit ist unsere Einführung in Pass by value vs Pass by reference abgeschlossen. Es gibt weitere Themen, die unter diesem grundlegenden Dach behandelt werden können, einschließlich dessen, was passiert, wenn eine Referenz überschrieben wird oder verloren geht, wie eine Referenz kopiert wird, um ein neues Objekt zu erstellen UND sicherzustellen, dass die Kopie eine tiefe Kopie ist, um nur einige zu nennen. Ich würde empfehlen, sich die Ressourcen anzusehen, die ich unten verwendet habe und die einige dieser zusätzlichen Themen ausführlicher behandeln.