JavaScriptObjectsDestructuringDeep Copy

JavaScript Object Copy: Destructuring, Shallow Copy, and Deep Copy

Understand JavaScript destructuring, spread syntax, shallow copy boundaries, structuredClone, JSON serialization, and why custom deep clone functions are easy to get wrong.

·Updated ·11 min read·Counting...
JavaScript Object Copy: Destructuring, Shallow Copy, and Deep Copy

Destructuring extracts values from objects or arrays. Spread syntax creates a new outer object or array. Neither of those automatically deep-copies nested data. If you need to copy nested structures, evaluate structuredClone first, and do not treat JSON.parse(JSON.stringify(value)) as a universal deep clone.

Object destructuring

const user = {
  id: 42,
  profile: { name: "Steven" },
  role: "editor",
}

const { id, role } = user

Rename properties and provide defaults:

const { role: userRole, locale = "en-US" } = user

Defaults apply only when the value is undefined. They do not replace null.

Nested destructuring works, but it can throw when the parent is missing:

const {
  profile: { name },
} = user

If profile may be absent, validate the data or provide a safe default:

const { profile: { name } = {} } = user

In TypeScript, be careful with empty-object defaults. Do not hide real type uncertainty with unsafe assertions.

Array destructuring

const [first, second] = ["Mac", "Windows", "Linux"]
const [head, ...rest] = [1, 2, 3, 4]

Swapping variables:

let left = "L"
let right = "R"
;[left, right] = [right, left]

If your codebase omits semicolons, watch for assignment expressions that start with [ or (. Automatic semicolon insertion can surprise you.

Spread syntax is shallow

const original = {
  name: "FreeMac",
  settings: { theme: "dark" },
}

const copy = { ...original }
copy.name = "Other"
copy.settings.theme = "light"

Changing copy.name does not affect original.name, but changing copy.settings.theme also changes original.settings.theme, because the nested object reference is shared.

Array spread, slice(), Array.from(), and Object.assign() are also shallow copy tools.

For immutable nested updates, copy each changed level:

const next = {
  ...original,
  settings: {
    ...original.settings,
    theme: "light",
  },
}

structuredClone

Modern browsers and Node.js support structuredClone:

const source = {
  createdAt: new Date(),
  tags: new Set(["Mac", "Free"]),
  map: new Map([["theme", "dark"]]),
  nested: { enabled: true },
}

const cloned = structuredClone(source)

It supports many built-in types and circular references. It can also work with transferable objects. But functions, DOM nodes, and class instances with custom behavior should not be treated as ordinary business data.

Before cloning, ask whether you really need a full copy of the entire tree. Large deep copies cost time and memory.

Limits of JSON copying

This pattern is common:

const copied = JSON.parse(JSON.stringify(value))

It is only safe for data that is intentionally JSON-compatible. It loses or changes many values:

  • Date becomes a string.
  • undefined, functions, and symbols may disappear.
  • Map and Set do not round-trip as their original types.
  • BigInt cannot be serialized directly.
  • Circular references throw.
  • NaN and Infinity do not preserve their original meaning.

If the data is already meant for a JSON API, this may be acceptable. Do not wrap it as a generic deepClone.

Why custom deep clone functions are risky

A simple recursive clone usually misses:

  • Circular references.
  • Property descriptors and non-enumerable properties.
  • Symbol keys.
  • Date, RegExp, Map, Set, and ArrayBuffer.
  • Sparse arrays and prototypes.
  • Getter side effects.

Write a custom clone only for a well-defined business model and test that exact model. Do not promise to clone arbitrary JavaScript values.

Destructuring is not copying everything

const { profile } = user

This assigns the profile reference to a new variable. It does not create a new profile object.

Object rest creates a new outer object, but nested values are still shared:

const { id, ...withoutId } = user

Subscribe to FreeMac

Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.