|

JavaScript 41 🧬 Spread operator

const arr = [1, 2, 3];
const copy = [...arr];
console.log(copy);

const more = [0, ...arr, 4];
console.log(more);

const obj = { a: 1, b: 2 };
const objCopy = { ...obj };
console.log(objCopy);

const merged = { ...obj, c: 3 };
console.log(merged);

const nums = [5, 3, 9, 1];
console.log(Math.max(...nums));

function sum(...args) {
  return args.reduce((a, b) => a + b, 0);
}
console.log(sum(...nums));

const [first, ...rest] = arr;
console.log(first, rest);

const { a, ...others } = obj;
console.log(a, others);

The spread operator (...) takes an iterable — an array, a string, a Set, a Map — or an object, and expands its contents into individual elements. It’s the opposite of the rest parameter, which collects values into an array. Same syntax, different purpose.

Key point: Spread expands; rest collects. Both use ..., and the context tells you which one you’re using — spread in a call or literal, rest in a parameter list or destructuring pattern.


a – What is the spread operator

The spread operator ... unpacks values from an iterable or object into a place where multiple values are expected.

The two uses of ...:

ContextNameBehavior
Function call — fn(...arr)SpreadExpand into arguments
Array literal — [...arr]SpreadExpand into elements
Object literal — {...obj}SpreadExpand into properties
Function parameter — fn(...args)RestCollect into array
Destructuring — [a, ...rest]RestCollect remaining

Spread in function calls:

const nums = [5, 3, 9, 1];

console.log(Math.max(...nums));
// [ 9 ]

console.log(Math.min(...nums));
// [ 1 ]

Without spread, you’d need Math.max.apply(null, nums). Spread is cleaner.

Spread in array literals:

const arr = [1, 2, 3];

const copy = [...arr];
console.log(copy);
// [ [ 1, 2, 3 ] ]

const more = [0, ...arr, 4];
console.log(more);
// [ [ 0, 1, 2, 3, 4 ] ]

Spread in object literals:

const obj = { a: 1, b: 2 };

const copy = { ...obj };
console.log(copy);
// [ { a: 1, b: 2 } ]

const merged = { ...obj, c: 3 };
console.log(merged);
// [ { a: 1, b: 2, c: 3 } ]

What spread works on:

ValueSpreadable?
Arrays
Strings✅ (into characters)
Sets
Maps✅ (into [k, v] pairs)
Arguments object
Typed arrays
Plain objects✅ (object spread only)
Numbers
Booleans
null / undefined❌ (throws)

Spread does a shallow copy:

const nested = { a: { b: 1 } };
const copy = { ...nested };

copy.a.b = 99;
console.log(nested.a.b);
// [ 99 ]  ← nested object is shared

Both objects point to the same inner object. Spread only copies the top level.

Spread is not the same as concat or Object.assign:

// These are equivalent, but spread is cleaner
const merged1 = [1, 2].concat([3, 4]);
const merged2 = [1, 2, ...[3, 4]];

const obj1 = Object.assign({}, { a: 1 }, { b: 2 });
const obj2 = { ...{ a: 1 }, ...{ b: 2 } };

Why spread matters:

  • Cleaner syntax than apply and concat
  • Non-mutating — creates new arrays and objects
  • Works with any iterable — arrays, strings, Sets, Maps
  • Composable — mix spread with regular elements
  • Short — one character longer than nothing

b – Spread with arrays

Spread with arrays is the most common use case. It replaces concat, slice, and apply.

Copying an array:

const original = [1, 2, 3];
const copy = [...original];

copy.push(4);
console.log(original);
// [ [ 1, 2, 3 ] ]

console.log(copy);
// [ [ 1, 2, 3, 4 ] ]

[...arr] creates a shallow copy. The original is untouched.

Concatenating arrays:

const a = [1, 2];
const b = [3, 4];
const c = [...a, ...b];

console.log(c);
// [ [ 1, 2, 3, 4 ] ]

You can mix multiple spreads and literal values:

const result = [0, ...a, 'middle', ...b, 99];
console.log(result);
// [ [ 0, 1, 2, 'middle', 3, 4, 99 ] ]

Inserting arrays in the middle:

const base = ['start', 'end'];
const expanded = [base[0], 'middle', base[1]];

// vs
const spread = ['start', 'middle', 'end'];
console.log(spread);
// [ [ 'start', 'middle', 'end' ] ]

Cloning — the right way:

// ❌ Shallow reference, not a copy
const wrong = original;

// ✅ Shallow copy via spread
const right = [...original];

Converting iterables to arrays:

const str = 'hello';
const chars = [...str];
console.log(chars);
// [ [ 'h', 'e', 'l', 'l', 'o' ] ]

const set = new Set([1, 2, 2, 3]);
const arrFromSet = [...set];
console.log(arrFromSet);
// [ [ 1, 2, 3 ] ]

const map = new Map([['a', 1], ['b', 2]]);
const arrFromMap = [...map];
console.log(arrFromMap);
// [ [ [ 'a', 1 ], [ 'b', 2 ] ] ]

Passing arrays as arguments:

function add(a, b, c) {
  return a + b + c;
}

const nums = [1, 2, 3];

console.log(add(...nums));
// [ 6 ]

// Mixed with regular arguments
console.log(add(10, ...[20, 30]));
// [ 60 ]

Using spread with Math:

const values = [4, 8, 15, 16, 23, 42];

console.log(Math.max(...values));
// [ 42 ]

console.log(Math.min(...values));
// [ 4 ]

Using spread with push:

const arr = [1, 2, 3];
const more = [4, 5, 6];

arr.push(...more);
console.log(arr);
// [ [ 1, 2, 3, 4, 5, 6 ] ]

Without spread, you’d need arr.push(4, 5, 6) or arr.push.apply(arr, more).

Removing duplicates:

const dupes = [1, 2, 2, 3, 3, 3];
const unique = [...new Set(dupes)];
console.log(unique);
// [ [ 1, 2, 3 ] ]

Quick max of array of objects by field:

const users = [
  { name: 'Alice', age: 30 },
  { name: 'Bob', age: 25 },
  { name: 'Charlie', age: 35 }
];

const oldest = Math.max(...users.map(u => u.age));
console.log(oldest);
// [ 35 ]

Flattening one level:

const nested = [[1, 2], [3, 4], [5, 6]];
const flat = [].concat(...nested);
console.log(flat);
// [ [ 1, 2, 3, 4, 5, 6 ] ]

Or use flat():

console.log(nested.flat());
// [ [ 1, 2, 3, 4, 5, 6 ] ]

Comparison — spread vs alternatives:

TaskSpreadAlternative
Copy[...arr]arr.slice()
Concat[...a, ...b]a.concat(b)
MaxMath.max(...arr)Math.max.apply(null, arr)
Push manyarr.push(...x)arr.push.apply(arr, x)
Iterable to array[...iter]Array.from(iter)
Dedupe[...new Set(a)]Array.from(new Set(a))

Shallow copy caveat:

const nested = [[1, 2], [3, 4]];
const copy = [...nested];

copy[0].push(99);
console.log(nested);
// [ [ [ 1, 2, 99 ], [ 3, 4 ] ] ]

The inner arrays are shared. To deep-copy, use structuredClone(nested).


c – Spread with objects

Object spread was added in ES2018. It copies own enumerable properties from one object to another.

Copying an object:

const original = { a: 1, b: 2 };
const copy = { ...original };

copy.a = 99;
console.log(original.a);
// [ 1 ]

console.log(copy.a);
// [ 99 ]

Merging objects:

const defaults = { theme: 'light', lang: 'en' };
const userPrefs = { theme: 'dark' };

const config = { ...defaults, ...userPrefs };
console.log(config);
// [ { theme: 'dark', lang: 'en' } ]

Later properties override earlier ones:

const merged = { a: 1, ...{ a: 2, b: 3 } };
console.log(merged);
// [ { a: 2, b: 3 } ]

Adding properties while copying:

const user = { name: 'Alice' };
const enriched = { ...user, age: 30, city: 'Paris' };
console.log(enriched);
// [ { name: 'Alice', age: 30, city: 'Paris' } ]

Overriding a property:

const user = { name: 'Alice', age: 30 };
const updated = { ...user, age: 31 };
console.log(updated);
// [ { name: 'Alice', age: 31 } ]

A common pattern — React-style state updates:

const state = { count: 0, name: 'Alice' };
const newState = { ...state, count: state.count + 1 };
console.log(newState);
// [ { count: 1, name: 'Alice' } ]

Removing a property:

const user = { name: 'Alice', password: 'secret', age: 30 };
const { password, ...safeUser } = user;
console.log(safeUser);
// [ { name: 'Alice', age: 30 } ]

You combine object destructuring (to extract) with rest (to collect the rest). The ...safeUser here is the rest pattern, not spread — same syntax, different context.

Merging with defaults and overrides:

function createConfig(options = {}) {
  const defaults = {
    host: 'localhost',
    port: 8080,
    protocol: 'http'
  };
  return { ...defaults, ...options };
}

console.log(createConfig({ port: 9090 }));
// [ { host: 'localhost', port: 9090, protocol: 'http' } ]

Nested object caveat — shallow copy:

const original = { a: { b: 1 } };
const copy = { ...original };

copy.a.b = 99;
console.log(original.a.b);
// [ 99 ]  ← shared

Spread only copies the top level. For deep copy, use structuredClone():

const deep = structuredClone(original);

What gets copied:

Property typeCopied?
Own enumerable
Inherited
Non-enumerable
Symbol keys
Getter valuesEvaluated, then copied as value
__proto__Copied as own property (not prototype)

Spread doesn’t change the prototype:

const base = { a: 1 };
const copy = { ...base };

console.log(Object.getPrototypeOf(copy) === Object.prototype);
// [ true ]  ← not base

Comparison — spread vs alternatives:

TaskSpreadAlternative
Copy{...obj}Object.assign({}, obj)
Merge{...a, ...b}Object.assign({}, a, b)
Update{...obj, x: 1}Object.assign({}, obj, { x: 1 })
Exclude keyconst { k, ...rest } = objManual delete

Using spread with arrays of objects:

const users = [
  { name: 'Alice', active: true },
  { name: 'Bob', active: false }
];

const activated = users.map(u => ({ ...u, active: true }));
console.log(activated);
// [ [ { name: 'Alice', active: true }, { name: 'Bob', active: true } ] ]

Spread preserves order:

const result = { ...{ c: 3, a: 1 }, ...{ b: 2 } };
console.log(Object.keys(result));
// [ [ 'c', 'a', 'b' ] ]

Later keys come after, but existing keys keep their position and only their value updates.


Complete Example Session

// ============================================
// PART 1: ARRAY COPY
// ============================================

const arr = [1, 2, 3];
const copy = [...arr];

console.log(copy);
// [ [ 1, 2, 3 ] ]

copy.push(4);
console.log(arr);
// [ [ 1, 2, 3 ] ]

console.log(copy);
// [ [ 1, 2, 3, 4 ] ]

// ============================================
// PART 2: COMBINE ARRAYS
// ============================================

const more = [0, ...arr, 4];
console.log(more);
// [ [ 0, 1, 2, 3, 4 ] ]

const a = [1, 2];
const b = [3, 4];
console.log([...a, ...b]);
// [ [ 1, 2, 3, 4 ] ]

// ============================================
// PART 3: OBJECT COPY
// ============================================

const obj = { a: 1, b: 2 };
const objCopy = { ...obj };
console.log(objCopy);
// [ { a: 1, b: 2 } ]

objCopy.a = 99;
console.log(obj.a);
// [ 1 ]

// ============================================
// PART 4: MERGE OBJECTS
// ============================================

const merged = { ...obj, c: 3 };
console.log(merged);
// [ { a: 1, b: 2, c: 3 } ]

const overridden = { ...obj, a: 99 };
console.log(overridden);
// [ { a: 99, b: 2 } ]

// ============================================
// PART 5: FUNCTION CALLS
// ============================================

const nums = [5, 3, 9, 1];

console.log(Math.max(...nums));
// [ 9 ]

console.log(Math.min(...nums));
// [ 1 ]

// ============================================
// PART 6: SUM WITH SPREAD
// ============================================

function sum(...args) {
  return args.reduce((a, b) => a + b, 0);
}

console.log(sum(...nums));
// [ 18 ]

// ============================================
// PART 7: DESTRUCTURING WITH REST
// ============================================

const [first, ...rest] = arr;
console.log(first);
// [ 1 ]
console.log(rest);
// [ [ 2, 3 ] ]

const { a: aa, ...others } = obj;
console.log(aa);
// [ 1 ]
console.log(others);
// [ { b: 2 } ]

// ============================================
// PART 8: SPREAD STRING
// ============================================

const chars = [...'hello'];
console.log(chars);
// [ [ 'h', 'e', 'l', 'l', 'o' ] ]

// ============================================
// PART 9: SPREAD SET
// ============================================

const set = new Set([1, 2, 2, 3]);
console.log([...set]);
// [ [ 1, 2, 3 ] ]

// ============================================
// PART 10: SPREAD MAP
// ============================================

const map = new Map([['a', 1], ['b', 2]]);
console.log([...map]);
// [ [ [ 'a', 1 ], [ 'b', 2 ] ] ]

// ============================================
// PART 11: DEDUPE ARRAY
// ============================================

const dupes = [1, 2, 2, 3, 3, 3];
console.log([...new Set(dupes)]);
// [ [ 1, 2, 3 ] ]

// ============================================
// PART 12: PUSH MANY
// ============================================

const target = [1, 2, 3];
const source = [4, 5, 6];
target.push(...source);
console.log(target);
// [ [ 1, 2, 3, 4, 5, 6 ] ]

// ============================================
// PART 13: UPDATE OBJECT
// ============================================

const user = { name: 'Alice', age: 30 };
const updated = { ...user, age: 31 };
console.log(updated);
// [ { name: 'Alice', age: 31 } ]

// ============================================
// PART 14: EXCLUDE KEY
// ============================================

const profile = { name: 'Alice', password: 'secret', age: 30 };
const { password, ...safeProfile } = profile;
console.log(safeProfile);
// [ { name: 'Alice', age: 30 } ]

// ============================================
// PART 15: MAP OBJECTS WITH SPREAD
// ============================================

const users = [
  { name: 'Alice', active: false },
  { name: 'Bob', active: false }
];

const activated = users.map(u => ({ ...u, active: true }));
console.log(activated);
// [ [ { name: 'Alice', active: true }, { name: 'Bob', active: true } ] ]

// ============================================
// PART 16: SHALLOW COPY CAVEAT
// ============================================

const nested = { a: { b: 1 } };
const shallow = { ...nested };

shallow.a.b = 99;
console.log(nested.a.b);
// [ 99 ]

// ============================================
// PART 17: DEEP COPY WITH STRUCTUREDCLONE
// ============================================

const deep = structuredClone(nested);
deep.a.b = 42;
console.log(nested.a.b);
// [ 99 ]  ← unchanged

console.log(deep.a.b);
// [ 42 ]

// ============================================
// PART 18: SPREAD VS CONCAT
// ============================================

const c1 = [1, 2].concat([3, 4]);
const c2 = [1, 2, ...[3, 4]];
console.log(c1, c2);
// [ [ 1, 2, 3, 4 ] [ 1, 2, 3, 4 ] ]

// ============================================
// PART 19: SPREAD VS OBJECT.ASSIGN
// ============================================

const o1 = Object.assign({}, { a: 1 }, { b: 2 });
const o2 = { ...{ a: 1 }, ...{ b: 2 } };
console.log(o1, o2);
// [ { a: 1, b: 2 } { a: 1, b: 2 } ]

// ============================================
// PART 20: FULL SCRIPT
// ============================================

const arr40 = [1, 2, 3];
const copy40 = [...arr40];
console.log(copy40);

const more40 = [0, ...arr40, 4];
console.log(more40);

const obj40 = { a: 1, b: 2 };
const objCopy40 = { ...obj40 };
console.log(objCopy40);

const merged40 = { ...obj40, c: 3 };
console.log(merged40);

const nums40 = [5, 3, 9, 1];
console.log(Math.max(...nums40));

function sum40(...args) {
  return args.reduce((a, b) => a + b, 0);
}
console.log(sum40(...nums40));

const [first40, ...rest40] = arr40;
console.log(first40, rest40);

const { a: a40, ...others40 } = obj40;
console.log(a40, others40);

Quick Reference

Spread Syntax

ContextSyntaxExample
Array copy[...arr]const c = [...a]
Array merge[...a, ...b]const c = [...a, ...b]
Array insert[...a, x, ...b]const c = [...a, 0, ...b]
Object copy{...obj}const c = {...o}
Object merge{...a, ...b}const c = {...a, ...b}
Function callfn(...arr)Math.max(...arr)
Iterable to array[...iter][...new Set(a)]

What Spread Works On

ValueArray spreadObject spread
Array✅ (into indexed props)
String
Set
Map✅ (pairs)
Object
Number
null✅ (no-op)
undefined✅ (no-op)

Spread vs Alternatives

TaskSpreadAlternative
Copy array[...arr]arr.slice()
Merge arrays[...a, ...b]a.concat(b)
Copy object{...obj}Object.assign({}, obj)
Merge objects{...a, ...b}Object.assign({}, a, b)
Max of arrayMath.max(...arr)Math.max.apply(null, arr)
Dedupe[...new Set(a)]Array.from(new Set(a))
Push manyarr.push(...x)arr.push.apply(arr, x)

Spread vs Rest

FeatureSpreadRest
DirectionExpandsCollects
WhereCall, literalParameter, destructuring
Examplefn(...arr)fn(...args)
Example[...a][a, ...rest]
Example{...o}{a, ...rest}

Shallow vs Deep

MethodCopy depth
[...arr]Shallow
{...obj}Shallow
structuredClone(x)Deep
JSON.parse(JSON.stringify(x))Deep (JSON-safe)

Best Practices

Do This:

// Copy arrays with spread
const copy = [...arr];                        // ✅

// Merge objects with spread
const merged = { ...defaults, ...options };   // ✅

// Update immutably
const next = { ...state, count: 1 };          // ✅

// Use spread for function arguments
Math.max(...values);                          // ✅

// Convert iterables to arrays
[...new Set(arr)];                            // ✅

// Exclude keys via rest
const { password, ...safe } = user;           // ✅

// Use structuredClone for deep copy
const deep = structuredClone(obj);            // ✅

// Spread in array methods
users.map(u => ({ ...u, active: true }));     // ✅

Don’t Do This:

// Don't spread non-iterables
[...42];                                      // ❌ TypeError
{ ...null }                                   // ✅ no-op
[...null];                                    // ❌ TypeError

// Don't expect deep copies
const copy = { ...nested };                   // ⚠️  shallow
copy.inner.x = 1;                             // mutates original

// Don't spread objects into arrays
[...{ a: 1 }];                                // ❌ TypeError

// Don't spread arrays into objects expecting arrays
{ ...[1, 2] };                                // ⚠️  gives {0: 1, 1: 2}

// Don't use spread for large arrays when performance matters
Math.max(...hugeArray);                       // ⚠️  may stack overflow

// Don't mix up spread and rest
function fn(...args) { fn2(...args); }        // ✅ different uses

// Don't spread prototype methods
const copy = { ...classInstance };            // ❌ loses prototype

Common Pitfalls

PitfallProblemSolution
Spread non-iterableTypeErrorCheck the value
Expecting deep copyShared referencesstructuredClone
Spread null in arrayTypeErrorGuard with || []
Spread object to arrayTypeErrorUse Object.values()
Stack overflowToo many argsLoop or reduce
Losing prototypeSpread class instanceUse Object.create
Spread null in objectNo-opSafe
Order confusionLater overrides earlierPlace overrides last

Real-World Examples

1. Copy an Array

const arr = [1, 2, 3];
const copy = [...arr];
console.log(copy);
// [ [ 1, 2, 3 ] ]

2. Merge Arrays

const a = [1, 2];
const b = [3, 4];
console.log([...a, ...b]);
// [ [ 1, 2, 3, 4 ] ]

3. Insert in Middle

const arr = [1, 4];
const filled = [arr[0], 2, 3, arr[1]];
console.log(filled);
// [ [ 1, 2, 3, 4 ] ]

4. Max of Array

const nums = [5, 3, 9, 1];
console.log(Math.max(...nums));
// [ 9 ]

5. Sum Arguments

function sum(...args) {
  return args.reduce((a, b) => a + b, 0);
}

console.log(sum(1, 2, 3, 4));
// [ 10 ]

6. Copy Object

const obj = { a: 1, b: 2 };
const copy = { ...obj };
console.log(copy);
// [ { a: 1, b: 2 } ]

7. Merge with Defaults

const defaults = { theme: 'light', lang: 'en' };
const user = { theme: 'dark' };
console.log({ ...defaults, ...user });
// [ { theme: 'dark', lang: 'en' } ]

8. Immutable Update

const state = { count: 0 };
const next = { ...state, count: state.count + 1 };
console.log(next);
// [ { count: 1 } ]

9. Exclude Key

const user = { name: 'Alice', password: 'secret', age: 30 };
const { password, ...safe } = user;
console.log(safe);
// [ { name: 'Alice', age: 30 } ]

10. String to Array

console.log([...'hello']);
// [ [ 'h', 'e', 'l', 'l', 'o' ] ]

11. Set to Array

const set = new Set([1, 2, 2, 3]);
console.log([...set]);
// [ [ 1, 2, 3 ] ]

12. Dedupe Array

const dupes = [1, 2, 2, 3, 3, 3];
console.log([...new Set(dupes)]);
// [ [ 1, 2, 3 ] ]

13. Push Many

const arr = [1, 2, 3];
arr.push(...[4, 5, 6]);
console.log(arr);
// [ [ 1, 2, 3, 4, 5, 6 ] ]

14. Map Objects

const users = [
  { name: 'Alice', active: false },
  { name: 'Bob', active: false }
];

const activated = users.map(u => ({ ...u, active: true }));
console.log(activated);
// [ [ { name: 'Alice', active: true }, { name: 'Bob', active: true } ] ]

15. Combine Spread and Rest

function tail([, ...rest]) {
  return rest;
}

console.log(tail([1, 2, 3, 4]));
// [ [ 2, 3, 4 ] ]

16. Pass Array as Arguments

function add(a, b, c) {
  return a + b + c;
}

console.log(add(...[1, 2, 3]));
// [ 6 ]

17. Clone and Modify

const original = { name: 'Alice', age: 30 };
const updated = { ...original, age: 31, city: 'Paris' };
console.log(updated);
// [ { name: 'Alice', age: 31, city: 'Paris' } ]

18. Spread Array of Objects

const parts = [{ a: 1 }, { b: 2 }];
const merged = Object.assign({}, ...parts);
console.log(merged);
// [ { a: 1, b: 2 } ]

19. Shallow vs Deep

const nested = { a: { b: 1 } };
const shallow = { ...nested };
shallow.a.b = 99;
console.log(nested.a.b);
// [ 99 ]

const deep = structuredClone(nested);
deep.a.b = 42;
console.log(nested.a.b);
// [ 99 ]

20. Full Script

const arr41 = [1, 2, 3];
const copy41 = [...arr41];
console.log(copy41);

const more41 = [0, ...arr41, 4];
console.log(more41);

const obj41 = { a: 1, b: 2 };
const objCopy41 = { ...obj41 };
console.log(objCopy41);

const merged41 = { ...obj41, c: 3 };
console.log(merged41);

const nums41 = [5, 3, 9, 1];
console.log(Math.max(...nums41));

function sum41(...args) {
  return args.reduce((a, b) => a + b, 0);
}
console.log(sum41(...nums41));

const [first41, ...rest41] = arr41;
console.log(first41, rest41);

const { a: a41, ...others41 } = obj41;
console.log(a41, others41);

Visual: Spread Expands

┌──────────────────────────────────────────────┐
│  arr = [1, 2, 3]                             │
│                                              │
│  [...arr]        →  1, 2, 3                  │
│  [0, ...arr]     →  0, 1, 2, 3               │
│  [...arr, 4]     →  1, 2, 3, 4               │
│                                              │
│  Spread EXPANDS the array into elements      │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│  obj = { a: 1, b: 2 }                        │
│                                              │
│  {...obj}            →  { a: 1, b: 2 }       │
│  {...obj, c: 3}      →  { a: 1, b: 2, c: 3 } │
│                                              │
│  Spread EXPANDS into key-value pairs         │
│                                              │
└──────────────────────────────────────────────┘

Visual: Spread vs Rest

┌──────────────────────────────────────────────┐
│           SPREAD (expands)                   │
│                                              │
│  const arr = [1, 2, 3];                      │
│  fn(...arr)                                  │
│       │                                      │
│       └──►  fn(1, 2, 3)                      │
│                                              │
│  const copy = [...arr];                      │
│       │                                      │
│       └──►  [1, 2, 3]                        │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│           REST (collects)                    │
│                                              │
│  function fn(...args) {                      │
│                │                             │
│                └──►  args = [1, 2, 3]        │
│  }                                           │
│                                              │
│  const [a, ...rest] = [1, 2, 3];             │
│              │                               │
│              └──►  rest = [2, 3]             │
│                                              │
└──────────────────────────────────────────────┘

Visual: Shallow vs Deep Copy

┌──────────────────────────────────────────────┐
│           Shallow copy ({...obj})            │
│                                              │
│  original ──► { a: { b: 1 } }                │
│                     ▲                        │
│                     │                        │
│  copy ────────► { a: ─────────────────────┐  │
│                                          │   │
│              inner object shared         │   │
│              changing copy.a.b           │   │
│              changes original.a.b too    │   │
│                                              │
└──────────────────────────────────────────────┘

┌──────────────────────────────────────────────┐
│           Deep copy (structuredClone)        │
│                                              │
│  original ──► { a: { b: 1 } }                │
│                                              │
│  copy ──────► { a: { b: 1 } }  (separate)    │
│                                              │
│              no shared references            │
│                                              │
└──────────────────────────────────────────────┘

Summary

ConceptSyntaxExample
Array copy[...arr]const c = [...a]
Array merge[...a, ...b]const c = [...a, ...b]
Array insert[...a, x, ...b][0, ...arr, 4]
Object copy{...obj}const c = {...o}
Object merge{...a, ...b}{...defaults, ...options}
Object update{...o, k: v}{...state, count: 1}
Function callfn(...arr)Math.max(...arr)
Iterable to array[...iter][...new Set(a)]
String to array[...str][...'hello']
Map to array[...map][...new Map()]
Dedupe[...new Set(a)][...new Set([1,1,2])]
Push manyarr.push(...x)arr.push(...[1,2,3])
Exclude keyconst {k, ...r} = objconst {pw, ...safe} = user
Deep copystructuredClone(x)Separate references

Key takeaways:

  • Spread (...) expands iterables and objects into individual elements
  • Rest (...) collects into arrays — same syntax, opposite direction
  • Array spread: [...arr], [...a, ...b], [0, ...arr, 4]
  • Object spread: {...obj}, {...a, ...b}, {...obj, k: v}
  • Function calls: fn(...arr), Math.max(...nums)
  • Works on: arrays, strings, Sets, Maps, arguments, and objects (object spread only)
  • Doesn’t work on: numbers, booleans, null, undefined (throws on iterables)
  • Spread does a shallow copy — nested objects are shared
  • Use structuredClone() for a deep copy
  • Later properties override earlier ones in merges
  • Immutable updates are the most common use: {...state, count: 1}
  • Exclude keys with object destructuring + rest: const {pw, ...safe} = user
  • Spread replaces concat, apply, and Object.assign in most modern code

Remember: ... is the most versatile punctuation in JavaScript. In a call or literal, it spreads — expanding an iterable or object into its elements. In a parameter list or destructuring pattern, it rests — collecting the remaining items into an array or object. Learn the difference and it stops being confusing. Spread replaces concat, apply, and Object.assign. It powers immutable updates. And when you need a real deep copy, reach for structuredClone. Master spread, and your code gets shorter, cleaner, and safer.


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!