-4

In this code, I copied obj using spread operator into copiedObj. Then modified the checked property value to false. However when I console.log(obj.checked[0]), it returns false not true. And it seems like the checked value in copiedObj also changed original property value. Why is that?

const obj = [{id: 1, checked: true}];
const copiedObj = [...obj];
copiedObj.checked[0] = false;
console.log(obj.checked[0]);
D Park
  • 504
  • 10
  • 19
  • 2
    `obj` is an array with a single object in it. `copiedObj` is another array with the same object in it. You're copying the array, not the object. If you want to deep copy the object inside the array do `copiedObj = [...{obj[0]}]`. But really, these variables should be `arr` and `copiedArr` to avoid confusion. – ggorlen Apr 25 '20 at 16:27
  • I suspect you meant `checkedObj[0].checked = true`, right? – T.J. Crowder Apr 25 '20 at 16:31
  • 1
    Does this answer your question? [Changing single object in JS array changes all elements](https://stackoverflow.com/questions/35737119/changing-single-object-in-js-array-changes-all-elements) – ggorlen Apr 25 '20 at 16:35
  • `copiedObj.checked[0] = false` => `copiedObj[0].checked` and `obj.checked[0]` => `obj[0].checked`, right? – T.J. Crowder Apr 25 '20 at 16:43

3 Answers3

4

obj and copiedObj are arrays, not plain objects. Changing the state of copiedObj (by adding a checked property to it) does not change the state of obj because they're separate arrays.

But, both of the arrays contain a reference to the same object (the one with cheecked on it). So if you did:

checkedObj[0].checked = true;

that would change the state of that one object, which you would see whether you looked up that object on obj[0] or checkedObj[0].

If you want to make a deep copy so that you have separate arrays and separate objects, see this question's answers.

Since I'm 99% sure you meant checkedObj[0].checked = true, I'll explain what's happening in this code:

// Creates an array containing an object
const obj = [{id: 1, checked: true}];
// Creates a new array that contains the *same* object (NOT a *copy* of it)
const copiedObj = [...obj];
// Sets `checked` on that one object that is in both arrays
copiedObj[0].checked = false;
// Looks at the `checked` property on that one object that is in both arrays
console.log(obj[0].checked);

Step by step:

After

// Creates an array containing an object
const obj = [{id: 1, checked: true}];

in memory you have something like

                  +−−−−−−−−−−−−−+
obj:Ref44329−−−−−>|   (array)   |
                  +−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−+
                  | 0: Ref82445 |−−−>|   (object)    |
                  +−−−−−−−−−−−−−+    +−−−−−−−−−−−−−−−+
                                     | id: 1         |
                                     | checked: true |
                                     +−−−−−−−−−−−−−−−+

Then when you do

// Creates a new array that contains the *same* object (NOT a *copy* of it)
const copiedObj = [...obj];

you have something like:

                       +−−−−−−−−−−−−−+
obj:Ref44329−−−−−−−−−−>|   (array)   |
                       +−−−−−−−−−−−−−+   
                       | 0: Ref82445 |−−+
                       +−−−−−−−−−−−−−+  |
                                        |
                                        |   +−−−−−−−−−−−−−−−−+
                                        +−−>|   (object)     |
                       +−−−−−−−−−−−−−+  |   +−−−−−−−−−−−−−−−−+
checkedObj:Ref12987−−−>|   (array)   |  |   | id: 1          |
                       +−−−−−−−−−−−−−+  |   | checked: true  |
                       | 0: Ref82445 |−−+   +−−−−−−−−−−−−−−−−+
                       +−−−−−−−−−−−−−+ 

Then when you do

// Sets `checked` on that one object that is in both arrays
copiedObj[0].checked = false;

it changes the object that both arrays point to

                       +−−−−−−−−−−−−−+
obj:Ref44329−−−−−−−−−−>|   (array)   |
                       +−−−−−−−−−−−−−+   
                       | 0: Ref82445 |−−+
                       +−−−−−−−−−−−−−+  |
                                        |
                                        |   +−−−−−−−−−−−−−−−−+
                                        +−−>|   (object)     |
                       +−−−−−−−−−−−−−+  |   +−−−−−−−−−−−−−−−−+
checkedObj:Ref12987−−−>|   (array)   |  |   | id: 1          |
                       +−−−−−−−−−−−−−+  |   | checked: false |
                       | 0: Ref82445 |−−+   +−−−−−−−−−−−−−−−−+
                       +−−−−−−−−−−−−−+

...so looking it up will give you false regardless of which array you look at it through.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thanks for such a detailed answer with diagrams! I also changed my question based on your comment. – D Park Apr 25 '20 at 16:46
1

obj is an array of objects, when you do:

const copiedObj = [...obj];

You make a new array, but inside that array you will still reference the object from obj

This happens because you have two levels: array -> object, but the spread operator only applies to the first level (the array)

HermitCrab
  • 3,194
  • 1
  • 11
  • 10
1

this is because when you created the copiedObj by the spread operator, you are still referencing the object which you initially created in memory.

In order to fix this so that you would get the expected behavior is to create another shallow copy of all the properties inside the object also.

p.s. I modified copiedObj[0].checked = false; console.log(obj[0].checked) because I think you were trying to select the first element in the array you created.

const obj = [{id: 1, checked: true}];
const copiedObj = [Object.assign({}, obj[0])];
copiedObj[0].checked = false;
console.log(obj[0].checked)
//returns true