Destructuring assignment in JavaScript
Destructuring assignment is a JavaScript syntax that pulls values from arrays or properties from objects into individual variables, in one step instead of multiple manual assignments.
Theory
TL;DR
- Think of it like unpacking a labeled box: you name each slot and pull only what you need
- Arrays match by position; objects match by key name
- Defaults apply only when the value is
undefined, not when it isnullor0 - Rest (
...) collects remaining items and must always come last - Use it when extracting 2+ values; for a single value, dot notation is cleaner
Quick example
// Array: position-based
const [first, second] = [10, 20, 30];
console.log(first); // 10
console.log(second); // 20
// Object: key-based
const user = { name: 'Alice', age: 25 };
const { name, age } = user;
console.log(name); // 'Alice'
console.log(age); // 25The engine reads left-to-right: for arrays it uses indices, for objects it looks up the property name. The compiled output is equivalent to manual assignment, so there is no performance difference.
When to use
- API responses with multiple fields:
const { data, error } = await fetchUser(id) - Function parameters from an object:
function greet({ name, role = 'user' }) {} - Variable swapping:
[a, b] = [b, a]without a temp variable - React hooks:
const [count, setCount] = useState(0) - Named imports:
import { useState, useEffect } from 'react'
Skip it for a single value. user.name is more readable than const { name } = user when that is all you need.
Array patterns
Skipping elements uses an empty slot:
const [first, , third] = [1, 2, 3];
console.log(third); // 3Rest gathers the remaining items:
const [head, ...tail] = [1, 2, 3, 4];
console.log(tail); // [2, 3, 4]Defaults fill in only when the value is undefined:
const [x = 5] = [0];
console.log(x); // 0 (not 5, because 0 is not undefined)Object patterns
Rename a property with a colon after the key:
const { name: userName } = { name: 'Alice' };
console.log(userName); // 'Alice'Nested patterns require nested syntax:
const { address: { city } } = { address: { city: 'Kyiv' } };
console.log(city); // 'Kyiv'If address is undefined, this throws a TypeError. Add a default or use optional chaining before destructuring.
Rest on objects collects all remaining keys:
const { id, ...rest } = { id: 1, name: 'Alice', age: 25 };
console.log(rest); // { name: 'Alice', age: 25 }Common mistakes
Nested destructuring throws on undefined.
const data = {};
const { user: { name } } = data; // TypeError: Cannot read properties of undefinedFix: const { user: { name } = {} } = data or use data.user?.name.
Rest cannot come before the last position.
const [...rest, last] = [1, 2, 3]; // SyntaxErrorRest must be the final element. [first, ...rest] works. [...rest, last] does not compile.
Wrong key name when renaming.
const { userName } = { name: 'Alice' };
console.log(userName); // undefineduserName looks for a key literally called userName. Use { name: userName } to rename it.
Defaults do not replace null.
const [x = 5] = [null];
console.log(x); // null (defaults only apply for undefined)Real-world usage
- React:
const [value, setValue] = useState(''), component propsfunction Button({ onClick, label }) - Express/Node.js:
const { id } = req.params,const { page = 1 } = req.query - Redux Toolkit:
const { type, payload: { id } } = action - SWR/React Query:
const { data, error } = useSWR('/api/user')
Follow-up questions
Q: What does const [x = 1] = [0] output?
A: 0. The default only applies when the value is undefined. Zero is a valid defined value.
Q: What happens when you destructure a key that does not exist on the object?
A: You get undefined. No error is thrown unless you then try to destructure a property of that undefined value.
Q: How do null and undefined behave differently with defaults?
A: Only undefined triggers the default. null passes through as-is. This catches many people off guard during interviews.
Q: Can you destructure a function return value inline?
A: Yes. const [success, data] = await fetchData() works whenever the function returns an array.
Q: (Senior) How does V8 compile const { x } = obj?
A: It emits a GetOwnProperty check for key x on obj, then assigns the result to a new binding. Array patterns compile to indexed accesses like arr[0]. Rest uses property enumeration or slice. The bytecode is equivalent to manual assignment, so there is no runtime overhead.
Examples
Basic: arrays and objects
const coords = [10, 20, 30];
const [x, y] = coords;
console.log(x, y); // 10 20
const product = { title: 'Keyboard', price: 99, stock: 5 };
const { title, price } = product;
console.log(title, price); // 'Keyboard' 99Array destructuring picks by index, object destructuring picks by key. A missing key gives undefined, not an error.
Real-world: function params with defaults
// The expected shape of input is visible right at the signature
function handleRequest({ params: { id }, query: { role = 'viewer', page = 1 } }) {
console.log(id, role, page);
}
handleRequest({ params: { id: '42' }, query: { role: 'admin' } });
// '42', 'admin', 1This pattern appears in React components, Express handlers, and Redux reducers. I find it most useful here: you see what the function expects without reading the body. That alone cuts the time spent understanding unfamiliar code.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.