-2

const girl = {
  name: 'Anna',
  info: { age: 20, number: 123 }
};

const newGirl = { ...girl };
newGirl.info.age = 30;
console.log(girl.info.age, newGirl.info.age);

The output is 30 30, we copy the properties of the girl object into the newGirl object using the spread operator. This operator creates a shallow copy of the object. Shallow copies are not safe from mutations.

Lets see another example

function test(obj) {
  const output = { ...obj };
  output.age = 30;
  return output;
}

let person = { age: 10 }
let newPerson = test(person);
console.log(newPerson.age, person.age); // output is 30 10

As you can see the second example also use a spread operator to create a copy of the object. Why does it do not behave the same as the first example? Why it does not affect original object field data?

adiga
  • 34,372
  • 9
  • 61
  • 83
Sean Liu
  • 831
  • 1
  • 11
  • 23

3 Answers3

3

The difference is that in the first snippet you're modifying the nested object in the info property, while in the second snippet you're modifying the containing object.

When you make a shallow copy, girl and newGirl are different objects, but they both contain references to the same info object. So girl.info and newGirl.info are the same objects, and modifying the properties of one of them will be reflected by accessing the other.

But if you assign to girl.name or newGirl.name you're modifying properties of different objects. You wouldn't see the change in the other object. This is what you were doing by assigning to output.age in the second snippet.

A deep copy would copy all the objects that are referenced recursively, not just the top-level object. If you made a deep copy, no modifications at any level would be seen when accessing the other object.

Barmar
  • 741,623
  • 53
  • 500
  • 612
2

The only values in person are immutable primitives (10). There are no objects that would be copied by reference (unlike girl where info is an object).

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
2

You've changed two things in your second example:

  • Passed the object into and out of a function
  • Changed the structure of the object

The difference you're seeing has nothing to do with the extra indirection through a function, only the structure of the object.

Let's change one thing at a time and see what happens:

const girl = { age: 20, number: 123 };

const newGirl = { ...girl };
newGirl.age = 30;
console.log(girl.age, newGirl.age);

Output: 20 30 - the original object has not been modified, because we directly copied the scalar value age, so the two properties are completely separate.

function test(obj) {
  const output = { ...obj };
  output.info.age = 30;
  return output;
}

let person = { info: { age: 10 } }
let newPerson = test(person);
console.log(newPerson.info.age, person.info.age);

Output is 30 30 - the original object has been modified, because we only copied the object reference for the info property, so both properties hold references pointing at the same object.

IMSoP
  • 89,526
  • 13
  • 117
  • 169