Warum wirkt sich das Ändern eines Arrays in JavaScript auf Kopien des Arrays aus?

80

Ich habe folgendes JavaScript geschrieben:

var myArray = ['a', 'b', 'c'];
var copyOfMyArray = myArray;
copyOfMyArray.splice(0, 1);
alert(myArray); // alerts ['b','c']
alert(copyOfMyArray); // alerts ['b','c']

var myNumber = 5;
var copyOfMyNumber = myNumber;
copyOfMyNumber = copyOfMyNumber - 1;
alert(myNumber); // alerts 5
alert(copyOfMyNumber); // alerts 4        

Dieser Code deklariert eine Variable myArrayund setzt sie auf einen Array-Wert. Es deklariert dann eine zweite Variable copyOfMyArrayund setzt sie auf myArray. Es führt eine Operation für aus copyOfMyArrayund alarmiert dann beide myArrayund copyOfMyArray. Wenn ich eine Operation ausführe copyOfMyArray, scheint es irgendwie so, als würde dieselbe Operation ausgeführt myArray.

Der Code macht dann dasselbe mit einem Zahlenwert: Er deklariert eine Variable myNumberund setzt sie auf einen Zahlenwert. Es deklariert dann eine zweite Variable copyOfMyNumberund setzt sie auf myNumber. Es führt eine Operation für aus copyOfMyNumberund alarmiert dann beide myNumberund copyOfMyNumber. Hier bekomme ich das erwartete Verhalten: verschiedene Werte für myNumberund copyOfMyNumber.

Was ist der Unterschied zwischen einem Array und einer Zahl in JavaScript, dass das Ändern eines Arrays den Wert einer Kopie des Arrays zu ändern scheint, während das Ändern einer Zahl den Wert einer Kopie der Zahl nicht ändert?

Ich vermute, dass aus irgendeinem Grund auf das Array als Referenz und die Zahl nach Wert verwiesen wird, aber warum? Wie kann ich wissen, welches Verhalten bei anderen Objekten zu erwarten ist?

Vivian River
quelle

Antworten:

107

Ein Array in JavaScript ist auch ein Objekt, und Variablen enthalten nur einen Verweis auf ein Objekt, nicht auf das Objekt selbst. Somit haben beide Variablen einen Verweis auf dasselbe Objekt.

Ihr Vergleich mit dem Zahlenbeispiel ist übrigens nicht korrekt. Sie weisen einen neuen Wert zu copyOfMyNumber. Wenn Sie ihm einen neuen Wert zuweisen, copyOfMyArrayändert sich dies ebenfalls myArraynicht.

Sie können eine Kopie eines Arrays mit slice [docs] erstellen :

var copyOfMyArray = myArray.slice(0);

Beachten Sie jedoch, dass dies nur eine flache Kopie zurückgibt , dh Objekte innerhalb des Arrays werden nicht geklont.

Felix Kling
quelle
+1 - Gibt es aus Neugier einen Nachteil, wenn Sie myArray.slice(0);direkt in diesem Kontext zuweisen ?
jAndy
2
@Rice: Nein, ich bearbeite nur zur Klarstellung. Wenn Sie eine tiefe Kopie wünschen, müssen Sie etwas selbst schreiben. Aber ich bin sicher, Sie werden ein Skript finden, das dies tut.
Felix Kling
@ FelixKling: Ich habe kein Beispiel. Ich habe nur gefragt, weil Sie zuerst die Prototypmethode angewendet haben.
jAndy
@jAndy: Ah, also hast du darauf hingewiesen ... Ich war nur ein bisschen verwirrt und ich in letzter Zeit öfter umgekehrt;)
Felix Kling
23

Nun, die einzig mögliche Antwort - und die richtige - ist, dass Sie das Array nicht tatsächlich kopieren. Wenn du schreibst

var copyOfArray = array;

Sie weisen einer anderen Variablen einen Verweis auf dasselbe Array zu. Mit anderen Worten, sie zeigen beide auf dasselbe Objekt.

Spitze
quelle
Ich würde sagen, Sie weisen nicht genau den Referenzzeiger zu, sondern wie eine Referenzkopie. Wenn Sie obj an function übergeben und versuchen, es durch ein anderes neues Objekt innerhalb der Funktion zu ersetzen, werden Sie das ursprüngliche Objekt nicht ändern.
Kashsandr
1
@kashesandr ja, "Zuweisen einer Referenz" bedeutet "Zuweisen einer Kopie einer Referenz", das stimmt. Zwei gleiche Referenzen sind jedoch immer gleich, genauso wie zwei Instanzen der Zahl 5immer gleich sind.
Pointy
15

Alle hier haben großartige Arbeit geleistet, um zu erklären, warum dies geschieht. Ich wollte nur eine Zeile schreiben und Sie wissen lassen, wie ich das beheben konnte.

thingArray = ['first_thing', 'second_thing', 'third_thing']
function removeFirstThingAndPreserveArray(){
  var copyOfThingArray = [...thingArray]
  copyOfThingArray.shift();
  return copyOfThingArray;
}

Dies verwendet die ... Spread-Syntax.

Spread-Syntaxquelle

EDIT: Zum Warum und zur Beantwortung Ihrer Frage:

Was ist der Unterschied zwischen einem Array und einer Zahl in JavaScript, dass das Ändern eines Arrays den Wert einer Kopie des Arrays zu ändern scheint, während das Ändern einer Zahl den Wert einer Kopie der Zahl nicht ändert?

Die Antwort ist, dass in JavaScript Arrays und Objekte veränderbar sind , während Zeichenfolgen und Zahlen sowie andere Grundelemente unveränderlich sind . Wenn wir eine Aufgabe erledigen wie:

var myArray = ['a', 'b', 'c']; var copyOfMyArray = myArray;

copyOfMyArray ist eigentlich nur ein Verweis auf myArray, keine tatsächliche Kopie.

Ich würde diesen Artikel empfehlen: Was sind unveränderliche und veränderbare Datenstrukturen? , tiefer in das Thema zu graben.

MDN-Glossar: Veränderlich

Salvatore
quelle
2
Genau das, wonach ich gesucht habe.
Fabien TheSolution
6

Objekte klonen -

A loop / array.pusherzeugt ein ähnliches Ergebnis wie array.slice(0)oder array.clone(). Alle Werte werden als Referenz übergeben. Da jedoch die meisten primitiven Datentypen unveränderlich sind , führen nachfolgende Operationen zum gewünschten Ergebnis - einem "Klon". Dies gilt natürlich nicht für Objekte und Arrays, die eine Änderung der ursprünglichen Referenz ermöglichen (es handelt sich um veränderbare Typen).

Nehmen Sie das folgende Beispiel:

const originalArray = [1, 'a', false, {foor: 'bar'}]
const newArray = [];

originalArray.forEach((v, i) => {
    newArray.push(originalArray[i]);
});

newArray[0] = newArray[0] + 1;
newArray[1] = 'b';
newArray[2] = true;
newArray[3] = Object.assign(newArray[3], {bar: 'foo'});

Die Operationen, die auf den newArray-Indizes ausgeführt werden, führen alle zum gewünschten Ergebnis, mit Ausnahme des endgültigen (Objekts), das, da es als Referenz kopiert wird, auch das ursprünglicheArray [3] mutiert.

https://jsfiddle.net/7ajz2m6w/

Beachten Sie, dass array.slice(0) and array.clone()diese Einschränkung gilt.

Eine Möglichkeit, dies zu lösen, besteht darin, das Objekt während der Push-Sequenz effektiv zu klonen:

originalArray.forEach((v, i) => {
    const val = (typeof v === 'object') ? Object.assign({}, v) : v;
    newArray.push(val);
});

https://jsfiddle.net/e5hmnjp0/

Prost

Bosworth99
quelle
3

In JS kopiert der Operator "=" den Zeiger in den Speicherbereich des Arrays. Wenn Sie ein Array in ein anderes kopieren möchten, müssen Sie die Klonfunktion verwenden.

Denn ganze Zahlen sind anders, weil sie ein primitiver Typ sind.

S.

DonCallisto
quelle
1

Mit Ausnahme der primitiven Datentypen (Zeichenfolgen und Zahlen IIRC) wird alles als Referenz kopiert.

QUentin
quelle
Das stimmt nicht Alle Zuordnungen weisen Referenzen zu. Zeichenfolgen und Zahlen sind unveränderlich.
SLaks
1

Sie haben keine Kopien.
Sie haben mehrere Variablen, die dasselbe Array enthalten.

Ebenso haben Sie mehrere Variablen mit derselben Nummer.

Wenn Sie schreiben copyOfMyNumber = ..., geben Sie eine neue Zahl in die Variable ein.
Das ist wie schreiben copyOfMyArray = ....

Wenn Sie schreiben copyOfMyArray.splice, ändern Sie das ursprüngliche Array .
Dies ist mit Zahlen nicht möglich, da Zahlen unveränderlich sind und nicht geändert werden können.

SLaks
quelle
1

Erstellen Sie einen Filter des ursprünglichen Arrays in der arrayCopy. Änderungen am neuen Array wirken sich also nicht auf das ursprüngliche Array aus.

var myArray = ['a', 'b', 'c'];
var arrayCopy = myArray.filter(function(f){return f;})
arrayCopy.splice(0, 1);
alert(myArray); // alerts ['a','b','c']
alert(arrayCopy); // alerts ['b','c']

Ich hoffe es hilft.

Stapel
quelle
1

Das Problem bei der flachen Kopie ist, dass nicht alle Objekte geklont werden, sondern eine Referenz erhalten. So funktioniert array.slice (0) nur mit dem Literal-Array, jedoch nicht mit dem Objekt-Array. In diesem Fall ist eine Möglichkeit ..

var firstArray = [{name: 'foo', id: 121}, {name: 'zoo', id: 321}];
var clonedArray = firstArray.map((_arrayElement) => Object.assign({}, _arrayElement));  
console.log(clonedArray);
// [{name: 'foo', id: 121}, {name: 'zoo', id: 321}]  // shallow copy
Omprakash Sharma
quelle
0

Sie können abhängig von Ihren Fällen eine Fehlerbehandlung hinzufügen und eine ähnliche Funktion wie die folgende verwenden, um das Problem zu beheben. Bitte kommentieren Sie alle Fehler / Probleme / Effizienz-Ideen.

function CopyAnArray (ari1) {
   var mxx4 = [];
   for (var i=0;i<ari1.length;i++) {
      var nads2 = [];
      for (var j=0;j<ari1[0].length;j++) {
         nads2.push(ari1[i][j]);
      }
      mxx4.push(nads2);
   }
   return mxx4;
}
Erjim
quelle
-1

Ein Array oder ein Objekt in Javascript enthält immer dieselbe Referenz, es sei denn, Sie klonen oder kopieren. Hier ist ein Beispiel:

http://plnkr.co/edit/Bqvsiddke27w9nLwYhcl?p=preview

// for showing that objects in javascript shares the same reference

var obj = {
  "name": "a"
}

var arr = [];

//we push the same object
arr.push(obj);
arr.push(obj);

//if we change the value for one object
arr[0].name = "b";

//the other object also changes
alert(arr[1].name);

Für das Klonen von Objekten können wir .clone () in jquery und angle.copy () verwenden. Diese Funktionen erstellen ein neues Objekt mit einer anderen Referenz. Wenn Sie mehr Funktionen dafür kennen, sagen Sie mir bitte, danke!

Albert Xu
quelle