Wie funktioniert die Variablenzuweisung in JavaScript?

97

Also habe ich neulich herumgespielt, um genau zu sehen, wie die Massenzuweisung in JavaScript funktioniert.

Zuerst habe ich dieses Beispiel in der Konsole ausprobiert:

a = b = {};
a.foo = 'bar';
console.log(b.foo);

Das Ergebnis war, dass "Balken" in einer Warnung angezeigt wurde. Das ist fair genug aund bwirklich nur Aliase für dasselbe Objekt. Dann dachte ich, wie könnte ich dieses Beispiel einfacher machen.

a = b = 'foo';
a = 'bar';
console.log(b);

Das ist so ziemlich das Gleiche, nicht wahr? Nun, diesmal kehrt es foonicht barso zurück , wie ich es vom Verhalten des ersten Beispiels erwarten würde.

Warum passiert das?

NB Dieses Beispiel könnte mit dem folgenden Code noch weiter vereinfacht werden:

a = {};
b = a;
a.foo = 'bar';
console.log(b.foo);

a = 'foo';
b = a;
a = 'bar';
console.log(b);

(Ich vermute, dass JavaScript Primitive wie Zeichenfolgen und Ganzzahlen anders behandelt als Hashes. Hashes geben einen Zeiger zurück, während "Core" -Primitive eine Kopie von sich selbst zurückgeben.)

Chris Lloyd
quelle
Erklären Sie die Zuordnung hier: syntaxsuccess.com/viewarticle/…
TGH

Antworten:

114

Im ersten Beispiel legen Sie eine Eigenschaft eines vorhandenen Objekts fest. Im zweiten Beispiel weisen Sie ein brandneues Objekt zu.

a = b = {};

aund bsind jetzt Zeiger auf dasselbe Objekt. Also, wenn Sie tun:

a.foo = 'bar';

Es setzt b.fooauch da aund zeigt bauf dasselbe Objekt.

Jedoch!

Wenn Sie dies stattdessen tun:

a = 'bar';

Sie sagen, das azeigt jetzt auf ein anderes Objekt. Dies hat keine Auswirkung auf das, worauf zuvor ahingewiesen wurde.

In JavaScript sind das Zuweisen einer Variablen und das Zuweisen einer Eigenschaft zwei verschiedene Operationen. Stellen Sie sich Variablen am besten als Zeiger auf Objekte vor. Wenn Sie einer Variablen direkt zuweisen, ändern Sie keine Objekte, sondern ordnen Ihre Variable lediglich einem anderen Objekt zu.

Durch das Zuweisen einer Eigenschaft wie a.foowird jedoch das Objekt geändert, auf das verwiesen wird a. Dadurch werden natürlich auch alle anderen Verweise geändert, die auf dieses Objekt verweisen, nur weil sie alle auf dasselbe Objekt verweisen.

Alex Wayne
quelle
3
"Sie sagen, dass a jetzt auf ein anderes Objekt zeigt." Nein, benutze nicht das Wort Objekt. Eine Zeichenfolge ist in JavaScript kein Objekt.
Nosredna
9
Vielleicht sind Strings technisch nicht vom Javascript-Typ "Object", aber sie können an Objekte im OO-Sinne gedacht werden.
Alex Wayne
2
@Squeegy: Strings sind Grundelemente, keine Objekte: Sie können Strings keine beliebigen Eigenschaften zuweisen! Sie verhalten sich nur objektartig aufgrund des sogenannten Autoboxing in Java
Christoph
11
Aber Strings haben Methoden und Eigenschaften, und der Prototyp von String kann definitiv geändert werden. Sie verhalten sich sicher wie Objekte.
Cameron Martin
9
@ddlshack: Wie Christoph erklärte, ist dies auf Autoboxing zurückzuführen. Wenn Sie eine Zeichenfolge über definieren sind var foo = new String('foo');, dann , dass wäre ein String - Objekt (und typeofbestätigt dies). Wenn Sie es jedoch über ein Zeichenfolgenliteral deklarieren, handelt es sich um Zeichenfolgenprimitive. Siehe: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
Lèse majesté
26

Ihre Frage wurde von Squeegy bereits zufriedenstellend beantwortet - sie hat nichts mit Objekten vs. Grundelementen zu tun, sondern mit der Neuzuweisung von Variablen vs. Festlegen von Eigenschaften in demselben referenzierten Objekt.

Die Antworten und Kommentare scheinen viel Verwirrung über JavaScript-Typen zu stiften. Hier eine kleine Einführung in das JavaScript-Typsystem:

In JavaScript gibt es zwei grundlegend unterschiedliche Arten von Werten: Grundelemente und Objekte (und es gibt nichts Vergleichbares wie einen 'Hash').

Zeichenfolgen, Zahlen und Boolesche Werte sowie nullund undefinedsind Grundelemente. Objekte sind alles, was Eigenschaften haben kann. Sogar Arrays und Funktionen sind reguläre Objekte und können daher beliebige Eigenschaften enthalten. Sie unterscheiden sich nur in der internen Eigenschaft [[Klasse]] (Funktionen haben zusätzlich eine Eigenschaft namens [[Aufruf]] und [[Konstrukt]], aber hey, das sind Details).

Der Grund dafür, dass sich primitive Werte wie Objekte verhalten, liegt im Autoboxing, aber die primitiven Werte selbst können keine Eigenschaften enthalten.

Hier ist ein Beispiel:

var a = 'quux';
a.foo = 'bar';
document.writeln(a.foo);

Dies gibt Folgendes aus undefined: aEnthält einen primitiven Wert, der beim Zuweisen der Eigenschaft zu einem Objekt heraufgestuft wird foo. Dieses neue Objekt wird jedoch sofort verworfen, sodass der Wert von fooverloren geht.

Stellen Sie sich das so vor:

var a = 'quux';
new String(a).foo = 'bar'; // we never save this new object anywhere!
document.writeln(new String(a).foo); // a completly new object gets created
Christoph
quelle
Auf der Seite der Mozilla Foundation 'Eine Wiedereinführung in JavaScript (JS-Tutorial)' werden JavaScript-Objekte "als einfache Sammlungen von Name-Wert-Paaren beschrieben. Als solche ähneln sie ...", gefolgt von einer Liste von Wörterbüchern, Hash , Hash-Tabellen und Hash-Maps aus verschiedenen Programmiersprachen. Auf derselben Seite werden Objekteigenschaftsreferenzen als Hash-Tabellensuche beschrieben. Objekte sind also alles wie eine 'Hash'-Tabelle. Dies macht die anderen nützlichen Informationen nicht ungültig, aber die ursprüngliche Charakterisierung von Chris Lloyd war nicht ungenau.
C Perkins
2

Sie sind mehr oder weniger korrekt, außer dass das, was Sie als "Hash" bezeichnen, eigentlich nur eine Kurzsyntax für ein Objekt ist.

Im ersten Beispiel beziehen sich a und b beide auf dasselbe Objekt. Im zweiten Beispiel ändern Sie a , um auf etwas anderes zu verweisen.

Kevin
quelle
Warum also die Doppelmoral für Object?
Chris Lloyd
Es ist keine Doppelmoral. Im ersten Beispiel beziehen sich a und b immer noch auf dasselbe Objekt. Sie ändern lediglich eine Eigenschaft dieses Objekts. Im zweiten Beispiel zeigen Sie a auf ein anderes Objekt.
Kevin
1
Nein, der Unterschied besteht darin, dass es sich im zweiten Fall um eine Zeichenfolge handelt, nicht um ein Objekt.
Nosredna
1
Um es klar auszudrücken: Dies hat nichts mit Zeichenfolgen zu tun, die eine Kopie von sich selbst zurückgeben. Der Grund, warum die beiden Codefragmente unterschiedlich sind, ist in Kevins zweitem Absatz (ausführlicher in Squeegys Antwort erläutert).
Chuck
Es spielt keine Rolle, ob Sie eine Zeichenfolge oder ein Objekt in der Variablen haben. Sie weisen einen neuen, anderen Wert zu und dann enthält die Variable diesen neuen, anderen Wert.
etw
2

Hier ist meine Version der Antwort:

obj = {a:"hello",b:"goodbye"}
x = obj
x.a = "bonjour"

// now obj.a is equal to "bonjour"
// because x has the same reference in memory as obj
// but if I write:
x = {}
x.a = obj.a
x.b = obj.b
x.a = "bonjour"

// now x = {a:"bonjour", b:"goodbye"} and obj = {a:"hello", b:"goodbye"}
// because x points to another place in the memory
Bashir Abdelwahed
quelle
0

Sie setzen a so, dass es auf ein neues Zeichenfolgenobjekt zeigt, während b weiterhin auf das alte Zeichenfolgenobjekt zeigt.

mdm
quelle
0

Im ersten Fall ändern Sie eine Eigenschaft des in der Variablen enthaltenen Objekts, im zweiten Fall weisen Sie der Variablen einen neuen Wert zu. Das sind grundsätzlich verschiedene Dinge. Die Variablen aund bsind nicht irgendwie magisch durch die erste Zuweisung verknüpft, sie enthalten nur das gleiche Objekt. Dies ist auch im zweiten Beispiel der Fall, bis Sie der bVariablen einen neuen Wert zuweisen .

etw
quelle
0

Der Unterschied besteht zwischen einfachen Typen und Objekten.

Alles, was ein Objekt ist (wie ein Array oder eine Funktion), wird als Referenz übergeben.

Alles, was ein einfacher Typ ist (wie eine Zeichenfolge oder eine Zahl), wird kopiert.

Ich habe immer eine copyArray-Funktion zur Hand, damit ich sicher sein kann, dass ich keine Aliase für dasselbe Array erstelle.

Nosredna
quelle
Der Unterschied ist in vielen Szenarien nicht erkennbar, aber Javascript wird nicht als Referenz übergeben oder zugewiesen. Es kopiert Referenzwerte.
Juan Pablo Califano
Diese Leute haben bereits gute Arbeit geleistet, um es zu erklären, also füge ich einfach den Link ein: stackoverflow.com/questions/40480/is-java-pass-by-reference (Ich beziehe mich auf Java, aber die Semantik für das Übergeben und Zuweisen Werte / Referenzen sind die gleichen wie in Javascript)
Juan Pablo Califano
1
Eigentlich ist diese Antwort falsch, alles wird in JavaScript als Wert übergeben. In MDN heißt es: "Die Parameter eines Funktionsaufrufs sind die Argumente der Funktion. Argumente werden nach Wert an Funktionen übergeben. Wenn die Funktion den Wert eines Arguments ändert, wird diese Änderung weder global noch in der aufrufenden Funktion wiedergegeben. Objektreferenzen sind dies jedoch." Auch Werte, und sie sind etwas Besonderes: Wenn die Funktion die Eigenschaften des referenzierten Objekts ändert, ist diese Änderung außerhalb der Funktion sichtbar. " developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
bittersweetryan
Grundelemente verhalten sich wie unveränderliche Objekte ( genau wie sie im strengen Modus). Diese Antwort ist nicht korrekt.
Ry-