A shallow copy duplicates only the top-level properties of an object, sharing references to nested objects. A deep copy recursively duplicates every level, producing a fully independent clone.
Assignment (=) copies a reference, not the object, so both variables point to the same data. Spread ({...obj}), Object.assign({}, obj), and array slice/spread create SHALLOW copies: top-level primitives are copied, but nested objects/arrays are still shared — mutating a nested property affects both copies. For a DEEP copy, modern environments provide structuredClone(obj); older code used JSON.parse(JSON.stringify(obj)) (which drops functions, undefined, Dates become strings, and fails on circular references) or a library like lodash cloneDeep. Choosing correctly prevents subtle bugs where changing one object unexpectedly changes another.
A test reuses a base fixture with const testUser = { ...baseUser }; then does testUser.address.city = 'NYC'. Because address is nested and shared, baseUser.address.city also changes — polluting every other test that uses baseUser. A deep copy (structuredClone(baseUser)) isolates it.
const a = { nested: { x: 1 } }; const b = { ...a }; b.nested.x = 99; What is a.nested.x now, and how do you prevent this?