Array Destructuring, Spread and Rest

▶ Try It Yourself

Destructuring is one of the most useful ES6 features — it lets you unpack values from arrays (and objects) into named variables in a single, expressive line. Combined with the spread operator (...) for expanding arrays and the rest pattern for collecting remaining elements, destructuring makes JavaScript code dramatically more readable and less repetitive. These patterns appear constantly in React, API data handling, function parameters, and state management.

Array Destructuring Patterns

Pattern Syntax Effect
Basic const [a, b] = [1, 2] a=1, b=2
Skip elements const [a, , c] = [1, 2, 3] a=1, c=3 — middle skipped
Default values const [a=0, b=0] = [1] a=1, b=0 — default when undefined
Rest element const [first, ...rest] = [1,2,3,4] first=1, rest=[2,3,4]
Swap variables [a, b] = [b, a] Swap without temp variable
Nested const [[a, b], [c, d]] = [[1,2],[3,4]] Unpack nested arrays
Function return const [x, y] = getCoords() Unpack multiple return values

Spread Operator Uses

Use Case Syntax Example
Copy array [...arr] Shallow clone
Merge arrays [...a, ...b] Concatenation without concat()
Insert items [...a, newItem, ...b] Insert in middle
Spread to function Math.max(...arr) Pass array as individual args
String to chars [...'hello'] ['h','e','l','l','o']
Set to array [...new Set(arr)] Deduplicate array

Rest vs Spread

Syntax Role Where Used
...rest (rest) Collects multiple items into one array Destructuring patterns, function parameters
...spread (spread) Expands one array into individual items Array literals, function call arguments
Note: The rest element in destructuring must always be the last element: const [first, ...rest] = arr is valid; const [...rest, last] = arr is a SyntaxError. This mirrors the rest parameter rule in function signatures. The rest element collects whatever is left after the named elements are assigned.
Tip: Destructuring with default values elegantly handles missing data from APIs: const [status = 200, message = 'OK', data = []] = apiResponse. Each variable gets the API value when present, or a sensible default when the position is undefined. This is much cleaner than three separate null-check ternaries after the fact.
Warning: Spread creates a shallow copy — nested arrays and objects are not deep-cloned. const copy = [...original] creates a new array, but any objects inside are still shared references. Modifying copy[0].name also changes original[0].name. For deep cloning, use structuredClone(original).

Basic Example

// ── Basic destructuring ───────────────────────────────────────────────────
const [red, green, blue] = [255, 128, 0];
console.log(red, green, blue);   // 255 128 0

// ── Skip elements ─────────────────────────────────────────────────────────
const [first, , third, , fifth = 99] = [10, 20, 30, 40];
console.log(first, third, fifth);   // 10 30 99 (fifth defaults to 99)

// ── Swap variables — no temp needed ──────────────────────────────────────
let x = 5, y = 10;
[x, y] = [y, x];
console.log(x, y);   // 10 5

// ── Rest element ──────────────────────────────────────────────────────────
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head);   // 1
console.log(tail);   // [2,3,4,5]

// ── Function returning multiple values ───────────────────────────────────
function getMinMax(numbers) {
    const sorted = [...numbers].sort((a, b) => a - b);
    return [sorted[0], sorted.at(-1)];
}

const [min, max] = getMinMax([5, 1, 8, 3, 9, 2]);
console.log(min, max);   // 1 9

// ── Nested array destructuring ────────────────────────────────────────────
const matrix = [[1, 2], [3, 4], [5, 6]];
const [[a, b], [c, d]] = matrix;
console.log(a, b, c, d);   // 1 2 3 4

// ── Spread — copy and merge ───────────────────────────────────────────────
const original = [1, 2, 3];
const copy      = [...original];       // shallow clone
const extended  = [...original, 4, 5]; // [1,2,3,4,5]
const prepended = [0, ...original];    // [0,1,2,3]

original.push(99);
console.log(original);   // [1,2,3,99] — changed
console.log(copy);       // [1,2,3]    — independent copy

// ── Spread to function arguments ─────────────────────────────────────────
const nums    = [3, 1, 4, 1, 5, 9, 2, 6];
console.log(Math.max(...nums));   // 9
console.log(Math.min(...nums));   // 1

// ── Deduplication with Set + spread ──────────────────────────────────────
const withDupes = ['css', 'js', 'html', 'css', 'js', 'react'];
const unique    = [...new Set(withDupes)];
console.log(unique);   // ['css','js','html','react']

// ── Destructuring in for...of ─────────────────────────────────────────────
const pairs = [['alice', 30], ['bob', 25], ['carol', 35]];
for (const [name, age] of pairs) {
    console.log(`${name} is ${age}`);
}
// alice is 30 / bob is 25 / carol is 35

// ── Destructuring entries() ───────────────────────────────────────────────
const fruits = ['apple', 'banana', 'cherry'];
for (const [index, fruit] of fruits.entries()) {
    console.log(`${index + 1}. ${fruit}`);
}

How It Works

Step 1 — Destructuring Matches by Position

Array destructuring assigns values by position, not by name. The first variable gets index 0, the second gets index 1, and so on. Empty commas [a,,c] skip positions — the second element is evaluated but discarded. If the array is shorter than the pattern, missing positions resolve to undefined (or the default value if provided).

Step 2 — Defaults Activate on undefined Only

Just like function default parameters, destructuring defaults activate when the value at that position is undefined — not for null, 0, false, or ''. This mirrors the behaviour of ?? but through the assignment syntax rather than an operator.

Step 3 — Variable Swap Without Temp

[a, b] = [b, a] creates a temporary array [b, a] on the right side and immediately destructures it into a and b. JavaScript evaluates the right side completely before assigning — so both original values are captured before either variable is overwritten.

Step 4 — Spread Calls the Iterable Protocol

...arr in an array literal or function call invokes the same iterable protocol as for...of. This means spread works on any iterable — arrays, strings, Sets, Maps, NodeLists, generator results. [...'hello'] produces ['h','e','l','l','o'] because strings are iterable character-by-character.

Step 5 — structuredClone for Deep Copies

When you need a fully independent copy where nested objects are also cloned, use structuredClone(arr) (ES2022). It handles nested objects, arrays, Maps, Sets, and Dates — things that JSON.parse(JSON.stringify(arr)) cannot handle (functions, undefined, circular references).

Real-World Example: CSV Parser and Coordinate System

// Parsing CSV rows with destructuring
function parseUserCSV(csvRow) {
    const [id, name, email, role = 'viewer', active = 'true'] = csvRow.split(',').map(s => s.trim());
    return {
        id:     Number(id),
        name,
        email,
        role,
        active: active === 'true',
    };
}

const rows = [
    '1, Alice Smith,  alice@example.com, admin, true',
    '2, Bob Jones,    bob@example.com,   editor',
    '3, Carol White,  carol@example.com',
];

const users = rows.map(parseUserCSV);
console.log(users);
// [
//   { id:1, name:'Alice Smith', email:'alice@example.com', role:'admin',   active:true },
//   { id:2, name:'Bob Jones',   email:'bob@example.com',   role:'editor',  active:true },
//   { id:3, name:'Carol White', email:'carol@example.com', role:'viewer',  active:true },
// ]

// Vector math with destructuring
function addVectors([x1, y1], [x2, y2])    { return [x1 + x2, y1 + y2]; }
function scaleVector([x, y], scalar)        { return [x * scalar, y * scalar]; }
function dotProduct([x1, y1], [x2, y2])    { return x1 * x2 + y1 * y2; }
function magnitude([x, y])                 { return Math.sqrt(x ** 2 + y ** 2); }

const v1 = [3, 4];
const v2 = [1, 2];

const [rx, ry] = addVectors(v1, v2);
console.log(`Sum: [${rx}, ${ry}]`);           // Sum: [4, 6]
console.log(`Magnitude of v1: ${magnitude(v1)}`);  // 5
console.log(`Dot product: ${dotProduct(v1, v2)}`);  // 11

// Immutable state updates with spread
const state = { items: ['a', 'b', 'c'], selected: 0 };

// Add item — immutable
const afterAdd = { ...state, items: [...state.items, 'd'] };

// Remove item at index 1 — immutable
const afterRemove = {
    ...state,
    items: state.items.filter((_, i) => i !== 1),
};

// Replace item at index 0 — immutable
const afterUpdate = {
    ...state,
    items: state.items.map((item, i) => i === 0 ? 'A' : item),
};

console.log(state.items);        // ['a','b','c'] — original unchanged
console.log(afterAdd.items);     // ['a','b','c','d']
console.log(afterRemove.items);  // ['a','c']
console.log(afterUpdate.items);  // ['A','b','c']

Common Mistakes

Mistake 1 — Rest element is not last

❌ Wrong — rest must be the final element:

const [...rest, last] = [1, 2, 3];   // SyntaxError

✅ Correct — rest goes at the end:

const [first, ...rest] = [1, 2, 3];  // first=1, rest=[2,3]

Mistake 2 — Spread creates shallow copy — shared nested references

❌ Wrong — nested objects are still shared:

const original = [{ name: 'Alice' }, { name: 'Bob' }];
const copy      = [...original];
copy[0].name    = 'Carol';
console.log(original[0].name);   // 'Carol' — original mutated!

✅ Correct — use structuredClone for deep copies:

const copy = structuredClone(original);
copy[0].name = 'Carol';
console.log(original[0].name);   // 'Alice' — untouched

Mistake 3 — Destructuring undefined throws TypeError

❌ Wrong — trying to destructure undefined or null throws:

const [a, b] = undefined;   // TypeError: undefined is not iterable

✅ Correct — provide a fallback with nullish coalescing:

const [a, b] = apiResponse ?? [];   // safe — falls back to empty array

▶ Try It Yourself

Quick Reference

Pattern Syntax Result
Basic const [a, b] = arr First two elements
Skip const [a, , c] = arr 1st and 3rd elements
Default const [a = 0] = arr 0 if arr[0] is undefined
Rest const [first, ...rest] = arr first = arr[0], rest = arr.slice(1)
Swap [a, b] = [b, a] Variables swapped
Shallow copy [...arr] New array, shared nested refs
Deep copy structuredClone(arr) Fully independent clone
Deduplicate [...new Set(arr)] Unique values only

🧠 Test Yourself

What does const [a, , b, c = 99] = [1, 2, 3] assign to c?





▶ Try It Yourself