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 ...:
| Context | Name | Behavior |
|---|---|---|
Function call — fn(...arr) | Spread | Expand into arguments |
Array literal — [...arr] | Spread | Expand into elements |
Object literal — {...obj} | Spread | Expand into properties |
Function parameter — fn(...args) | Rest | Collect into array |
Destructuring — [a, ...rest] | Rest | Collect 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:
| Value | Spreadable? |
|---|---|
| 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
applyandconcat - 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:
| Task | Spread | Alternative |
|---|---|---|
| Copy | [...arr] | arr.slice() |
| Concat | [...a, ...b] | a.concat(b) |
| Max | Math.max(...arr) | Math.max.apply(null, arr) |
| Push many | arr.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 type | Copied? |
|---|---|
| Own enumerable | ✅ |
| Inherited | ❌ |
| Non-enumerable | ❌ |
| Symbol keys | ✅ |
| Getter values | Evaluated, 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:
| Task | Spread | Alternative |
|---|---|---|
| Copy | {...obj} | Object.assign({}, obj) |
| Merge | {...a, ...b} | Object.assign({}, a, b) |
| Update | {...obj, x: 1} | Object.assign({}, obj, { x: 1 }) |
| Exclude key | const { k, ...rest } = obj | Manual 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
| Context | Syntax | Example |
|---|---|---|
| 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 call | fn(...arr) | Math.max(...arr) |
| Iterable to array | [...iter] | [...new Set(a)] |
What Spread Works On
| Value | Array spread | Object spread |
|---|---|---|
| Array | ✅ | ✅ (into indexed props) |
| String | ✅ | ✅ |
| Set | ✅ | ❌ |
| Map | ✅ (pairs) | ❌ |
| Object | ❌ | ✅ |
| Number | ❌ | ❌ |
| null | ❌ | ✅ (no-op) |
| undefined | ❌ | ✅ (no-op) |
Spread vs Alternatives
| Task | Spread | Alternative |
|---|---|---|
| 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 array | Math.max(...arr) | Math.max.apply(null, arr) |
| Dedupe | [...new Set(a)] | Array.from(new Set(a)) |
| Push many | arr.push(...x) | arr.push.apply(arr, x) |
Spread vs Rest
| Feature | Spread | Rest |
|---|---|---|
| Direction | Expands | Collects |
| Where | Call, literal | Parameter, destructuring |
| Example | fn(...arr) | fn(...args) |
| Example | [...a] | [a, ...rest] |
| Example | {...o} | {a, ...rest} |
Shallow vs Deep
| Method | Copy 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
| Pitfall | Problem | Solution |
|---|---|---|
| Spread non-iterable | TypeError | Check the value |
| Expecting deep copy | Shared references | structuredClone |
Spread null in array | TypeError | Guard with || [] |
| Spread object to array | TypeError | Use Object.values() |
| Stack overflow | Too many args | Loop or reduce |
| Losing prototype | Spread class instance | Use Object.create |
Spread null in object | No-op | Safe |
| Order confusion | Later overrides earlier | Place 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
| Concept | Syntax | Example |
|---|---|---|
| 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 call | fn(...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 many | arr.push(...x) | arr.push(...[1,2,3]) |
| Exclude key | const {k, ...r} = obj | const {pw, ...safe} = user |
| Deep copy | structuredClone(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, andObject.assignin 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!