Ch. 1 · JavaScript

JavaScript Rest and Spread Gotchas

Use spread and rest correctly, and avoid the shallow-copy trap where nested objects are still shared between the original and the copy.

~2 min readbeginnerupdated Oct 5, 2026

The spread operator copies own enumerable properties into a new array or object, and the rest syntax collects the remaining ones. Both are shallow: a copied object still shares every nested reference with the original, which is the source of many “I changed the copy and the original changed too” bugs.

Before you start

You should know arrays, objects, and that assignment copies references rather than values. This article focuses on shallow copy semantics and the syntax rules; deep cloning is covered separately.

Step-by-step walkthrough

Step 1: Spread is one level deep

{ ...base } and [...items] create a new top-level container, but nested objects, arrays and class instances are copied by reference. Editing copy.nested.x also changes base.nested.x because both point at the same object.

Step 2: Rest must come last

In array and parameter destructuring, rest must be the final element: const [first, ...others] = list is valid, const [...others, last] = list is a syntax error. In object rest, const { a, ...rest } = obj collects every remaining own enumerable property.

Step 3: Know what spread skips

Object spread copies own enumerable properties only. Non-enumerable fields such as a class method defined with a non-enumerable descriptor, getters that are not enumerable, and inherited prototype members are not copied. Spreading a class instance therefore yields a plain object without its prototype.

Worked scenario

Run this with Node.js. The top-level property is independent, but the nested object is shared.

const base = { a: 1, nested: { n: 1 } };
const copy = { ...base, a: 2 };
copy.nested.n = 9;
console.log(base.a); // 1 (independent)
console.log(base.nested.n); // 9 (shared reference)
JavaScript

Walk through the example

The spread creates a new object, so overriding a on copy does not touch base.a. But copy.nested and base.nested are the same object, so mutating the nested field through the copy is visible through the original. If the value must be isolated, clone the nested level too or use a deep clone.

Common mistake

Reaching for { ...state } to update nested state in a reducer, then mutating the nested object because it “felt copied”. React and similar libraries rely on reference changes, so a shared nested reference both mutates the old state and fails to trigger updates. Copy each changed level explicitly.

Verify the behavior

Assert that changing a top-level key on the copy leaves the original unchanged. Assert that changing a nested key is visible from the original, proving the share. Then replace the nested value with a fresh object and confirm the two are fully independent. Repeat for arrays to confirm [...items] is also shallow.

Interview exercise

Does { ...obj } create a deep copy? What about spreading a class instance?

Answer and reasoning

No — it is a shallow copy: only the top-level own enumerable properties are copied, and nested values remain shared. Spreading a class instance produces a plain object containing its own enumerable properties, without the prototype, so its methods are gone. For deep isolation use structuredClone, and for classes prefer an explicit copy method that restores the prototype.

Continue learning

See the deep-clone alternative in structuredClone and transferables and read the MDN spread reference. Continue with the JavaScript MCQs.

More in JavaScript

read ✓JavaScript · mid

JavaScript Date and Timezone Pitfalls

Parse and format dates without timezone surprises: zero-based months, date-only versus date-time parsing, and why UTC storage avoids drift.

~3 min readread →
read ✓JavaScript · mid

JavaScript Number Precision and BigInt

Why 0.1 + 0.2 is not 0.3, where safe integer range ends, and when BigInt is the right tool for large identifiers and money.

~2 min readread →
esc