Today, while investigating a bug in our app, I witnessed a very surprising behavior in JavaScript's structuredClone.
This method promises to create a deep clone of a given value. This was previously achieved using the JSON.parse(JSON.stringify(value)) technique, and on paper structuredClone appears to be a superset per se of this technique, yielding the same result, while also supporting things like Dates and circular references.
However, today I learned that if you use structuredClone to clone an object containing reference type variables pointing to the same reference, these references will be kept, as opposed to creating new values with different references.
Here is a toy example to demonstrate this behavior:
const someSharedArray = ['foo', 'bar']
const myObj = {
field1: someSharedArray,
field2: someSharedArray,
field3: someSharedArray,
}
const myObjCloned = structuredClone(myObj)
console.log(myObjCloned)
/**
{
"field1": ["foo", "bar"],
"field2": ["foo", "bar"],
"field3": ["foo", "bar"],
}
**/
myObjCloned.field2[1] = 'baz'
// At this point:
// Expected: only `field2`'s value should change, because `myObjCloned` was deeply cloned.
// Actual: all fields' values change, because they all still point to `someSharedArray`
console.log(myObjCloned)
/**
{
"field1": ["foo", "baz"],
"field2": ["foo", "baz"],
"field3": ["foo", "baz"],
}
**/
This is a very surprising behavior of structuredClone, because:
- It claims to perform deep cloning
- This behavior doesn't happen when using
JSON.parse(JSON.stringify(value))
myObj.field2[1] = 'baz'instead, you would have the exact same behavior inmyObjalready. So why should a clone behave any different, than the original?myObj? If yes: that'd be surprising. If not: arguablymyObjClonedbehaves exactly asmyObjwould.structuredCloneeven bettermyObjClonedIS deep cloned. As is the array. The structure is preserved as-is and there are no links fromsomeSharedArrayto the clone. All the requirements for deep cloning are fulfilled. You seem to expect that the shared array would suddenly become multiple arrays but I don't think it's a reasonable expectation.JSON.parse(JSON.stringify)has always been an ugly hack IMO, which has always had several asterisks attached to it, like not being able to "clone" certain types or causing shared array references to arguably break.structuredCloneis the much more faithful cloning mechanism. You've just been relying on side effects of the JSON workaround so far.