1

I'm having trouble in understanding why nested object inside the source object is not copied into target object while using assign() method,

const original = {
  name: 'Fiesta',
  car: {
    color: 'blue'
  }
}
const copied = Object.assign({}, original)

original.name = 'Lamborghini'
original.car.color = 'red'

console.log(original); // color: "red", name: "Lamborghini"
console.log(copied); //   color: "red", name: "Fiesta"

I was expecting copied.name to be 'Lamborghini' as it was changed to 'Lamborghini' later, can anyone explain why is this?

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
Nag
  • 806
  • 1
  • 13
  • 28
  • Is your question about why `.car.color` is not copied, or why `.name` is copied? – Bergi Jul 14 '19 at 13:53
  • 1
    Object.assign doesn't do a deep copy, you need another method to do your copy – Nick Parsons Jul 14 '19 at 13:53
  • @Bergi, .car.color was changed to red in both original and copied object, and name property was not changed in copied object even though it was overridden in original object – Nag Jul 14 '19 at 14:00
  • @Nag That's because there is only one car object, which both `original` and `copied` reference, and you did change its `.color` property. Remember that `Object.assign` does not create a deep copy. – Bergi Jul 14 '19 at 14:06
  • @Bergi, i'm sorry if i'm understanding this wrong, what i thought is car.color is deep copy but when changed it got changed in both original and copied object, where as name is shallow copy, it got changed, i'm really confused. – Nag Jul 14 '19 at 14:19
  • It doesn't get changed "in original and copied object". The car is a separate object, which exists only once and has one `.color` property that you change - whereas there are two `.name` properties, one on the `original` object and one on the `copied`. – Bergi Jul 14 '19 at 14:21
  • @Nag: What exactly is the desired behavior you want: say I do `original.name = "Honda"` what do you want to happen to `copied`? Or if say I do: `original.car.color = "blue"`: what should happen to `copied` in your ideal state? – Zargold Jul 14 '19 at 14:23
  • Assuming you want `copied` to have all the same values meaning you want it to start off the same as `original` then you have 3 options: 1. do a deep clone of original: (you can use my function to do that): if you do that what will happen is `copied` will start off looking the same as original. But any changes happening to `original` (or to `copied`) will NOT affect `copied`. 2. You can use `Object.assign` as you have: this means that copied starts off same as original but changes to non-nested properties (like name) will not be reflected but changes to nested objects in original change copy. – Zargold Jul 14 '19 at 14:30
  • 3) you can set `copied = original` then `copied` starts off as original and any and all changes to original "also happen" to `copied`. (Because nothing really happened to either original nor copied: the changes happened to the object that they both reference.) You can even tell this based on the fact that copied and original are assigned with `const` you CANNOT change a `const` but you CAN change the object `const` references – Zargold Jul 14 '19 at 14:32
  • @Zargold, i was expecting that both original and copied will have same values, but i'm understanding that object inside object will be of reference type, so change in value of reference type will change in both original and copied object – Nag Jul 14 '19 at 14:32
  • Not only nested objects are of reference types but any object when assigned to a variable is really only having its `reference` assigned to the variable. That is why you can still make changes to the reference of a `const`. (Same applies to Objects/Arrays/class instances anything stored on the heap rather than on the stack --which holds only pure/primitive values (numbers/immutable strings) – Zargold Jul 14 '19 at 14:35

2 Answers2

0

Since you updated your question then: now I guess you would prefer to not use Object.assign at all. What you seem to want because you want even the name of "copy" to be changed when "original" gets updated is to set copy to be a reference/pointer to the same object that original references

You can simply: set const copy = original to get that result.

const original = {
name: 'porsche',
car: { color: 'green' }
}
const copied = original
original.name = 'Lamborghini'
document.write(copied.name)
// despite copied being a "copy" of original... it gets all the updates that original gets.

// That's because copy is not a copy at all but rather a pointer just like original is a pointer and they both point to the same object in the heap of memory.

you can have multiple pointers to the same space in memory/object which will allow you to modify one of the pointer's key and then it will change it for all pointers. This is assignment to the reference.


This is regarding why Object.assign is not a deep clone: So that is because the object contains only a reference to the pointer to the nested object.

Specifically,

The object:

{
    color: 'blue'
}

Is an object that you did not apply Object.assign to... so by having: color: "0xC32332"

"0xC32332" is the pointer to the space in memory which contains the object with the key color and the current value blue.

When doing Object.assign you are not changing the value -from the key car (aka the object) you are just creating a new pointer to the same object/space in memory.

So if you want to do a "deep" clone using Object.assign() you have to call it recursively.

const recursiveDeep = (obj) => {
  return Object.keys(obj).reduce((newObj, currentKey) => {
    if (typeof obj[currentKey] === 'object'){
      newObj[currentKey] = recursiveDeep(obj[currentKey])
    }
    else {
      newObj[currentKey] = obj[currentKey]
    }
    return newObj
  }, {})
}

const original = {
  name: 'porsche',
  car: { color: 'green' }
}
const copied = recursiveDeep(original)

original.name = 'Lamborghini'
original.car.color = 'red'
document.write(copied.name)
document.write(copied.car.color)
Zargold
  • 1,892
  • 18
  • 24
0

copied is a shallow clone of original, so all top-level properties are cloned. Changing one of them (like name) on the original, won't change it on the clone (that's the point of a clone).

The car object nested within the original object isn't cloned whatsoever, so changing a property in it still results in a change to both original and copied.

Jb31
  • 1,381
  • 1
  • 10
  • 19