Why does changing an Array in JavaScript affect copies of the array?
Well, the only possible answer — and the correct one — is that you're not actually copying the array. When you write
var copyOfArray = array;
you're assigning a reference to the same array into another variable. They're both pointing at the same object, in other words.
So everyone here has done a great job of explaining why this is happening - I just wanted to drop a line and let you know how I was able to fix this - pretty easily:
thingArray = ['first_thing', 'second_thing', 'third_thing']
function removeFirstThingAndPreserveArray(){
var copyOfThingArray = [...thingArray]
copyOfThingArray.shift();
return copyOfThingArray;
}
This is using the ... spread syntax.
Spread Syntax Source
EDIT: As to the why of this, and to answer your question:
What is the difference between an array and a number in JavaScript that it seems changing an array changes the value of a copy of the array, where as changing a number does not change the value of a copy of the number?
The answer is that in JavaScript, arrays and objects are mutable, while strings and numbers and other primitives are immutable. When we do an assignment like:
var myArray = ['a', 'b', 'c']; var copyOfMyArray = myArray;
copyOfMyArray is really just a reference to myArray, not an actual copy.
I would recommend this article, What are immutable and mutable data structures?, to dig deeper into the subject.
MDN Glossary: Mutable
Cloning objects -
A loop / array.push
produces a similar result to array.slice(0)
or array.clone()
. Values are all passed by reference, but since most primitive data types are immutable, subsequent operations produce the desired result - a 'clone'. This is not true of objects and arrays, of course, which allow for modification of the original reference (they are mutable types).
Take the following example:
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'});
The operations run on the newArray indices all produce the desired result, except the final (object), which, because it is copied by reference, will mutate the originalArray[3] as well.
https://jsfiddle.net/7ajz2m6w/
Note that array.slice(0)
and array.clone()
suffers from this same limitation.
One way to solve this is by effectively cloning the object during the push sequence:
originalArray.forEach((v, i) => {
const val = (typeof v === 'object') ? Object.assign({}, v) : v;
newArray.push(val);
});
https://jsfiddle.net/e5hmnjp0/
cheers
An array in JavaScript is also an object and variables only hold a reference to an object, not the object itself. Thus both variables have a reference to the same object.
Your comparison with the number example is not correct btw. You assign a new value to copyOfMyNumber
. If you assign a new value to copyOfMyArray
it will not change myArray
either.
You can create a copy of an array using slice
[docs]:
var copyOfMyArray = myArray.slice(0);
But note that this only returns a shallow copy, i.e. objects inside the array will not be cloned.