In JavaScript wird der Typ des Wertes ausschließlich steuert, ob dieser Wert von Wert-Kopie oder durch Referenz-Kopie .
Primitive Werte werden immer durch Wertkopie zugewiesen/übergeben :
null
undefined
- String
- Nummer
- boolean
- Symbol in
ES6
Zusammengesetzte Werte werden immer durch Referenzkopie zugewiesen/übergeben
Zum Beispiel
var a = 2;
var b = a; // `b` is always a copy of the value in `a`
b++;
a; // 2
b; // 3
var c = [1,2,3];
var d = c; // `d` is a reference to the shared `[1,2,3]` value
d.push( 4 );
c; // [1,2,3,4]
d; // [1,2,3,4]
In dem obigen Ausschnitt, weil 2
ist ein skalares Primitivum, a
enthält eine erste Kopie dieses Wertes, und b
wird eine weitere Kopie des Wertes zugewiesen. Beim Ändern b
ändern Sie in keiner Weise den Wert in a
.
Aber beide c
y d
sind getrennte Verweise auf denselben gemeinsamen Wert [1,2,3]
der ein zusammengesetzter Wert ist. Es ist wichtig zu beachten, dass weder c
noch d
mehr "besitzt" die [1,2,3]
Wert - beide sind nur gleichrangige Verweise auf den Wert. Wenn also eine der beiden Referenzen zum Ändern verwendet wird ( .push(4)
) die eigentliche gemeinsame array
Wert selbst, betrifft es nur den einen gemeinsamen Wert, und beide Referenzen verweisen auf den neu geänderten Wert [1,2,3,4]
.
var a = [1,2,3];
var b = a;
a; // [1,2,3]
b; // [1,2,3]
// later
b = [4,5,6];
a; // [1,2,3]
b; // [4,5,6]
Wenn wir den Auftrag erteilen b = [4,5,6]
tun wir absolut nichts, um zu beeinflussen, wo a
noch referenziert ( [1,2,3]
). Um das zu tun, b
müsste ein Zeiger sein auf a
statt eines Verweises auf den array
-- aber eine solche Möglichkeit gibt es in JS nicht!
function foo(x) {
x.push( 4 );
x; // [1,2,3,4]
// later
x = [4,5,6];
x.push( 7 );
x; // [4,5,6,7]
}
var a = [1,2,3];
foo( a );
a; // [1,2,3,4] not [4,5,6,7]
Wenn wir das Argument a
weist es eine Kopie der Datei a
Bezugnahme auf x
. x
y a
sind separate Verweise, die auf dieselbe [1,2,3]
Wert. Jetzt können wir innerhalb der Funktion diesen Verweis verwenden, um den Wert selbst zu verändern ( push(4)
). Aber wenn wir die Zuordnung vornehmen x = [4,5,6]
hat dies keinen Einfluss darauf, wo die ursprüngliche Referenz a
zeigt - immer noch auf die (jetzt geänderte) [1,2,3,4]
Wert.
Um effektiv einen zusammengesetzten Wert zu übergeben (wie eine array
) durch value-copy, müssen Sie manuell eine Kopie davon erstellen, damit die übergebene Referenz nicht immer noch auf das Original verweist. Zum Beispiel:
foo( a.slice() );
Zusammengesetzter Wert (Objekt, Array usw.), der durch Referenzkopie übergeben werden kann
function foo(wrapper) {
wrapper.a = 42;
}
var obj = {
a: 2
};
foo( obj );
obj.a; // 42
Hier, obj
dient als Wrapper für die skalare primitive Eigenschaft a
. Bei Übergabe an foo(..)
eine Kopie der obj
Referenz übergeben und auf den wrapper
Parameter. Wir können nun den wrapper
Referenz, um auf das gemeinsame Objekt zuzugreifen und seine Eigenschaft zu aktualisieren. Nachdem die Funktion beendet ist, obj.a
wird der aktualisierte Wert angezeigt 42
.
Quelle
0 Stimmen
Ich denke, wir haben genügend Beweise dafür, was die genaue Semantik der Sprache ist. Jetzt fehlt nur noch der Auszug aus der EcmaScript-Spezifikation, der sie definiert, und wir werden eine Antwort auf diese Frage haben.
8 Stimmen
Ich glaube, Sie haben versehentlich Ihre Definitionen von "passed-by-value" und "passed-by-reference" vertauscht... "passed-by-value (wenn wir davon ausgehen, dass eine Variable, die ein Objekt enthält, in Wirklichkeit ein Verweis auf das Objekt ist) und passed-by-reference (wenn wir davon ausgehen, dass die Variable auf das Objekt das Objekt selbst enthält)"
11 Stimmen
Ja. Unabhängig von der Syntax bedeutet "pass-by-reference" bei einem Funktionsaufruf in einer beliebigen Programmiersprache, dass die mit der übergebenen Variablen verbundenen Daten bei der Übergabe an die Funktion nicht kopiert werden und somit alle von der Funktion an der übergebenen Variablen vorgenommenen Änderungen nach Beendigung des Funktionsaufrufs im Programm erhalten bleiben. Pass-by-Value bedeutet, dass die mit der Variablen verbundenen Daten bei der Übergabe an die Funktion tatsächlich kopiert werden und dass alle von der Funktion an der Variablen vorgenommenen Änderungen verloren gehen, wenn die Variable bei der Rückkehr der Funktion aus dem Anwendungsbereich des Funktionskörpers verschwindet.
0 Stimmen
Wenn eine Funktion eine Variable zurückgibt, ist der zurückgegebene Wert dann ein Verweis auf die Variable im Funktionskörper oder eine Kopie der Variable im Funktionskörper?
0 Stimmen
@JohnSonderson, ich glaube, dass der Geltungsbereich der zurückgegebenen Variablen standardmäßig derselbe ist wie der der Funktion, die die Variable zurückgibt (Sie können dies testen, indem Sie eine Funktion als Variable definieren und diese zurückgeben). Das führt mich zu der Annahme, dass der zurückgegebene Wert genauso behandelt wird wie die Übergabe eines Arguments an eine Funktion - es wird der Wert kopiert (pass by value), aber wenn sich der Wert auf ein Objekt bezieht, wird die Referenz auf dieses Objekt kopiert, nicht das Objekt selbst. WEITER: AFAIK,
object.create
ist der einzige Mechanismus, der ein Objekt in Javascript klont (siehe stackoverflow.com/q/728360/2155068 ).1 Stimmen
@MjrKusanagi Ich glaube nicht.
Object.create
kann als richtige Klontechnik betrachtet werden, d. h. wenn Sie die Originalkopie des Objekts ändern, wirkt sich dies auf den Klon aus, nur Änderungen am Klon wirken sich nicht auf das Originalobjekt aus. Natürlich hat es seinen Nutzen und JavaScript wurde um dieses Konzept herum entwickelt (prototypische Vererbung), aber ich denke, es ist irreführend, wenn manObject.create
der einzige Mechanismus zum Klonen eines Objekts.9 Stimmen
Diese alte Frage ist etwas giftig, weil die Antwort, für die viele Stimmen abgegeben wurden, falsch ist. JavaScript ist streng Pass-by-Value .
2 Stimmen
@Pointy Ich kann nicht erkennen, wie das in der Antwort beschriebene Verhalten von JavaScript falsch sein soll. Ich stimme jedoch zu, dass sie nicht viel beiträgt - sie wiederholt nur die Frage. In der Antwort, die Sie verlinkt haben, heißt es, dass JavaScript strikt pass-by-value ist. Sie basiert jedoch auf einer Interpretation, wie es funktioniert. Den gleichen Fehler macht der Blogbeitrag. Es gibt immer noch keinen Auszug aus der Spezifikation, der die eine oder die andere Interpretation unterstützt (pass-by-value für alles und Objektvariablen sind Referenzen oder pass-by-value für Primitive und pass-by-reference für Objekte).
9 Stimmen
@DanailNachev Die Terminologie ist bedauerlicherweise verwirrend. Die Sache ist die, dass "Wertübergabe" und "Referenzübergabe" Begriffe sind, die aus der Zeit vor vielen moderneren Programmiersprachen stammen. Die Wörter "Wert" und "Referenz" beziehen sich auf speziell auf den Parameter, wie er im Ausdruck des Funktionsaufrufs erscheint. JavaScript wertet immer jeden Ausdruck in einer Funktionsaufruf-Parameterliste aus vor Aufruf der Funktion, so dass die Parameter immer Werte sind. Das Verwirrende daran ist, dass Verweise auf Objekte übliche JavaScript-Werte sind. Das macht es aber nicht zu einer "pass by reference"-Sprache.
4 Stimmen
@DanailNachev "pass by reference" bedeutet konkret, dass man, wenn man
var x=3, y=x; f(x); alert(y === x);
dann Funktionf()
kann den Warnbericht erstellenfalse
und nichttrue
. In JavaScript ist das nicht möglich, also ist es nicht pass-by-reference. Es ist gut, dass es möglich ist, Referenzen auf veränderbare Objekte zu übergeben, aber das ist nicht das, was "pass by reference" bedeutet. Wie ich schon sagte, ist es schade, dass die Terminologie so verwirrend ist.1 Stimmen
@Pointy, ich glaube nicht, dass ich mit dem, was Sie sagen, nicht einverstanden bin. Aber ich denke, Sie sollten trotzdem die Call-by-Sharing Wikipedia-Link, der in dem Beitrag, über den Sie sich beschweren, genannt wird.
0 Stimmen
@DanailNachev Ich habe kein Problem mit dieser Terminologie ("Call-by-Sharing"), und es ist offensichtlich richtig, dass die Leute einen solchen Begriff wirklich wollen. Wie im Artikel erwähnt, ist er jedoch nicht gebräuchlich. Danke für den Hinweis!
0 Stimmen
Number und String sind keine Primitive. Sie sind Wrapper-Objekte, die eine Zahl oder einen String (Kleinbuchstaben) umhüllen, die Primitive sind.
1 Stimmen
Dies ist einer der vielen Gründe, warum JavaScript schlecht ist. In anderen Sprachen wie C und sogar PHP kann man die Argumentübergabe IMPLICITLY per Wert oder per Referenz festlegen. In JS ist es ein semantisches Durcheinander. Ich kann es kaum erwarten, dass WebAssembly zum Mainstream wird und den ersten Nagel in den Sarg von JS schlägt.
0 Stimmen
@RyanNerd: Sie können nicht in C. C ist immer pass-by-value. PHP hat callee-side pass-by-reference, ja, und es ist schrecklich. C# ist ein Beispiel für eine Sprache, die es richtig macht, indem sie explizite
ref
und eine Unterscheidung fürout
.1 Stimmen
@RyanNerd - Du liegst völlig falsch. JavaScript ist kein semantisches Chaos. In diesem Fall ist es geradlinig und konsistent: JavaScript ist "pass by value". Das war's. Mehr muss nicht gesagt werden. Dieses Verhalten wird jedem, der Java, C# oder PHP verwendet hat, völlig vertraut sein.
0 Stimmen
Sonderfall: Beibehaltung von Referenzen von Werten als Teil anderer Variablenbindungen. Betrachten Sie
const a = 1, b = a + 2, c = [ a, 2 ]; a = 4;
. Jetzt,b
ist noch3
, nicht6
;c
es[ 1, 2 ]
, nicht[ 4, 2 ]
. Es gibt keine inhärente Verbindung zwischena
und die anderen Variablen; die Tatsache, dass die eine als Teil der Definition der anderen verwendet wird, ist irrelevant. Nach der Lektüre dieser Q&A sollte es jedoch keine Überraschung sein, dass inconst a = { x: 1 }, b = [ a, { x: 2 } ]; a.x = 4
,b
es[ { x: 4 }, { x: 2 } ]
. Siehe Variablenwert ändern, der Teil der Definition einer anderen Variablen ist .