Destructuring
Unpack arrays & objects into variables — positional vs by-key, defaults, renaming, nesting, swapping, and function-param destructuring.
Destructuring unpacks values out of arrays (by position) and objects (by key) into standalone variables — less boilerplate, clearer intent.
flowchart LR
ARR["[10, 20]"] -->|"by position"| a["const [x, y]"]
OBJ["{ name, age }"] -->|"by key name"| b["const { name, age }"]
Array destructuring — by position#
const [a, b] = [1, 2]; // a=1, b=2
const [, , third] = [1, 2, 3]; // skip with commas → third=3
const [head, ...tail] = [1, 2, 3]; // rest → head=1, tail=[2,3]
const [x = 10] = []; // default → x=10 (source was undefined)
// the famous swap — no temp variable
let p = 1, q = 2;
[p, q] = [q, p]; // p=2, q=1
Object destructuring — by key#
const user = { name: "Ada", age: 30, city: "NYC" };
const { name, age } = user; // by matching key name
const { city: town } = user; // rename: town = "NYC" 🎯
const { role = "user" } = user; // default when key missing
const { name: n = "Anon" } = user; // rename + default together
const { city, ...rest } = user; // rest → rest = { name, age }
⚠️ Rename uses
key: newName— the opposite order of what many expect.{ city: town }means "readcity, call ittown." It is not creating acityvariable.
Nested destructuring#
const res = { data: { items: [{ id: 1 }] }, ok: true };
const { data: { items: [{ id }] }, ok } = res; // id = 1, ok = true
🟢 Reach for one level or two; deeply nested destructuring gets hard to read fast.
Function parameters — the "options object" pattern 🎯#
The cleanest way to take named options with defaults:
function createUser({ name, role = "user", active = true } = {}) {
return { name, role, active };
}
createUser({ name: "Ada" }); // { name:"Ada", role:"user", active:true }
createUser(); // works — `= {}` lets you omit the arg entirely
Array-param destructuring works too — great with map/entries:
[[1, "a"], [2, "b"]].map(([num, char]) => `${num}${char}`); // ["1a","2b"]
Object.entries(user).forEach(([key, val]) => console.log(key, val));
Gotchas 🎯#
const { a } = null; // ❌ TypeError — can't destructure null/undefined
const { a } = obj ?? {}; // ✅ guard with a fallback object
let title;
{ title } = data; // ❌ SyntaxError — { looks like a block
({ title } = data); // ✅ wrap in parens when assigning to existing vars
- A default fires only for
undefined, notnull/0/"". - Destructuring copies references for nested objects (it's not a deep copy).
Interview Q&A#
Q1. Array vs object destructuring — what does each match on?
Array destructuring binds by position (const [a, b] = arr), so order matters and you skip slots with commas. Object destructuring binds by key name (const { name } = obj), so order is irrelevant and missing keys yield undefined.
Q2. How do you rename a key while destructuring? Rename and default it?
Use key: newName (the opposite order of what people expect), and combine with = for a default: const { name: n = "Anon" } = user; reads name, calls it n, and falls back to "Anon" if the key is undefined.
Q3. Swap two variables without a temp using destructuring.
let p = 1, q = 2;
[p, q] = [q, p]; // p=2, q=1
Q4. Why does { title } = data throw, and how do you fix it?
A statement starting with { is parsed as a block, not an object pattern, so it's a SyntaxError. Wrap the assignment in parens: ({ title } = data);.
Q5. Does a destructuring default trigger for null?
No — defaults fire only for undefined, not for null, 0, or "". const { role = "user" } = { role: null } leaves role as null.