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)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.