← thecodex.expert · The Codex Family of Knowledge
The Codex / JavaScript / Destructuring & Spread

Destructuring & Spread

Unpacking arrays and objects into variables, the rest pattern, and spreading values — the ES2015 features that cleaned up modern JavaScript.

What it is, in plain English: Destructuring is a shorthand for extracting values from arrays or objects into named variables. Instead of array[0], array[1], you write const [first, second] = array. Spread (...) does the opposite — it expands an array or object into individual items. Rest (...) collects remaining items into an array. Together these three patterns appear in almost every modern JS codebase.
Where this bites you

A destructuring default is used only when the value is undefined, meaning the key is missing or explicitly undefined. null is a real value, so it goes straight through and the default is skipped. APIs and databases often send null to mean "unknown", which is how a perfectly reasonable default ends up printing the word null on screen.

function greet({ name, city = "Mumbai" }) {
  console.log(`Hello ${name} from ${city}!`);
}

const fromApi = { name: "Bob", city: null };   // the API sends null for "unknown"
greet(fromApi);
// Hello Bob from null!

Unpacking and expanding values

Destructuring and spread are two sides of the same coin. Destructuring pulls values out — from an array or object into variables. Spread pushes values in — expands an array or object into a new context. Once you learn these patterns, you'll use them constantly.

Array destructuring

// Old way — repetitive:
const arr = [10, 20, 30];
const first  = arr[0];
const second = arr[1];

// Destructuring — clean:
const [a, b, c] = [10, 20, 30];
console.log(a, b, c);   // 10 20 30

// Skip elements with empty commas:
const [x, , z] = [10, 20, 30];
console.log(x, z);   // 10 30  (20 is skipped)

// Rest — collect remaining items:
const [head, ...tail] = [1, 2, 3, 4, 5];
console.log(head);   // 1
console.log(tail);   // [2, 3, 4, 5]

// Default values (used if element is undefined):
const [p = 0, q = 0, r = 0] = [10, 20];
console.log(p, q, r);   // 10 20 0  ← r got its default

The swap trick — no temporary variable needed

let x = 1, y = 2;

// Old way — needs a temp:
// let temp = x; x = y; y = temp;

// Destructuring swap — one line:
[x, y] = [y, x];
console.log(x, y);   // 2 1

Object destructuring

const person = { name: "Alice", age: 30, city: "Mumbai" };

// Old way:
const name1 = person.name;
const age1  = person.age;

// Destructuring — cleaner:
const { name, age, city } = person;
console.log(name, age, city);  // "Alice" 30 "Mumbai"

// Rename while extracting (key: newName):
const { name: fullName, age: years } = person;
console.log(fullName, years);  // "Alice" 30

// Default value (used if key is missing):
const { country = "India", email = "none" } = person;
console.log(country);   // "India" — person has no country

// Most common use: function parameters
function greet({ name, city = "Mumbai" }) {
  console.log(`Hello ${name} from ${city}!`);
}
greet({ name: "Bob", city: "Delhi" });  // "Hello Bob from Delhi!"
greet({ name: "Carol" });               // "Hello Carol from Mumbai!"

The greet({ name: "Carol" }) call above gets "Mumbai" because city is missing, which is undefined. When the data might contain null as well, apply the fallback with ?? (nullish coalescing), which covers both null and undefined but leaves real values such as "" and 0 alone:

function greet({ name, city }) {
  console.log(`Hello ${name} from ${city ?? "Mumbai"}!`);   // ?? covers null AND undefined
}

const fromApi = { name: "Bob", city: null };
greet(fromApi);
// Hello Bob from Mumbai!

Spread — copy and combine

// Spread arrays:
const nums = [1, 2, 3];
const moreNums = [...nums, 4, 5, 6];
console.log(moreNums);  // [1, 2, 3, 4, 5, 6]

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

// Copy an array (spread fixes the reference trap):
const original = [1, 2, 3];
const copy = [...original];   // new array, same values
copy.push(4);
console.log(original);  // [1, 2, 3] — unaffected!

// Spread objects:
const defaults = { theme: "light", lang: "en" };
const custom   = { lang: "hi", fontSize: 16 };

// Merge — later keys win:
const merged = { ...defaults, ...custom };
console.log(merged);  // { theme: "light", lang: "hi", fontSize: 16 }

// Shallow copy:
const obj     = { a: 1, b: 2 };
const objCopy = { ...obj };
objCopy.a = 99;
console.log(obj.a);     // 1 — original unchanged

In practice — the patterns you'll see everywhere

// React / state update pattern:
const state   = { count: 0, user: "Alice", loading: false };
const newState = { ...state, count: state.count + 1 };  // update one field

// API response unpacking:
async function getUser(id) {
  const { name, email, role } = await fetchUser(id);  // destructure directly
  return { name, email, role };
}

// Passing partial options to a function:
function createButton({ label = "Click", color = "blue", size = "md" } = {}) {
  return { label, color, size };
}
createButton({ label: "Submit" });  // { label: "Submit", color: "blue", size: "md" }
Try It Yourself

Official Sources

The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.