Q24 of 40 · JavaScript
What is the difference between a shallow copy and a deep copy in JavaScript?
Short answer
Short answer: A shallow copy duplicates the top-level structure but nested objects are still shared references. A deep copy recursively clones every level so changes to nested properties do not affect the original. JSON round-trip, structuredClone(), and recursive functions are common deep-copy approaches.
Detail
Understanding copy semantics is essential for managing test data and avoiding flaky tests caused by shared state.
Shallow copy: The top-level properties are copied to a new object, but nested objects or arrays still point to the same memory. Methods that produce shallow copies: object spread ({ ...obj }), Object.assign({}, obj), Array.slice(), Array.from(), and array spread ([...arr]).
Deep copy: Every level is recursively cloned. Modifications to any nested property do not affect the original. Approaches:
structuredClone(value)(Node 17+, modern browsers) — the recommended native methodJSON.parse(JSON.stringify(value))— works for plain data but loses functions, Dates become strings,undefinedis dropped- Utility libraries like Lodash
_.cloneDeep()— handles edge cases reliably
When shallow copies cause test bugs: Shared test fixtures that mutate nested data across tests silently corrupt state. Each test should receive a freshly deep-cloned copy of the fixture.
Circular references: JSON.parse/stringify throws on circular structures; structuredClone handles them correctly.
// EXAMPLE
const original = { name: "Alice", address: { city: "Dublin" } };
// Shallow copy — nested object is shared
const shallow = { ...original };
shallow.address.city = "London";
console.log(original.address.city); // "London" — mutated!
// Deep copy with structuredClone (recommended)
const deep = structuredClone(original);
deep.address.city = "Paris";
console.log(original.address.city); // "Dublin" — unaffected
// JSON round-trip (limited — loses functions, Dates, undefined)
const jsonClone = JSON.parse(JSON.stringify(original));
// Test fixture helper
function freshFixture() {
return structuredClone(BASE_USER); // each test gets an isolated copy
}