Ist JavaScript eine Pass-by-Reference- oder Pass-by-Value-Sprache?

1404

Die primitiven Typen (Zahl, Zeichenfolge usw.) werden als Wert übergeben, Objekte sind jedoch unbekannt, da sie beide als Wert übergeben werden können (falls wir davon ausgehen, dass eine Variable, die ein Objekt enthält, tatsächlich eine Referenz auf das Objekt ist ) und als Referenz übergeben (wenn wir bedenken, dass die Variable zum Objekt das Objekt selbst enthält).

Obwohl es am Ende nicht wirklich wichtig ist, möchte ich wissen, wie die Argumente, die Konventionen verabschieden, richtig präsentiert werden. Gibt es einen Auszug aus der JavaScript-Spezifikation, der definiert, wie die Semantik dazu aussehen soll?

Danail Nachev
quelle
2
Ich denke, Sie haben versehentlich Ihre Definitionen von Wertübergabe und Referenzübergabe umgedreht ... "Wertübergabe (falls wir davon ausgehen, dass eine Variable, die ein Objekt enthält, tatsächlich eine Referenz auf das Objekt ist) und übergeben -by-reference (wenn wir bedenken, dass die Variable zum Objekt das Objekt selbst enthält) "
Niko Bellic
5
Ja. Unabhängig von der Syntax bedeutet Pass-by-Reference bei jedem Funktionsaufruf in einer beliebigen Programmiersprache, dass die mit der übergebenen Variablen verknüpften Daten beim Übergeben an die Funktion nicht kopiert werden und daher alle von der Funktion an der übergebenen Variablen vorgenommenen Änderungen beibehalten werden im Programm nach Beendigung des Funktionsaufrufs. Wertübergabe bedeutet, dass die der Variablen zugeordneten Daten tatsächlich kopiert werden, wenn sie an die Funktion übergeben werden. Alle Änderungen, die diese Funktion an dieser Variablen vornimmt, gehen verloren, wenn die Variable bei der Rückkehr der Funktion den Gültigkeitsbereich des Funktionskörpers verlässt.
John Sonderson
5
Diese alte Frage ist etwas giftig, weil ihre stark positive Antwort falsch ist. JavaScript wird ausschließlich als Wert übergeben .
Pointy
6
@DanailNachev Die Terminologie ist leider verwirrend. Die Sache ist, "Pass by Value" und "Pass by Reference" sind Begriffe, die vor vielen moderneren Programmiersprachenfunktionen liegen. Die Wörter "Wert" und "Referenz" beziehen sich speziell auf den Parameter, wie er im Funktionsaufrufausdruck erscheint. JavaScript wertet jeden Ausdruck in einer Parameterliste für Funktionsaufrufe immer aus, bevor die Funktion aufgerufen wird. Die Parameter sind also immer Werte. Der verwirrende Teil ist, dass Verweise auf Objekte übliche JavaScript-Werte sind. Das macht es jedoch nicht zu einer "Pass by Reference" -Sprache.
Pointy
2
@DanailNachev "Referenzübergabe" bedeutet speziell, dass, wenn Sie var x=3, y=x; f(x); alert(y === x);dann Funktion haben f(), der Alarmbericht erstellt werden kann falseund nicht true. In JavaScript ist dies nicht möglich, daher wird es nicht als Referenz übergeben. Es ist gut, dass es möglich ist, Verweise auf veränderbare Objekte zu übergeben, aber das ist nicht das, was "Referenzübergabe" bedeutet. Wie gesagt, es ist eine Schande, dass die Terminologie so verwirrend ist.
Pointy

Antworten:

1598

Es ist interessant in JavaScript. Betrachten Sie dieses Beispiel:

function changeStuff(a, b, c)
{
  a = a * 10;
  b.item = "changed";
  c = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};

changeStuff(num, obj1, obj2);

console.log(num);
console.log(obj1.item);
console.log(obj2.item);

Dies erzeugt die Ausgabe:

10
changed
unchanged
  • Wenn dies überhaupt obj1keine Referenz obj1.itemwäre, hätte eine Änderung keine Auswirkung auf die obj1Außenseite der Funktion.
  • Wenn das Argument eine richtige Referenz wäre, hätte sich alles geändert. numwäre 100und obj2.itemwürde lesen "changed".

Stattdessen wird das übergebene Element als Wert übergeben. Das Element, das als Wert übergeben wird, ist selbst eine Referenz. Technisch wird dies als Call-by-Sharing bezeichnet .

In der Praxis bedeutet dies, dass sich das Ändern des Parameters selbst (wie bei numund obj2) nicht auf das Element auswirkt, das in den Parameter eingegeben wurde. Wenn Sie jedoch die INTERNEN des Parameters ändern , wird dies (wie bei obj1) wieder weitergegeben.

Deworde
quelle
32
Dies ist genau das gleiche (oder zumindest semantisch) wie C #. Das Objekt hat zwei Typen: Wert (primitive Typen) und Referenz.
Peter Lee
53
Ich denke, dies wird auch in Java verwendet: Referenz nach Wert.
Jpnh
295
Der wahre Grund ist, dass in changeStuff num, obj1 und obj2 Referenzen sind. Wenn Sie die itemEigenschaft des Objekts ändern, auf das obj1 verweist, ändern Sie den Wert der Elementeigenschaft, die ursprünglich auf "unverändert" festgelegt wurde. Wenn Sie obj2 den Wert {item: "defined"} zuweisen, ändern Sie den Verweis auf ein neues Objekt (das beim Beenden der Funktion sofort den Gültigkeitsbereich verlässt). Es wird deutlicher, was passiert, wenn Sie die Funktionsparameter wie numf, obj1f und obj2f benennen. Dann sehen Sie, dass die Parameter die externen Variablennamen versteckten.
Jinglesthula
13
@ BartoNaz Nicht wirklich. Sie möchten die Referenz als Referenz übergeben, anstatt die Referenz als Wert zu übergeben. Aber JavaScript übergibt die Referenz immer als Wert, genauso wie es alles andere als Wert übergibt. (Zum Vergleich hat C # ein ähnliches Verhalten wie JavaScript und Java, aber Sie können Pass-Reference-by-Reference mit dem refSchlüsselwort angeben .) Normalerweise muss die Funktion nur das neue Objekt zurückgeben und dies tun die Zuordnung an der Stelle, an der Sie die Funktion aufrufen. ZB foo = GetNewFoo();stattGetNewFoo(foo);
Tim Goodman
55
Obwohl diese Antwort die beliebteste ist, kann sie etwas verwirrend sein, da sie besagt: "Wenn es sich um einen reinen Wertübergang handelt". JavaScript ist eine reine Übergabe. Der übergebene Wert ist jedoch eine Referenz. Dies ist überhaupt nicht auf die Parameterübergabe beschränkt. Sie könnten die Variable einfach von kopieren var obj1 = { item: 'unchanged' }; var obj2 = obj1; obj2.item = 'changed';und würden den gleichen Effekt wie in Ihrem Beispiel beobachten. Deshalb verweise ich persönlich auf die Antwort von Tim Goodman
chiccodoro
476

Es wird immer als Wert übergeben, aber für Objekte ist der Wert der Variablen eine Referenz. Wenn Sie ein Objekt übergeben und seine Mitglieder ändern , bleiben diese Änderungen daher außerhalb der Funktion bestehen. Dadurch sieht es so aus, als würde man als Referenz übergeben. Wenn Sie jedoch den Wert der Objektvariablen tatsächlich ändern, werden Sie feststellen, dass die Änderung nicht fortbesteht, was beweist, dass sie tatsächlich als Wert übergeben wird.

Beispiel:

function changeObject(x) {
  x = { member: "bar" };
  console.log("in changeObject: " + x.member);
}

function changeMember(x) {
  x.member = "bar";
  console.log("in changeMember: " + x.member);
}

var x = { member: "foo" };

console.log("before changeObject: " + x.member);
changeObject(x);
console.log("after changeObject: " + x.member); /* change did not persist */

console.log("before changeMember: " + x.member);
changeMember(x);
console.log("after changeMember: " + x.member); /* change persists */

Ausgabe:

before changeObject: foo
in changeObject: bar
after changeObject: foo

before changeMember: foo
in changeMember: bar
after changeMember: bar
Tim Goodman
quelle
14
@daylight: Eigentlich liegst du falsch; Wenn es von const ref übergeben würde, würde der Versuch, changeObject auszuführen, einen Fehler verursachen, anstatt nur zu scheitern. Versuchen Sie, einer konstanten Referenz in C ++ einen neuen Wert zuzuweisen, und der Compiler lehnt ihn ab. In Bezug auf den Benutzer ist dies der Unterschied zwischen der Übergabe nach Wert und der Übergabe nach Konstante.
Deworde
5
@daylight: Es ist nicht konstant ref. In changeObjecthabe ich geändert x, um einen Verweis auf das neue Objekt zu enthalten. x = {member:"bar"};entspricht x = new Object(); x.member = "bar"; übrigens dem , was ich sage, auch für C #.
Tim Goodman
2
@daylight: Bei C # können Sie dies von außerhalb der Funktion sehen. Wenn Sie das refSchlüsselwort verwenden, können Sie die Referenz als Referenz übergeben (anstelle der Standardeinstellung, die Referenz als Wert zu übergeben), und die Änderung, auf a zu zeigen, new Object() bleibt bestehen .
Tim Goodman
11
@adityamenon Es ist schwer zu beantworten, warum, aber ich würde bemerken, dass die Designer von Java und C # eine ähnliche Wahl getroffen haben; Dies ist nicht nur eine JavaScript-Verrücktheit. Wirklich, es ist sehr konsequent Wertübergabe, was es für Menschen verwirrend macht, ist, dass ein Wert eine Referenz sein kann. Es ist nicht viel anders, als einen Zeiger (nach Wert) in C ++ zu übergeben und ihn dann zu dereferenzieren, um die Mitglieder festzulegen. Niemand würde sich wundern, dass diese Änderung anhält. Aber weil diese Sprachen den Zeiger abstrahieren und stillschweigend die Dereferenzierung für Sie durchführen, werden die Leute verwirrt.
Tim Goodman
41
Mit anderen Worten, das Verwirrende hier ist nicht Pass-by-Value / Pass-by-Reference. Alles ist pass-by-value, Punkt. Das Verwirrende ist, dass Sie weder ein Objekt übergeben noch ein Objekt in einer Variablen speichern können. Jedes Mal, wenn Sie glauben , dies zu tun, übergeben oder speichern Sie tatsächlich einen Verweis auf dieses Objekt. Wenn Sie jedoch auf seine Mitglieder zugreifen, kommt es zu einer stillen Dereferenzierung, die die Fiktion aufrechterhält, dass Ihre Variable das eigentliche Objekt enthielt.
Tim Goodman
150

Die Variable "hält" das Objekt nicht; es enthält eine Referenz. Sie können diese Referenz einer anderen Variablen zuweisen, und jetzt verweisen beide auf dasselbe Objekt. Es wird immer als Wert übergeben (auch wenn dieser Wert eine Referenz ist ...).

Es gibt keine Möglichkeit, den Wert einer als Parameter übergebenen Variablen zu ändern. Dies wäre möglich, wenn JavaScript die Übergabe als Referenz unterstützt.

Shog9
quelle
2
Das verwirrt mich nur ein bisschen. Wird eine Referenz nicht als Referenz übergeben?
8
Der Autor bedeutet, dass Sie durch Übergeben einer Referenz einen Referenzwert übergeben (eine andere Möglichkeit, sich das vorzustellen, besteht darin, den Wert der Speicheradresse zu übergeben). Wenn Sie das Objekt neu deklarieren, ändert sich das Original nicht, da Sie ein neues Objekt an einem anderen Speicherort erstellen. Wenn Sie eine Eigenschaft ändern, ändert sich das ursprüngliche Objekt, weil Sie es am ursprünglichen Speicherort geändert haben (der nicht neu zugewiesen wurde).
Huy-Anh Hoang
113

Meine zwei Cent ... So verstehe ich es. (Fühlen Sie sich frei, mich zu korrigieren, wenn ich falsch liege)

Es ist Zeit, alles, was Sie über Pass by Value / Reference wissen, wegzuwerfen.

Denn in JavaScript spielt es keine Rolle, ob es als Wert oder als Referenz oder was auch immer übergeben wird. Was zählt, ist die Mutation gegen die Zuordnung der an eine Funktion übergebenen Parameter.

OK, lassen Sie mich mein Bestes geben, um zu erklären, was ich meine. Angenommen, Sie haben einige Objekte.

var object1 = {};
var object2 = {};

Was wir getan haben, ist "Zuweisung" ... Wir haben den Variablen "Objekt1" und "Objekt2" 2 separate leere Objekte zugewiesen.

Nehmen wir nun an, wir mögen object1 besser ... Also "weisen" wir eine neue Variable zu.

var favoriteObject = object1;

Als nächstes entscheiden wir aus irgendeinem Grund, dass uns Objekt 2 besser gefällt. Also machen wir einfach eine kleine Neuzuweisung.

favoriteObject = object2;

Objekt1 oder Objekt2 ist nichts passiert. Wir haben überhaupt keine Daten geändert. Wir haben lediglich neu zugewiesen, was unser Lieblingsobjekt ist. Es ist wichtig zu wissen, dass Objekt2 und Favoritenobjekt beide demselben Objekt zugewiesen sind. Wir können dieses Objekt über eine dieser Variablen ändern.

object2.name = 'Fred';
console.log(favoriteObject.name) // Logs Fred
favoriteObject.name = 'Joe';
console.log(object2.name); // Logs Joe

OK, jetzt schauen wir uns Primitive wie zum Beispiel Strings an

var string1 = 'Hello world';
var string2 = 'Goodbye world';

Wieder wählen wir einen Favoriten.

var favoriteString = string1;

Sowohl unsere Favoriten-Variablen stringString als auch string1 sind 'Hello world' zugeordnet. Was ist, wenn wir unseren Favoriten ändern wollen? Was wird passieren???

favoriteString = 'Hello everyone';
console.log(favoriteString); // Logs 'Hello everyone'
console.log(string1); // Logs 'Hello world'

Oh oh ... was ist passiert? Wir konnten string1 nicht ändern, indem wir favorString geändert haben ... Warum? Weil wir unser String- Objekt nicht geändert haben . Alles, was wir getan haben, war "RE ASSIGN", die Variable "avorString " für einen neuen String. Dies trennte es im Wesentlichen von string1. Im vorherigen Beispiel haben wir beim Umbenennen unseres Objekts nichts zugewiesen. (Nun, nicht der Variablen selbst , ... wir haben jedoch die Eigenschaft name einer neuen Zeichenfolge zugewiesen.) Stattdessen haben wir einfach das Objekt mutiert, das die Verbindungen zwischen den beiden Variablen und den zugrunde liegenden Objekten beibehält. (Auch wenn wir das String-Objekt selbst ändern oder mutieren wollten, konnten wir nicht haben, weil Strings in JavaScript tatsächlich unveränderlich sind.)

Nun zu Funktionen und Übergabe von Parametern ... Wenn Sie eine Funktion aufrufen und einen Parameter übergeben, tun Sie im Wesentlichen eine "Zuweisung" zu einer neuen Variablen, und dies funktioniert genauso, als ob Sie einfach mit zugewiesen hätten das Gleichheitszeichen (=).

Nehmen Sie diese Beispiele.

var myString = 'hello';

// Assign to a new variable (just like when you pass to a function)
var param1 = myString;
param1 = 'world'; // Re assignment

console.log(myString); // Logs 'hello'
console.log(param1);   // Logs 'world'

Nun das Gleiche, aber mit einer Funktion

function myFunc(param1) {
    param1 = 'world';

    console.log(param1);   // Logs 'world'
}

var myString = 'hello';
// Calls myFunc and assigns param1 to myString just like param1 = myString
myFunc(myString);

console.log(myString); // logs 'hello'

OK, jetzt geben wir ein paar Beispiele, die stattdessen Objekte verwenden ... zuerst ohne die Funktion.

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Assign to a new variable (just like when you pass to a function)
var otherObj = myObject;

// Let's mutate our object
otherObj.firstName = 'Sue'; // I guess Joe decided to be a girl

console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Sue'

// Now, let's reassign the variable
otherObj = {
    firstName: 'Jack',
    lastName: 'Frost'
};

// Now, otherObj and myObject are assigned to 2 very different objects
// And mutating one object has no influence on the other
console.log(myObject.firstName); // Logs 'Sue'
console.log(otherObj.firstName); // Logs 'Jack';

Nun das Gleiche, aber mit einem Funktionsaufruf

function myFunc(otherObj) {

    // Let's mutate our object
    otherObj.firstName = 'Sue';
    console.log(otherObj.firstName); // Logs 'Sue'

    // Now let's re-assign
    otherObj = {
        firstName: 'Jack',
        lastName: 'Frost'
    };
    console.log(otherObj.firstName); // Logs 'Jack'

    // Again, otherObj and myObject are assigned to 2 very different objects
    // And mutating one object doesn't magically mutate the other
}

var myObject = {
    firstName: 'Joe',
    lastName: 'Smith'
};

// Calls myFunc and assigns otherObj to myObject just like otherObj = myObject
myFunc(myObject);

console.log(myObject.firstName); // Logs 'Sue', just like before

OK, wenn Sie diesen gesamten Beitrag durchlesen, haben Sie jetzt vielleicht ein besseres Verständnis dafür, wie Funktionsaufrufe in JavaScript funktionieren. Es spielt keine Rolle, ob etwas als Referenz oder als Wert übergeben wird ... Was zählt, ist Zuordnung gegen Mutation.

Jedes Mal, wenn Sie eine Variable an eine Funktion übergeben, "weisen" Sie dem Namen der Parametervariablen "zu", genau wie wenn Sie das Gleichheitszeichen (=) verwendet haben.

Denken Sie immer daran, dass das Gleichheitszeichen (=) Zuweisung bedeutet. Denken Sie immer daran, dass das Übergeben eines Parameters an eine Funktion in JavaScript auch eine Zuweisung bedeutet. Sie sind gleich und die beiden Variablen sind genau gleich miteinander verbunden (dh sie sind es nicht, es sei denn, Sie zählen, dass sie demselben Objekt zugewiesen sind).

Das "Ändern einer Variablen" wirkt sich nur dann auf eine andere Variable aus, wenn das zugrunde liegende Objekt mutiert ist (in diesem Fall haben Sie nicht die Variable, sondern das Objekt selbst geändert.

Es macht keinen Sinn, zwischen Objekten und Grundelementen zu unterscheiden, da dies genauso funktioniert, als ob Sie keine Funktion hätten und nur das Gleichheitszeichen verwendet hätten, um einer neuen Variablen zuzuweisen.

Das einzige Problem ist, wenn der Name der Variablen, die Sie an die Funktion übergeben, mit dem Namen des Funktionsparameters übereinstimmt. In diesem Fall müssen Sie den Parameter in der Funktion so behandeln, als wäre er eine ganz neue Variable, die für die Funktion privat ist (weil dies der Fall ist).

function myFunc(myString) {
    // myString is private and does not affect the outer variable
    myString = 'hello';
}

var myString = 'test';
myString = myString; // Does nothing, myString is still 'test';

myFunc(myString);
console.log(myString); // Logs 'test'
Ray Perea
quelle
2
Denken Sie bei allen C-Programmierern an char *. foo(char *a){a="hello";} tut nichts, aber wenn Sie dies tun, wird foo(char *a){a[0]='h';a[1]='i';a[2]=0;}es außerhalb geändert, da aein Speicherort als Wert übergeben wird, der auf eine Zeichenfolge verweist (char-Array). Das Übergeben von Strukturen (ähnlich wie bei js-Objekten) als Wert in C ist zulässig, wird jedoch nicht empfohlen. JavaScript setzt diese Best Practices einfach durch und verbirgt die unnötigen und normalerweise unerwünschten Cruft ... und erleichtert das Lesen.
Technosaurus
2
Dies ist richtig - die Begriffe Pass-by-Value und Pass-by-Reference haben Bedeutungen im Design von Programmiersprachen, und diese Bedeutungen haben überhaupt nichts mit Objektmutation zu tun. Es geht darum, wie Funktionsparameter funktionieren.
Pointy
2
Jetzt, da ich verstehe, dass obj1 = obj2 bedeutet, dass sowohl obj1 als auch obj2 jetzt auf denselben Referenzort zeigen, und wenn ich die Interna von obj2 ändere, werden durch Verweisen auf obj1 dieselben Interna verfügbar gemacht. Wie kopiere ich ein Objekt so, source = { "id":"1"}; copy = source /*this is wrong*/; copy.id="2"dass diese Quelle immer noch {"id": "1"} ist?
Machtyn
1
Ich habe eine weitere Antwort mit traditionellen Definitionen veröffentlicht, um hoffentlich die Verwirrung zu verringern. Traditionelle Definitionen von "Pass-by-Value" und "Pass-by-Reference" wurden bereits am Tag der Speicherzeiger vor der automatischen Dereferenzierung definiert. Es war völlig klar, dass der Wert einer Objektvariablen tatsächlich der Speicherzeigerort war, nicht das Objekt. Obwohl Ihre Diskussion über Zuordnung gegen Mutation vielleicht nützlich ist, ist es nicht notwendig, die traditionellen Begriffe oder ihre Definitionen wegzuwerfen. Mutation, Zuordnung, Wertübergabe, Referenzübergabe usw. dürfen sich nicht widersprechen.
C Perkins
ist "Zahl" auch eine "unveränderliche"?
Ebram Khalil
72

Folgendes berücksichtigen:

  1. Variablen sind Zeiger auf Werte im Speicher.
  2. Wenn Sie eine Variable neu zuweisen, zeigt dieser Zeiger lediglich auf einen neuen Wert.
  3. Das Neuzuweisen einer Variablen wirkt sich niemals auf andere Variablen aus, die auf dasselbe Objekt zeigen

Also, vergessen Sie „Pass von reference / Wert“ nicht auf aufgehängt bekommen „vorbei reference / Wert“ , weil:

  1. Die Begriffe werden nur verwendet, um das Verhalten einer Sprache zu beschreiben , nicht unbedingt die tatsächlich zugrunde liegende Implementierung. Infolge dieser Abstraktion gehen kritische Details verloren, die für eine anständige Erklärung wesentlich sind, was unweigerlich zu der aktuellen Situation führt, in der ein einzelner Begriff das tatsächliche Verhalten nicht angemessen beschreibt und zusätzliche Informationen bereitgestellt werden müssen
  2. Diese Konzepte wurden ursprünglich nicht mit der Absicht definiert, insbesondere Javascript zu beschreiben, und daher fühle ich mich nicht gezwungen, sie zu verwenden, wenn sie nur zur Verwirrung beitragen.

Um Ihre Frage zu beantworten: Zeiger werden übergeben.


// code
var obj = {
    name: 'Fred',
    num: 1
};

// illustration
               'Fred'
              /
             /
(obj) ---- {}
             \
              \
               1


// code
obj.name = 'George';


// illustration
                 'Fred'


(obj) ---- {} ----- 'George'
             \
              \
               1


// code
obj = {};

// illustration
                 'Fred'


(obj)      {} ----- 'George'
  |          \
  |           \
 { }            1


// code
var obj = {
    text: 'Hello world!'
};

/* function parameters get their own pointer to 
 * the arguments that are passed in, just like any other variable */
someFunc(obj);


// illustration
(caller scope)        (someFunc scope)
           \             /
            \           /
             \         /
              \       /
               \     /
                 { }
                  |
                  |
                  |
            'Hello world'

Einige abschließende Kommentare:

  • Es ist verlockend zu glauben , dass Primitive durch spezielle Regeln durchgesetzt werden , während Objekte sind nicht, aber Primitive sind einfach das Ende der Zeigerkette.
  • Betrachten Sie als letztes Beispiel, warum ein häufiger Versuch, ein Array zu löschen, nicht wie erwartet funktioniert.


var a = [1,2];
var b = a;

a = [];
console.log(b); // [1,2]
// doesn't work because `b` is still pointing at the original array
geg
quelle
Anschlussfragen für zusätzliche Gutschrift;) Wie funktioniert die Speicherbereinigung? Wenn ich eine Variable durch eine Million {'George', 1}Werte durchlaufe , aber jeweils nur einen davon verwende, wie werden dann die anderen verwaltet? Und was passiert, wenn ich dem Wert einer anderen Variablen eine Variable zuordne? Zeige ich dann auf einen Zeiger oder auf den Zeiger des richtigen Operanden? Hat var myExistingVar = {"blah", 42}; var obj = myExistingVar;Ergebnis in objzeigt auf {"blah", 42}, oder myExistingVar?
Michael Hoffmann
@MichaelHoffmann Diese verdienen ihre eigenen SO-Fragen und werden wahrscheinlich schon besser beantwortet, als ich es schaffen kann. Davon abgesehen habe 1)ich in den Browser-Entwicklungstools ein Speicherprofil für eine Schleifenfunktion wie die von Ihnen beschriebene ausgeführt und während des gesamten Schleifenprozesses Spitzen in der Speichernutzung festgestellt. Dies scheint darauf hinzudeuten, dass in jeder Iteration der Schleife tatsächlich neue identische Objekte erstellt werden. Wenn die Stacheln plötzlich fallen, hat der Müllsammler gerade eine Gruppe dieser nicht verwendeten Objekte aufgeräumt.
geg
1
@MichaelHoffmann 2)In Bezug auf so etwas var a = bbietet Javascript keinen Mechanismus für die Verwendung von Zeigern, sodass eine Variable niemals auf einen Zeiger verweisen kann (wie in C), obwohl die zugrunde liegende Javascript-Engine sie zweifellos verwendet. Also ... var a = bwird a"auf den Pointee des richtigen Operanden" zeigen
geg
Ich habe hier Frage 1 gestellt (speziell zu Chrome, da die Implementierung wahrscheinlich in jedem Browser unterschiedlich ist) stackoverflow.com/q/42778439/539997 und ich versuche immer noch zu überlegen , wie Frage 2 formuliert werden soll . Jede Hilfe wird geschätzt.
Michael Hoffmann
1
Es ist nicht nötig, "Referenz / Wert übergeben" zu vergessen ! Diese Begriffe haben historische Bedeutungen, die genau beschreiben, was Sie zu beschreiben versuchen. Wenn wir die historischen Begriffe und Definitionen wegwerfen und zu faul werden, um zu erfahren, was sie ursprünglich bedeuteten, verlieren wir die Fähigkeit, effektiv zwischen den Generationen zu kommunizieren. Es würde keinen guten Weg geben, die Unterschiede zwischen verschiedenen Sprachen und Systemen zu diskutieren. Stattdessen müssen neue Programmierer die traditionellen Begriffe lernen und verstehen und warum und woher sie kamen. Andernfalls verlieren wir gemeinsam Wissen und Verständnis.
C Perkins
24

Ein Objekt außerhalb einer Funktion wird an eine Funktion übergeben, indem auf das externe Objekt verwiesen wird.

Wenn Sie diese Referenz verwenden, um das Objekt zu bearbeiten, ist das Objekt außerhalb davon betroffen. Wenn Sie sich jedoch innerhalb der Funktion entschieden haben, den Verweis auf etwas anderes zu verweisen, haben Sie das Objekt außerhalb überhaupt nicht beeinflusst, da Sie lediglich den Verweis auf etwas anderes umgeleitet haben.

user779764
quelle
20

Stellen Sie sich das so vor: Es geht immer um Wert. Der Wert eines Objekts ist jedoch nicht das Objekt selbst, sondern ein Verweis auf dieses Objekt.

Hier ist ein Beispiel für die Übergabe einer Zahl (ein primitiver Typ).

function changePrimitive(val) {
    // At this point there are two '10's in memory.
    // Changing one won't affect the other
    val = val * 10;
}
var x = 10;
changePrimitive(x);
// x === 10

Wenn Sie dies mit einem Objekt wiederholen, erhalten Sie unterschiedliche Ergebnisse:

function changeObject(obj) {
    // At this point there are two references (x and obj) in memory,
    // but these both point to the same object.
    // changing the object will change the underlying object that
    // x and obj both hold a reference to.
    obj.val = obj.val * 10;
}
var x = { val: 10 };
changeObject(x);
// x === { val: 100 }

Noch ein Beispiel:

function changeObject(obj) {
    // Again there are two references (x and obj) in memory,
    // these both point to the same object.
    // now we create a completely new object and assign it.
    // obj's reference now points to the new object.
    // x's reference doesn't change.
    obj = { val: 100 };
}
var x = { val: 10 };
changeObject(x);
// x === { val: 10}
Phil Mander
quelle
19

Eine sehr detaillierte Erklärung zum Kopieren, Übergeben und Vergleichen nach Wert und Referenz finden Sie in diesem Kapitel des Buches "JavaScript: The Definitive Guide" .

Bevor wir das Thema der Manipulation von Objekten und Arrays als Referenz verlassen, müssen wir einen Punkt der Nomenklatur klären.

Der Ausdruck "Referenzübergabe" kann mehrere Bedeutungen haben. Für einige Leser bezieht sich der Ausdruck auf eine Funktionsaufruftechnik, mit der eine Funktion ihren Argumenten neue Werte zuweisen und diese geänderten Werte außerhalb der Funktion sichtbar machen kann. Dies ist nicht die Art und Weise, wie der Begriff in diesem Buch verwendet wird.

Hier meinen wir einfach, dass ein Verweis auf ein Objekt oder Array - nicht auf das Objekt selbst - an eine Funktion übergeben wird. Eine Funktion kann die Referenz verwenden, um Eigenschaften des Objekts oder der Elemente des Arrays zu ändern. Wenn die Funktion die Referenz jedoch mit einer Referenz auf ein neues Objekt oder Array überschreibt, ist diese Änderung außerhalb der Funktion nicht sichtbar.

Leser, die mit der anderen Bedeutung dieses Begriffs vertraut sind, möchten vielleicht lieber sagen, dass Objekte und Arrays als Wert übergeben werden, aber der übergebene Wert ist tatsächlich eine Referenz und nicht das Objekt selbst.

igor
quelle
Wow, das ist unglaublich verwirrend. Wer würde bei klarem Verstand einen gut etablierten Begriff als genau das Gegenteil definieren und ihn dann so verwenden? Kein Wunder, dass so viele Antworten auf diese Frage so verwirrt sind.
Jörg W Mittag
16

JavaScript wird immer als Wert übergeben . Alles ist vom Werttyp.

Objekte sind Werte, und Elementfunktionen von Objekten sind selbst Werte (denken Sie daran, dass Funktionen in JavaScript erstklassige Objekte sind). Auch in Bezug auf das Konzept, dass alles in JavaScript ein Objekt ist ; das ist falsch. Streicher, Symbole, Zahlen, Boolesche Werte, Nullen und undefineds sind Primitiven .

Gelegentlich können sie einige Mitgliedsfunktionen und -eigenschaften nutzen, die von ihren Basisprototypen geerbt wurden. Dies dient jedoch nur der Bequemlichkeit. Dies bedeutet nicht, dass sie selbst Objekte sind. Versuchen Sie Folgendes als Referenz:

x = "test";
alert(x.foo);
x.foo = 12;
alert(x.foo);

In beiden Warnungen finden Sie den Wert als undefiniert.

Michael Roberts
quelle
12
-1, es wird nicht immer als Wert übergeben. Von MDC: "Wenn Sie ein Objekt (dh einen nicht primitiven Wert wie Array oder ein benutzerdefiniertes Objekt) als Parameter übergeben, wird ein Verweis auf das Objekt an die Funktion übergeben."
Nick
37
@ Nick: Es wird immer als Wert übergeben. Zeitraum. Ein Verweis auf das Objekt wird als Wert an die Funktion übergeben. Das ist kein Hinweis. "Referenzübergabe" könnte fast als Übergabe der Variablen selbst und nicht als Wert angesehen werden. Alle Änderungen, die die Funktion am Argument vornimmt (einschließlich des vollständigen Ersetzens durch ein anderes Objekt!), werden im Aufrufer wiedergegeben. Dieses letzte Bit ist in JS nicht möglich, da JS nicht als Referenz übergeben wird - es übergibt Referenzen als Wert. Die Unterscheidung ist subtil, aber wichtig, um die Grenzen zu verstehen.
CHao
1
Für zukünftige Stapler ... Über Ihre Referenz: x = "teste"; x.foo = 12;usw. Nur weil eine Eigenschaft nicht persistent ist, heißt das nicht, dass sie kein Objekt ist. Wie MDN sagt: In JavaScript ist fast alles ein Objekt. Alle primitiven Typen außer null und undefiniert werden als Objekte behandelt. Ihnen können Eigenschaften zugewiesen werden (zugewiesene Eigenschaften einiger Typen sind nicht persistent) und sie haben alle Eigenschaften von Objekten. Link
Slacktracer
9
MDN ist ein vom Benutzer bearbeitetes Wiki und dort ist es falsch. Die normative Referenz ist ECMA-262. Siehe S. 8 "Der Referenzspezifikationstyp", in dem erläutert wird, wie Referenzen aufgelöst werden, und 8.12.5 "[[Put]]", in dem der Zuweisungsausdruck zu einer Referenz erläutert wird, und 9.9 ToObject für die Objektzusammensetzung. Für primitive Werte hat Michael bereits erklärt, was ToObject wie in der Spezifikation tut. Siehe aber auch s. 4.3.2 Grundwert.
Garrett
1
@ WonderLand: Nein, ist er nicht. Personen, die noch nie in der Lage waren, eine Referenz zu übergeben, verstehen möglicherweise nie die Unterschiede zwischen einer Referenzübergabe und einer Wertübergabe. Aber sie sind da und sie sind wichtig. Es ist mir egal, Leute falsch zu informieren, nur weil es einfacher klingt.
CHao
12

In JavaScript steuert der Werttyp ausschließlich , ob dieser Wert durch Wertkopie oder Referenzkopie zugewiesen wird .

Primitive Werte werden immer von value-copy zugewiesen / übergeben :

  • null
  • undefined
  • Zeichenfolge
  • Nummer
  • Boolescher Wert
  • Symbol in ES6

Zusammengesetzte Werte werden immer per Referenzkopie zugewiesen / übergeben

  • Objekte
  • Arrays
  • Funktion

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]

Da 2es sich bei dem obigen Snippet um ein skalares Grundelement handelt, aenthält es eine erste Kopie dieses Werts und berhält eine weitere Kopie des Werts. Beim Ändern ändern bSie in keiner Weise den Wert in a.

Aber beide cund dsind separate Verweise auf denselben gemeinsamen Wert [1,2,3], der ein zusammengesetzter Wert ist. Es ist wichtig zu beachten, dass weder cnoch dmehr den [1,2,3]Wert "besitzen" - beide sind nur gleiche Peer-Verweise auf den Wert. Wenn Sie also eine der Referenzen verwenden, um .push(4)den tatsächlichen gemeinsamen arrayWert selbst zu ändern ( ) , wirkt sich dies nur auf den einen gemeinsamen Wert aus, 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 die Zuweisung vornehmen b = [4,5,6], tun wir absolut nichts, um zu beeinflussen, wo anoch referenziert wird ( [1,2,3]). Um dies zu tun, bmüsste es eher ein Zeiger aals ein Verweis auf das sein array- aber in JS gibt es keine solche Fähigkeit!

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 übergeben a, weist es eine Kopie des aVerweises zu x. xund asind separate Referenzen, die auf denselben [1,2,3]Wert verweisen . Jetzt können wir innerhalb der Funktion diese Referenz verwenden, um den Wert selbst zu mutieren ( push(4)). Wenn wir die Zuweisung vornehmen x = [4,5,6], hat dies jedoch keinen Einfluss darauf, wohin die ursprüngliche Referenz azeigt - zeigt immer noch auf den (jetzt geänderten) [1,2,3,4]Wert.

Um einen zusammengesetzten Wert (wie einen array) effektiv als Wertkopie zu übergeben, 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 per Referenzkopie übergeben werden kann

function foo(wrapper) {
    wrapper.a = 42;
}

var obj = {
    a: 2
};

foo( obj );

obj.a; // 42

Hier objfungiert es als Wrapper für die skalare primitive Eigenschaft a. Bei der Übergabe an foo(..)wird eine Kopie der objReferenz übergeben und auf den wrapperParameter gesetzt. Wir können jetzt die wrapperReferenz verwenden, um auf das freigegebene Objekt zuzugreifen und seine Eigenschaft zu aktualisieren. Nach Abschluss der Funktion obj.awird der aktualisierte Wert angezeigt 42.

Quelle

zangw
quelle
Sie geben zuerst an, dass zusammengesetzte Werte immer als Referenzkopie zugewiesen / übergeben werden, und geben dann an, dass x eine Kopie der Referenz zugewiesen wird . Bei einem sogenannten "zusammengesetzten Wert" ist der tatsächliche Variablenwert die Referenz (dh der Speicherzeiger). Genau wie Sie erklärt haben, wird die Referenz kopiert ... also wird der Variablenwert kopiert , was wiederum betont, dass die REFERENZ DER WERT IST. Das bedeutet, dass JavaScript für alle Typen als Wert übergeben wird. Wertübergabe bedeutet, eine Kopie des Variablenwerts zu übergeben. Es spielt keine Rolle, dass der Wert eine Referenz auf ein Objekt / Array ist.
C Perkins
Sie führen eine neue Terminologie ein (Wert-Kopie / Referenz-Kopie) und das macht die Dinge nur komplexer. Es gibt nur Kopien, Punkt. Wenn Sie ein Grundelement übergeben, haben Sie eine Kopie der tatsächlichen Grunddaten übergeben. Wenn Sie ein Objekt übergeben, haben Sie eine Kopie des Speicherorts des Objekts übergeben. Das ist alles was du sagen musst. Alles andere verwirrt die Menschen nur noch weiter.
Scott Marcus
9

Nun, es geht um "Leistung" und "Geschwindigkeit" und um das einfache Wort "Speicherverwaltung" in einer Programmiersprache.

In Javascript können wir Werte in zwei Ebenen einfügen : Typ1 - objectsund Typ2 - alle anderen Werttypen wie string& boolean& etc.

Wenn Sie sich Speicher wie unter Quadraten vorstellen, in denen in jedem von ihnen nur ein Typ2-Wert gespeichert werden kann:

Geben Sie hier die Bildbeschreibung ein

Jeder Typ2-Wert (grün) ist ein einzelnes Quadrat, während ein Typ1-Wert (blau) eine Gruppe davon ist :

Geben Sie hier die Bildbeschreibung ein

Der Punkt ist, dass, wenn Sie einen Typ2-Wert angeben möchten, die Adresse einfach ist, aber wenn Sie dasselbe für den Typ1-Wert tun möchten, ist das überhaupt nicht einfach! ::

Geben Sie hier die Bildbeschreibung ein

und in einer komplizierteren Geschichte:

Geben Sie hier die Bildbeschreibung ein

Hier können uns Referenzen retten: Geben Sie hier die Bildbeschreibung ein

Während der grüne Pfeil hier eine typische Variable ist, ist der lila Pfeil eine Objektvariable. Da der grüne Pfeil (typische Variable) nur eine Aufgabe hat (und dies zeigt einen typischen Wert an), müssen wir seinen Wert nicht von diesem trennen es so bewegen wir den grünen Pfeil mit dem Wert davon, wohin es geht und in allen Aufgaben, Funktionen und so weiter ...

Aber wir können nicht dasselbe mit dem lila Pfeil machen. Vielleicht möchten wir die 'John'-Zelle hierher bewegen oder viele andere Dinge ..., damit der lila Pfeil an seiner Stelle bleibt und nur typische Pfeile, die ihm zugewiesen wurden, sich bewegen ...

In einer sehr verwirrenden Situation können Sie nicht erkennen, wie sich Ihre referenzierte Variable ändert. Schauen wir uns ein sehr gutes Beispiel an:

let arr = [1, 2, 3, 4, 5]; //arr is an object now and a purple arrow is indicating it
let obj2 = arr; // now, obj2 is another purple arrow that is indicating the value of arr obj
let obj3 = ['a', 'b', 'c'];
obj2.push(6); // first pic below - making a new hand for the blue circle to point the 6
//obj2 = [1, 2, 3, 4, 5, 6]
//arr = [1, 2, 3, 4, 5, 6]
//we changed the blue circle object value (type1-value) and due to arr and obj2 are indicating that so both of them changed
obj2 = obj3; //next pic below - changing the direction of obj2 array from blue circle to orange circle so obj2 is no more [1,2,3,4,5,6] and it's no more about changing anything in it but we completely changed its direction and now obj2 is pointing to obj3
//obj2 = ['a', 'b', 'c'];
//obj3 = ['a', 'b', 'c'];

Geben Sie hier die Bildbeschreibung ein Geben Sie hier die Bildbeschreibung ein

ashkan nasirzadeh
quelle
8

Dies ist kaum eine Erklärung für die Übergabe von Wert und Übergabe als Referenz (JavaScript). In diesem Konzept geht es darum, die Variable als Referenz und die Variable als Referenz zu übergeben.

Wert übergeben (primitiver Typ)

var a = 3;
var b = a;

console.log(a); // a = 3
console.log(b); // b = 3

a=4;
console.log(a); // a = 4
console.log(b); // b = 3
  • gilt für alle primitiven Typen in JavaScript (Zeichenfolge, Zahl, Boolescher Wert, undefiniert und null).
  • a wird ein Speicher zugewiesen (z. B. 0x001) und b erstellt eine Kopie des Werts im Speicher (z. B. 0x002).
  • Das Ändern des Werts einer Variablen wirkt sich also nicht auf die andere aus, da sich beide an zwei verschiedenen Orten befinden.

Referenzübergabe (Objekte)

var c = { "name" : "john" };
var d = c;

console.log(c); // { "name" : "john" }
console.log(d); // { "name" : "john" }

c.name = "doe";

console.log(c); // { "name" : "doe" }
console.log(d); // { "name" : "doe" }
  • Die JavaScript-Engine weist das Objekt der Variablen zu cund zeigt auf etwas Speicher, z. B. (0x012).
  • Wenn d = c ist, zeigt in diesem Schritt dauf dieselbe Stelle (0x012).
  • Durch Ändern des Werts eines Änderungswerts für beide Variablen.
  • Funktionen sind Objekte

Sonderfall, Referenzübergabe (Objekte)

c = {"name" : "jane"};
console.log(c); // { "name" : "jane" }
console.log(d); // { "name" : "doe" }
  • Der Operator gleich (=) richtet einen neuen Speicherplatz oder eine neue Adresse ein
Ashish Singh Rawat
quelle
In Ihrem sogenannten Sonderfall ist es nicht der Zuweisungsoperator, der die Zuweisung von Speicherplatz bewirkt, sondern das Objektliteral selbst. Die Curley-Klammer-Notation bewirkt die Erstellung eines neuen Objekts. Die Eigenschaft cwird auf eine Kopie der Referenz des neuen Objekts festgelegt.
Georgeawg
6

Teilen, was ich über Referenzen in JavaScript weiß

Wenn Sie in JavaScript einer Variablen ein Objekt zuweisen, ist der der Variablen zugewiesene Wert eine Referenz auf das Objekt:

var a = {
  a: 1,
  b: 2,
  c: 3
};
var b = a;

// b.c is referencing to a.c value
console.log(b.c) // Output: 3
// Changing value of b.c
b.c = 4
// Also changes the value of a.c
console.log(a.c) // Output: 4

Xameeramir
quelle
1
Dies ist eine zu vereinfachende Antwort, die nichts aussagt, was frühere Antworten nicht besser erklärt haben. Ich bin verwirrt darüber, warum Sie Arrays als Sonderfall aufrufen.
Quentin
1
" Objekte werden als Referenzen gespeichert " ist irreführend. Ich meine, Sie meinen, wenn Sie einer Variablen ein Objekt zuweisen, ist der der Variablen zugewiesene Wert eine Referenz auf das Objekt.
RobG
Dies behebt nicht das Problem des Aktualisierens eines Objekts innerhalb einer Funktion, das das Objekt außerhalb der Funktion nicht aktualisiert. Das ist das ganze Bild, in dem es als Werte statt als Referenz zu funktionieren scheint. Daher -1
amaster
@amaster Danke, dass du darauf hingewiesen hast! Können Sie bitte eine Bearbeitung vorschlagen?
Xameeramir
Haha, ich habe es versucht ... meine vorgeschlagene Änderung wurde zu stark geändert und war nicht erlaubt
amaster
4

Semantik!! Das Festlegen konkreter Definitionen führt zwangsläufig dazu, dass einige Antworten und Kommentare nicht kompatibel sind, da sie nicht dasselbe beschreiben, selbst wenn dieselben Wörter und Ausdrücke verwendet werden. Es ist jedoch wichtig, die Verwirrung zu überwinden (insbesondere für neue Programmierer).

Erstens gibt es mehrere Abstraktionsebenen, die nicht jeder zu verstehen scheint. Neuere Programmierer, die Sprachen der 4. oder 5. Generation gelernt haben, haben möglicherweise Schwierigkeiten, sich mit Konzepten zu beschäftigen, die Assembler- oder C-Programmierern vertraut sind, die nicht durch Zeiger auf Zeiger auf Zeiger gestaffelt sind. Referenzübergabe bedeutet nicht einfach die Möglichkeit, ein referenziertes Objekt mithilfe einer Funktionsparametervariablen zu ändern.

Variable : Kombiniertes Konzept eines Symbols, das auf einen Wert an einer bestimmten Stelle im Speicher verweist. Dieser Begriff ist normalerweise zu geladen, um allein zur Erörterung von Details verwendet zu werden.

Symbol : Textzeichenfolge, die auf die Variable verweist (dh den Namen der Variablen).

Wert : Bestimmte Bits, die im Speicher gespeichert sind und mit dem Variablensymbol referenziert werden.

Speicherort : Hier wird der Wert einer Variablen gespeichert. (Der Ort selbst wird durch eine Zahl dargestellt, die von dem am Ort gespeicherten Wert getrennt ist.)

Funktionsparameter : In einer Funktionsdefinition deklarierte Variable, die zum Verweisen auf an die Funktion übergebene Variablen verwendet wird.

Funktionsargument : Variable außerhalb der Funktion, die vom Aufrufer an die Funktion übergeben wird.

Objektvariable : Variable, deren grundlegender Grundwert nicht das "Objekt" selbst ist, sondern deren Wert ein Zeiger (Speicherplatzwert) auf einen anderen Speicherort ist, an dem die tatsächlichen Daten des Objekts gespeichert sind. In den meisten Sprachen der höheren Generation wird der Aspekt "Zeiger" durch automatische De-Referenzierung in verschiedenen Kontexten effektiv verborgen.

Primitive Variable : Variable, deren Wert der tatsächliche Wert ist. Selbst dieses Konzept kann durch Auto-Boxing und objektähnliche Kontexte verschiedener Sprachen kompliziert werden. Die allgemeine Idee ist jedoch, dass der Wert der Variablen der tatsächliche Wert ist, der durch das Symbol der Variablen dargestellt wird, und nicht ein Zeiger auf einen anderen Speicherort.

Funktionsargumente und Parameter sind nicht dasselbe. Außerdem ist der Wert einer Variablen nicht das Objekt der Variablen (wie bereits von verschiedenen Personen hervorgehoben, aber anscheinend ignoriert). Diese Unterscheidungen sind entscheidend für das richtige Verständnis.

Pass-by-Value oder Call-by-Sharing (für Objekte): Der Wert des Funktionsarguments wird auf einen anderen Speicherort kopiert, auf den das Parametersymbol der Funktion verweist (unabhängig davon, ob er sich auf dem Stapel oder auf dem Heap befindet). Mit anderen Worten, der Funktionsparameter hat eine Kopie des Wertes des übergebenen Arguments erhalten ... UND (kritisch) der Wert des Arguments wird von der aufrufenden Funktion NIE AKTUALISIERT / GEÄNDERT / GEÄNDERT. Denken Sie daran, dass der Wert einer Objektvariablen NICHT das Objekt selbst ist, sondern der Zeiger auf das Objekt. Wenn Sie also eine Objektvariable als Wert übergeben, wird der Zeiger auf die Funktionsparametervariable kopiert. Der Wert des Funktionsparameters zeigt auf genau dasselbe Objekt im Speicher. Die Objektdaten selbst können direkt über den Funktionsparameter geändert werden, ABER der Wert des Funktionsarguments wird NIE AKTUALISIERT, sodass er weiterhin auf denselben zeigtObjekt während und sogar nach dem Funktionsaufruf (auch wenn die Daten seines Objekts geändert wurden oder wenn dem Funktionsparameter ein anderes Objekt zugewiesen wurde). Es ist falsch zu schließen, dass das Funktionsargument als Referenz übergeben wurde, nur weil das referenzierte Objekt über die Funktionsparametervariable aktualisiert werden kann.

Aufruf / Referenzübergabe : Der Wert des Funktionsarguments kann / wird direkt durch den entsprechenden Funktionsparameter aktualisiert. Wenn dies hilft, wird der Funktionsparameter zu einem effektiven "Alias" für das Argument - er verweist effektiv auf denselben Wert am selben Speicherort. Wenn ein Funktionsargument eine Objektvariable ist, unterscheidet sich die Möglichkeit, die Daten des Objekts zu ändern, nicht vom Fall der Wertübergabe, da der Funktionsparameter weiterhin auf dasselbe Objekt wie das Argument verweist. Wenn im Fall der Objektvariablen der Funktionsparameter auf ein völlig anderes Objekt gesetzt ist, verweist das Argument ebenfalls auf das andere Objekt - dies geschieht im Fall der Wertübergabe nicht.

JavaScript wird nicht als Referenz übergeben. Wenn Sie genau lesen, werden Sie feststellen, dass alle gegensätzlichen Meinungen falsch verstehen, was unter Übergabe von Werten zu verstehen ist, und fälschlicherweise zu dem Schluss kommen, dass die Möglichkeit, die Daten eines Objekts über den Funktionsparameter zu aktualisieren, gleichbedeutend mit "Übergabe nach Wert" ist.

Objekt klonen / kopieren : Ein neues Objekt wird erstellt und die Daten des ursprünglichen Objekts werden kopiert. Dies kann eine tiefe oder flache Kopie sein, aber der Punkt ist, dass ein neues Objekt erstellt wird. Das Erstellen einer Kopie eines Objekts ist ein vom Wertübergabe getrenntes Konzept. Einige Sprachen unterscheiden zwischen Klassenobjekten und Strukturen (oder dergleichen) und haben möglicherweise ein unterschiedliches Verhalten beim Übergeben von Variablen der verschiedenen Typen. JavaScript führt so etwas jedoch nicht automatisch aus, wenn Objektvariablen übergeben werden. Das Fehlen des automatischen Klonens von Objekten führt jedoch nicht zu einer Referenzübergabe.

C Perkins
quelle
4

JavaScript übergibt primitive Typen als Wert und Objekttypen als Referenz

Nun streiten sich die Leute gerne endlos darüber, ob "Pass by Reference" der richtige Weg ist, um zu beschreiben, was Java et al. tatsächlich tun. Der Punkt ist folgender:

  1. Durch das Übergeben eines Objekts wird das Objekt nicht kopiert.
  2. Ein Objekt, das an eine Funktion übergeben wird, kann seine Mitglieder durch die Funktion ändern lassen.
  3. Ein an eine Funktion übergebener Grundwert kann von der Funktion nicht geändert werden. Eine Kopie wird erstellt.

In meinem Buch heißt das Passieren als Referenz.

- Brian Bi - Welche Programmiersprachen werden als Referenz übergeben?


Aktualisieren

Hier ist eine Gegenargumentation dazu:

In JavaScript ist keine "Referenzübergabe" verfügbar.

georgeawg
quelle
@Amy Da dies die Übergabe nach Wert beschreibt, nicht die Übergabe als Referenz. Diese Antwort ist gut und zeigt den Unterschied: stackoverflow.com/a/3638034/3307720
nasch
@nasch Ich verstehe den Unterschied. Nr. 1 und Nr. 2 beschreiben die Pass-by-Ref-Semantik. # 3 beschreibt die Pass-by-Value-Semantik.
Amy
@Amy 1, 2 und 3 stimmen alle mit dem Wert für die Übergabe überein. Um eine Referenz zu übergeben, benötigen Sie außerdem 4: Wenn Sie die Referenz einem neuen Wert innerhalb der Funktion zuweisen (mit dem Operator =), wird die Referenz auch außerhalb der Funktion neu zugewiesen. Dies ist bei Javascript nicht der Fall, sodass es ausschließlich als Wert übergeben wird. Wenn Sie ein Objekt übergeben, übergeben Sie einen Zeiger auf das Objekt und übergeben diesen Zeiger als Wert.
Nasch
Das ist im Allgemeinen nicht das, was unter "Pass-by-Reference" zu verstehen ist. Sie haben meine Anfrage erfüllt und ich stimme Ihnen nicht zu. Vielen Dank.
Amy
"In meinem Buch heißt das Passieren als Referenz." - In jedem einzelnen Compiler-Buch, Interpreter-Buch, Programmiersprachentheorie-Buch und Informatik-Buch, das jemals geschrieben wurde, ist dies nicht der Fall.
Jörg W Mittag
3

Mein einfacher Weg, das zu verstehen ...

  • Wenn Sie eine Funktion aufrufen, übergeben Sie den Inhalt (Referenz oder Wert) der Argumentvariablen, nicht die Variablen selbst.

    var var1 = 13;
    var var2 = { prop: 2 };
    
    //13 and var2's content (reference) are being passed here
    foo(var1, var2); 
  • Innerhalb der Funktion erhalten Parametervariablen inVar1und inVar2den Inhalt, der übergeben wird.

    function foo(inVar1, inVar2){
        //changing contents of inVar1 and inVar2 won't affect variables outside
        inVar1 = 20;
        inVar2 = { prop: 7 };
    }
  • Seit Sie inVar2die Referenz von erhalten haben { prop: 2 }, können Sie den Wert der Objekteigenschaft ändern.

    function foo(inVar1, inVar2){
        inVar2.prop = 7; 
    }
dpp
quelle
3

Das Übergeben von Argumenten an eine Funktion in JavaScript entspricht dem Übergeben von Parametern als Zeigerwert in C:

/*
The following C program demonstrates how arguments
to JavaScript functions are passed in a way analogous
to pass-by-pointer-value in C. The original JavaScript
test case by @Shog9 follows with the translation of
the code into C. This should make things clear to
those transitioning from C to JavaScript.

function changeStuff(num, obj1, obj2)
{
    num = num * 10;
    obj1.item = "changed";
    obj2 = {item: "changed"};
}

var num = 10;
var obj1 = {item: "unchanged"};
var obj2 = {item: "unchanged"};
changeStuff(num, obj1, obj2);
console.log(num);
console.log(obj1.item);    
console.log(obj2.item);

This produces the output:

10
changed
unchanged
*/

#include <stdio.h>
#include <stdlib.h>

struct obj {
    char *item;
};

void changeStuff(int *num, struct obj *obj1, struct obj *obj2)
{
    // make pointer point to a new memory location
    // holding the new integer value
    int *old_num = num;
    num = malloc(sizeof(int));
    *num = *old_num * 10;
    // make property of structure pointed to by pointer
    // point to the new value
    obj1->item = "changed";
    // make pointer point to a new memory location
    // holding the new structure value
    obj2 = malloc(sizeof(struct obj));
    obj2->item = "changed";
    free(num); // end of scope
    free(obj2); // end of scope
}

int num = 10;
struct obj obj1 = { "unchanged" };
struct obj obj2 = { "unchanged" };

int main()
{
    // pass pointers by value: the pointers
    // will be copied into the argument list
    // of the called function and the copied
    // pointers will point to the same values
    // as the original pointers
    changeStuff(&num, &obj1, &obj2);
    printf("%d\n", num);
    puts(obj1.item);
    puts(obj2.item);
    return 0;
}
John Sonderson
quelle
1
Ich glaube nicht, dass dies in JavaScript der Fall ist: `` `Javascript var num = 5;
Danail Nachev
@DanailNachev: Dies mag zwar technisch zutreffen, der Unterschied ist jedoch nur bei veränderlichen Objekten zu beobachten, bei denen es sich nicht um ECMAScript-Grundelemente handelt.
Jörg W Mittag
3

Für Anwälte in Programmiersprachen habe ich die folgenden Abschnitte von ECMAScript 5.1 (das einfacher zu lesen ist als die neueste Ausgabe) durchgesehen und es sogar auf der ECMAScript-Mailingliste angefragt.

TL; DR : Alles wird als Wert übergeben, aber die Eigenschaften von Objekten sind Referenzen, und die Definition von Objekt fehlt im Standard gruselig.

Erstellung von Argumentlisten

In Abschnitt 11.2.4 "Argumentlisten" wird Folgendes zum Erstellen einer Argumentliste beschrieben, die nur aus einem Argument besteht:

Die Produktionsargumentliste: AssignmentExpression wird wie folgt ausgewertet:

  1. Ref sei das Ergebnis der Auswertung von AssignmentExpression.
  2. Sei arg GetValue (ref).
  3. Geben Sie eine Liste zurück, deren einziges Element arg ist.

In diesem Abschnitt werden auch Fälle aufgeführt, in denen die Argumentliste 0 oder> 1 Argumente enthält.

Somit wird alles als Referenz übergeben.

Zugriff auf Objekteigenschaften

Abschnitt 11.2.1 "Property Accessors"

Die Produktion MemberExpression: MemberExpression [Ausdruck] wird wie folgt ausgewertet:

  1. BaseReference sei das Ergebnis der Auswertung von MemberExpression.
  2. Sei baseValue GetValue (baseReference).
  3. Es sei propertyNameReference das Ergebnis der Auswertung von Expression.
  4. Es sei propertyNameValue GetValue (propertyNameReference).
  5. Rufen Sie CheckObjectCoercible (baseValue) auf.
  6. Es sei propertyNameString ToString (propertyNameValue).
  7. Wenn die syntaktische Produktion, die ausgewertet wird, im Code für den strengen Modus enthalten ist, sei strict wahr, andernfalls sei strict falsch.
  8. Gibt einen Wert vom Typ Referenz zurück, dessen Basiswert baseValue ist und dessen referenzierter Name propertyNameString ist und dessen strikter Modus-Flag streng ist.

Somit stehen Eigenschaften von Objekten immer als Referenz zur Verfügung.

Auf Referenz

In Abschnitt 8.7 "Der Referenzspezifikationstyp" wird beschrieben, dass Referenzen keine echten Typen in der Sprache sind. Sie werden nur verwendet, um das Verhalten der Lösch-, Typ- und Zuweisungsoperatoren zu beschreiben.

Definition von "Objekt"

In der Ausgabe 5.1 ist definiert, dass "Ein Objekt eine Sammlung von Eigenschaften ist". Daher können wir schließen, dass der Wert des Objekts die Sammlung ist, aber was der Wert der Sammlung ist, ist in der Spezifikation schlecht definiert und erfordert ein wenig Aufwand, um zu verstehen.

DannyNiu
quelle
Es überrascht mich immer wieder, wie viele Menschen durch die Unterscheidung zwischen Argumenten, die als Wert übergeben werden, Argumenten, die als Referenz übergeben werden, Operationen an ganzen Objekten und Operationen an ihren Eigenschaften verwirrt werden. 1979 machte ich keinen Abschluss in Informatik und entschied mich stattdessen dafür, meinem MBA-Programm etwa 15 Stunden CS-Wahlfächer hinzuzufügen. Trotzdem wurde mir schnell klar, dass ich diese Konzepte mindestens so gut verstand wie meine Kollegen mit einem Abschluss in Informatik oder Mathematik. Studieren Sie Assembler, und es wird ganz klar.
David A. Gray
3

Die MDN-Dokumente erklären es klar, ohne zu ausführlich zu sein:

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 jedoch auch Werte und etwas Besonderes: Wenn die Funktion die Eigenschaften des referenzierten Objekts ändert, ist diese Änderung außerhalb der Funktion sichtbar. (...)

Quelle: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions#Description

miguelr
quelle
1

Wenn Sie in einer einfachen Sprache eine Variable als Referenz übergeben möchten, müssen Sie bei der Erstellung der Funktion eine bestimmte Syntax verwenden:

int myAge = 14;
increaseAgeByRef(myAge);
function increaseAgeByRef(int &age) {
  *age = *age + 1;
}

Das &ageist eine Referenz auf myAge, aber wenn Sie den Wert wollen, müssen Sie die Referenz mit konvertieren *age.

Javascript ist eine Hochsprache, die diese Konvertierung für Sie durchführt. Obwohl Objekte als Referenz übergeben werden, konvertiert die Sprache den Referenzparameter in den Wert. Sie müssen weder &die Funktionsdefinition verwenden, um sie als Referenz zu übergeben, noch *den Funktionskörper, um die Referenz in den Wert zu konvertieren. JS erledigt dies für Sie.

Wenn Sie versuchen, ein Objekt innerhalb einer Funktion zu ändern, indem Sie dessen Wert (dh age = {value:5}) ersetzen , bleibt die Änderung daher nicht bestehen. Wenn Sie jedoch die Eigenschaften (dh age.value = 5) ändern , ist dies der Fall.

Erfahren Sie mehr

Narayon
quelle
1

Ich habe diese Antworten mehrmals durchgelesen, aber nicht WIRKLICH verstanden, bis ich von der technischen Definition von "Call by Sharing", wie sie von Barbara Liskov genannt wird, erfahren habe

Die Semantik von Aufruf durch Freigabe unterscheidet sich von Aufruf durch Referenz darin, dass Zuweisungen zu Funktionsargumenten innerhalb der Funktion für den Aufrufer nicht sichtbar sind (im Gegensatz zur Referenzsemantik) [Zitieren erforderlich]. Wenn also beispielsweise eine Variable übergeben wurde, ist dies nicht möglich um eine Zuordnung zu dieser Variablen im Bereich des Aufrufers zu simulieren. Da die Funktion jedoch Zugriff auf dasselbe Objekt wie der Aufrufer hat (es wird keine Kopie erstellt), sind Mutationen zu diesen Objekten, sofern die Objekte veränderbar sind, innerhalb der Funktion für den Aufrufer sichtbar, die sich möglicherweise vom Aufruf nach Wert unterscheiden Semantik. Mutationen eines veränderlichen Objekts innerhalb der Funktion sind für den Aufrufer sichtbar, da das Objekt nicht kopiert oder geklont wird - es wird gemeinsam genutzt.

Das heißt, Parameterreferenzen können geändert werden, wenn Sie auf den Parameterwert selbst zugreifen. Andererseits verschwindet die Zuordnung zu einem Parameter nach der Auswertung und ist für den Funktionsaufrufer nicht zugänglich.

steviejay
quelle
Nein, ob ein Objekt veränderlich ist oder nicht, ist nicht wirklich das Problem. Alles wird immer als Wert übergeben. Es hängt nur davon ab, was Sie übergeben (ein Wert oder eine Referenz). Sehen Sie das .
Scott Marcus
Was sie beschreibt, ist die Übergabe eines Referenz-BY-VALUE. Es gibt keinen Grund, eine neue Terminologie einzuführen.
Sanjeev
1

Meist einfachster Weg

// Copy JS object without reference
var first = {"a":"value1","b":"value2"};
var clone = JSON.parse( JSON.stringify( first ) ); 

var second = ["a","b","c"];
var clone = JSON.parse( JSON.stringify( second ) ); 
Asad Raza
quelle
JSON.parse( JSON.stringify( obj ) )ist ein schrecklicher Weg, um Objekte tief zu klonen. Es ist nicht nur langsam, sondern kann auch Datenverlust verursachen.
D. Pardal
0

Ich habe die Extend-Methode der Underscore.js-Bibliothek als sehr nützlich empfunden , wenn ich ein Objekt als Parameter übergeben möchte, der entweder geändert oder vollständig ersetzt werden kann.

function replaceOrModify(aObj) {
  if (modify) {

    aObj.setNewValue('foo');

  } else {

   var newObj = new MyObject();
   // _.extend(destination, *sources) 
   _.extend(newObj, aObj);
  }
}
Jack
quelle
0

Die prägnanteste Erklärung, die ich gefunden habe, war im AirBNB-Styleguide :

  • Grundelemente : Wenn Sie auf einen Grundelementtyp zugreifen, arbeiten Sie direkt an dessen Wert

    • Zeichenfolge
    • Nummer
    • Boolescher Wert
    • Null
    • nicht definiert

Z.B:

var foo = 1,
    bar = foo;

bar = 9;

console.log(foo, bar); // => 1, 9
  • Komplex : Wenn Sie auf einen komplexen Typ zugreifen, arbeiten Sie an einem Verweis auf dessen Wert

    • Objekt
    • Array
    • Funktion

Z.B:

var foo = [1, 2],
    bar = foo;

bar[0] = 9;

console.log(foo[0], bar[0]); // => 9, 9

Das heißt, primitive Typen werden effektiv als Wert übergeben, und komplexe Typen werden als Referenz übergeben.

Deckel
quelle
Nein, alles wird immer als Wert übergeben. Es hängt nur davon ab, was Sie übergeben (ein Wert oder eine Referenz). Sehen Sie das .
Scott Marcus
-1

Ich würde sagen, es ist Pass-by-Copy -

Betrachten Sie Argumente und variable Objekte als Objekte, die während des Ausführungskontexts erstellt wurden, der zu Beginn des Funktionsaufrufs erstellt wurde - und Ihr tatsächlicher Wert / Ihre Referenz, die an die Funktion übergeben werden, werden nur in diesen Argumenten + variablen Objekten gespeichert.

Einfach gesagt, für primitive Typen werden die Werte zu Beginn des Funktionsaufrufs kopiert, für den Objekttyp wird die Referenz kopiert.

lyslim
quelle
1
"Pass-by-Copy" === Pass-by-Wert
Scott Marcus
-1

Es gibt einige Diskussionen über die Verwendung des Begriffs „als Verweis übergeben“ in JavaScript hier , aber Ihre Frage zu beantworten:

Ein Objekt wird automatisch als Referenz übergeben, ohne dass es speziell angegeben werden muss

(Aus dem oben genannten Artikel.)

Jack Sleight
quelle
7
Der verlinkte Artikel enthält diese Aussagen nicht mehr und vermeidet die Verwendung von "Referenzübergabe" insgesamt.
C Perkins
Der Wert ist eine Referenz
-2

Eine einfache Möglichkeit, um festzustellen, ob etwas "Referenzübergabe" ist, besteht darin, ob Sie eine "Swap" -Funktion schreiben können. In C können Sie beispielsweise Folgendes tun:

void swap(int *i, int *j)
{
    int t;
    t = *i;
    *i = *j;
    *j = t;
}

Wenn Sie dies in JavaScript nicht tun können, handelt es sich nicht um "Referenzübergabe".

Ross
quelle
21
Dies ist nicht wirklich als Referenz. Sie übergeben Zeiger an die Funktion, und diese Zeiger werden nach Wert übergeben. Ein besseres Beispiel wäre C ++ 's & Operator oder C #' s "ref" Schlüsselwort, beide werden wirklich als Referenz übergeben.
Matt Greer
Noch einfacher ist, dass in JavaScript alles als Wert übergeben wird.
Scott Marcus
-3
  1. primitive Typvariable wie Zeichenfolge, Nummer werden immer als Wertübergabe übergeben.
  2. Array und Objekt werden basierend auf diesen beiden Bedingungen als Referenzübergabe oder als Übergabewert übergeben.

    • Wenn Sie den Wert dieses Objekts oder Arrays durch ein neues Objekt oder Array ändern, wird es als Wert übergeben.

      object1 = {item: "car"}; array1=[1,2,3];

    Hier weisen Sie dem alten Objekt ein neues Objekt oder Array zu. Sie ändern den Wert der Eigenschaft des alten Objekts nicht. Es wird also als Wert übergeben.

    • Wenn Sie einen Eigenschaftswert eines Objekts oder Arrays ändern, wird dieser als Referenz übergeben.

      object1.key1= "car"; array1[0]=9;

    Hier ändern Sie einen Eigenschaftswert des alten Objekts. Sie weisen dem alten Objekt kein neues Objekt oder Array zu. Es wird also als Referenz übergeben.

Code

    function passVar(object1, object2, number1) {

        object1.key1= "laptop";
        object2 = {
            key2: "computer"
        };
        number1 = number1 + 1;
    }

    var object1 = {
        key1: "car"
    };
    var object2 = {
        key2: "bike"
    };
    var number1 = 10;

    passVar(object1, object2, number1);
    console.log(object1.key1);
    console.log(object2.key2);
    console.log(number1);

Output: -
    laptop
    bike
    10
Mukund Kumar
quelle
1
Der Zuweisungsoperator ist nicht mit einem Funktionsaufruf zu verwechseln. Wenn Sie einer vorhandenen Variablen neue Daten zuweisen, verringert sich die Referenzanzahl der alten Daten, und der alten Variablen werden neue Daten zugeordnet. Grundsätzlich zeigt die Variable auf die neuen Daten. Gleiches gilt für Eigenschaftsvariablen. Da diese Zuweisungen keine Funktionsaufrufe sind, haben sie nichts mit Wertübergabe oder Referenzübergabe zu tun.
John Sonderson
1
Nein, alles wird immer als Wert übergeben. Es hängt nur davon ab, was Sie übergeben (ein Wert oder eine Referenz). Sehen Sie das .
Scott Marcus
-3
  1. Grundelemente (Zahl, Boolescher Wert usw.) werden als Wert übergeben.
    • Saiten sind unveränderlich, daher spielt es für sie keine Rolle.
  2. Objekte werden als Referenz übergeben (die Referenz wird als Wert übergeben).
uriz
quelle
Nein, alles wird immer als Wert übergeben. Es hängt nur davon ab, was Sie übergeben (ein Wert oder eine Referenz). Sehen Sie das .
Scott Marcus
Ihre zweite Aussage widerspricht sich.
Jörg W Mittag
-5

Einfache Werte innerhalb von Funktionen ändern diese Werte außerhalb der Funktion nicht (sie werden als Wert übergeben), während komplexe Werte (sie werden als Referenz übergeben).

function willNotChange(x) {

    x = 1;
}

var x = 1000;

willNotChange(x);

document.write('After function call, x = ' + x + '<br>'); // Still 1000

function willChange(y) {

    y.num = 2;
}

var y = {num: 2000};

willChange(y);
document.write('After function call y.num = ' + y.num + '<br>'); // Now 2, not 2000
Olivera Kovacevic
quelle
das ist lächerlich, y wird sich aufgrund des Funktionsumfangs ändern, es wird gehisst, nicht weil es als Referenz übergeben wird.
Parijat Kalia
Nein, alles wird immer als Wert übergeben. Es hängt nur davon ab, was Sie übergeben (ein Wert oder eine Referenz). Sehen Sie das .
Scott Marcus