0

I'm very surprised that this happens in javascript, but when I was developing an algorithm I've noticed this.

node.js program in REPEL mode

When I put the "mt" array inside the "wm" constant, and I change a value in the "mt" array, that value to change in the "wm" array. Why does that happen? And how can I make that this doesn't happen?

I'm asking this because "wm", in my opinion, should contain a copy of the "mt" array, and should not change unless I directly modify it.

Hamada
  • 1,836
  • 3
  • 13
  • 27
  • 1
    Please post code, error messages, markup, etc. **as text**, not as a *picture* of text. Why: http://meta.stackoverflow.com/q/285551/157247 – T.J. Crowder Jul 07 '20 at 09:12
  • Both the variables "wm" and "mt" are referencing to the same data stored ultimately. – Helper Jul 07 '20 at 09:14
  • documentation says that you cant re-declare or re-assign a const variable and did you doing it here? no, you modified a variable inside the array – bill.gates Jul 07 '20 at 09:14

2 Answers2

4

Variables don't directly contain objects (including arrays), they contain references to those objects elsewhere in memory. So you start with:

                       +−−−−−−−−−−−−+
mt:Ref55162−−−−−−−−−−−>|  (array)   |
                       +−−−−−−−−−−−−+
                       | 0: "hola"  |
                       | 1: "adios" |
                       +−−−−−−−−−−−−+

Then when you do const wm = mt;, you're just copying the reference; both variables (constants) point to the same array:

mt:Ref55162−−−−−+
                |
                |      +−−−−−−−−−−−−+
                +−−−−−>|  (array)   |
                |      +−−−−−−−−−−−−+
                |      | 0: "hola"  |
wm:Ref55162−−−−−+      | 1: "adios" |
                       +−−−−−−−−−−−−+

(That Ref55162 value is just for illustration; you never see the actual value of an object reference in JavaScript code.)

If you want to make a copy of an array, you have various options:

  1. slice: const wm = mt.slice();

  2. Array.from: const wm = Array.from(mt);

  3. Spread notation: const wm = [...mt];

  4. concat: [].concat(mt)

Note that #2 - #4 in theory involve temporary objects, but if this code is in a performance-critical area, those temporary objects may get optimized away. #1 doesn't involve any temporary objects. (Okay, even for the last one, in theory the spec describes the creation of a lexical environment object for the call to slice but in practice, implementations don't always have to literally create those objects and avoid it where possible.)

It may be worth pointing out that if you have an array that contains object references, then just like wd = mt, the above all give you a new array containing copies of the references. So if you have:

const a = [{value: 1}, {value: 2}];
const b = a.slice();

...now a and b are different arrays, but both arrays contain references to the same two objects:

                +−−−−−−−−−−−−−+  +−−−−−−−−−−−−−−−−−+
a:Ref14785−−−−−>|   (array)   |  |                 |
                +−−−−−−−−−−−−−+  \   +−−−−−−−−−−+  |
                | 0: Ref55463 |−−−+−>| (object) |  |
                | 1: Ref16839 |−−+   +−−−−−−−−−−+  |
                +−−−−−−−−−−−−−+  |   | value: 1 |  |
                                 |   +−−−−−−−−−−+  |
                                 |                 |
                                 \   +−−−−−−−−−−+  |
                                  +−>| (object) |  |
                                 /   +−−−−−−−−−−+  |
                                 |   | value: 2 |  |
                                 |   +−−−−−−−−−−+  |
                +−−−−−−−−−−−−−+  |                 |
b:Ref98464−−−−−>|   (array)   |  |                 |
                +−−−−−−−−−−−−−+  |                 |
                | 0: Ref55463 |−−|−−−−−−−−−−−−−−−−−+
                | 1: Ref16839 |−−+
                +−−−−−−−−−−−−−+

If you wanted to copy those objects, you'd have to do that on purpose, for instance:

const b = a.map(obj => ({...obj}));

map calls a callback for each entry in an array and builds a new array from the return values; {...obj} copies the own enumerable properties from an object into a new object.

Or if a were an array of arrays, you might do:

const b = a.map(array => array.slice());

(This continues. If the inner object or array contains an object, reference, then...)

This question's answers may be useful if you want to copy all the way down (a "deep" copy): What is the most efficient way to deep clone an object in JavaScript?


Alternatively, if you want the array to be unmodifiable, you can use freeze on it:

Object.freeze(mt);

Then you can't change it at all.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
2

When you asign wm to mt it doesn't copy the array, it creates a reference to that array, so when you modify the original array it also modifies wm.

You can get around this by using Array.prototype.slice.

So:

const wm = mt.slice();
lusc
  • 1,206
  • 1
  • 10
  • 19