Free lesson · JavaScript Basic to Advance
Destructuring: Objects and Arrays
Pulling Values Out, the Modern Way
Destructuring is a syntax for extracting values from objects and arrays into individual variables. It looks like the opposite of construction — instead of writing { name: "Olena" } to build an object, you write const { name } = user to take the name property out of one.
// Object destructuring
const user = { name: "Olena", age: 35, role: "Head of Product" };
const { name, age } = user;
console.log(name, age);
// Array destructuring
const colors = ["red", "green", "blue"];
const [first, second, third] = colors;
console.log(first, second, third);
Two lines to extract three values, with names automatically. Compare to the pre-ES6 equivalent: const name = user.name; const age = user.age;. The destructuring version is shorter, more declarative, and impossible to typo a variable name without JavaScript noticing.
Destructuring is one of the most-used modern JavaScript features. You'll see it in every real codebase — function signatures, variable declarations, import statements, return values. This Topic covers both forms (objects and arrays) and the patterns built on top of them.
When I started learning JavaScript in 2018, destructuring was the feature I picked up slowest. The old style was so ingrained that I kept writing user.name even when I knew destructuring existed. It took weeks of deliberate practice to make the modern syntax automatic. Worth the effort — once it clicks, you write significantly cleaner code.
Interactive animation: Brackets take positions, braces take names - an array and an object unpacked side by side, with the output.
Object Destructuring
The pattern: write the names of the properties you want, in curly braces, on the left of =. JavaScript extracts those properties from the right-hand object into variables of the same name.
const user = {
name: "Olena",
age: 35,
role: "Head of Product",
company: "Microsoft"
};
// Extract specific properties
const { name, role } = user;
console.log(name, role);
// You can rename while destructuring
const { name: userName, age: userAge } = user;
console.log(userName, userAge);
// Defaults if a property is missing
const { name: n, location = "Unknown" } = user;
console.log(n, location);
// Combine rename and default
const { city: userCity = "Redmond" } = user;
console.log(userCity);
The features stack together: extract a property, rename it as you go, provide a default if it's missing. All in the same syntax.
The default only triggers when the property is undefined — the same rule as default parameters in Topic 5.4. null and 0 and "" are valid values and don't trigger the default.
Interactive animation: Rename, default, and both at once - the default fills a missing property but leaves a null untouched.
Nested Object Destructuring
If your object has nested objects, you can destructure deeper:
const user = {
name: "Olena",
address: {
city: "Redmond",
state: "WA",
country: "USA"
},
contact: {
email: "olena@example.com"
}
};
// Extract from nested objects
const { name, address: { city, state } } = user;
console.log(name, city, state);
// The nested object isn't a separate variable — just a path
// console.log(address); // ❌ ReferenceError
Notice the gotcha at the bottom. When you write address: { city, state }, you're saying "go into the address property and extract city and state from there." You're not creating a variable called address. If you want both — the nested values and the parent object — you'd need to destructure twice.
Nested destructuring is powerful but can become unreadable. Three levels of nesting in a single destructuring expression is usually too dense. When in doubt, break it into multiple steps.
Interactive animation: Deep reaches, guarded at each level - an = {} default keeps a missing nested object from throwing.
Array Destructuring
The same idea, but with arrays and position-based extraction:
const coords = [10, 20, 30];
const [x, y, z] = coords;
console.log(x, y, z);
// Skip elements with commas
const [first, , third] = coords;
console.log(first, third); // 10, 30
// Defaults work the same
const [a = 1, b = 2, c = 3, d = 4] = [10, 20];
console.log(a, b, c, d); // 10, 20, 3, 4
// Swap variables — clean idiom
let val1 = "old1";
let val2 = "old2";
[val1, val2] = [val2, val1];
console.log(val1, val2); // "old2", "old1"
The swap idiom is one of my favorites. Without destructuring, swapping two variables requires a temporary — let tmp = a; a = b; b = tmp;. With destructuring, it's one line.
Function Parameter Destructuring
This is where destructuring earns most of its keep. When a function takes an object parameter, destructuring in the signature makes the expected shape visible:
// Without destructuring
function describeUserOld(user) {
const name = user.name;
const age = user.age;
return `${name} is ${age} years old`;
}
// With destructuring — much cleaner
function describeUser({ name, age }) {
return `${name} is ${age} years old`;
}
const user = { name: "Olena", age: 35, role: "Head of Product" };
console.log(describeUserOld(user));
console.log(describeUser(user));
Reading the function signature alone tells you the function needs name and age from the argument. No need to scan the body to see what properties are accessed.
Combined with defaults, this produces self-documenting function signatures:
function createUser({
name,
age = 0,
role = "member",
isActive = true
} = {}) {
return { name, age, role, isActive };
}
console.log(createUser({ name: "Olena" }));
console.log(createUser({ name: "Nikhil", role: "admin" }));
console.log(createUser()); // works even with no argument, thanks to = {}
The = {} at the end of the parameter list is the default for the whole argument object — without it, calling createUser() would try to destructure undefined and throw.
Destructuring in for...of Loops
Object.entries returns an array of [key, value] pairs. Destructuring lets you unpack them cleanly in iteration:
const scores = { alice: 85, bob: 72, carol: 91 };
for (const [name, score] of Object.entries(scores)) {
console.log(`${name}: ${score}`);
}
// Same pattern with arrays of arrays
const pairs = [["a", 1], ["b", 2], ["c", 3]];
for (const [key, value] of pairs) {
console.log(`${key} = ${value}`);
}
This is the standard idiom for iterating objects in modern JavaScript. We saw it in Topic 7.4. The destructuring [name, score] extracts each pair into named variables, making the loop body read naturally.
Destructuring Function Return Values
When a function returns an array or object, destructure the result directly:
function getDimensions() {
return { width: 1920, height: 1080 };
}
const { width, height } = getDimensions();
console.log(width, height);
// Multiple return values via array
function getMinMax(numbers) {
return [Math.min(...numbers), Math.max(...numbers)];
}
const [min, max] = getMinMax([3, 1, 4, 1, 5, 9, 2, 6]);
console.log(min, max);
Array destructuring on returns is a common pattern when you want to return multiple values from a function. React hooks use this pattern heavily — const [value, setValue] = useState(initial) returns a two-element array that gets destructured at the call site.
Interactive animation: Swap, entries, unpack - the swap idiom, an Object.entries loop and return-value unpacking, with their output.
Patterns That Look Confusing
Some destructuring patterns are concise but unusual. Worth recognizing in real code:
// Conditional destructure with default chain
function processConfig({ host = "localhost", port = 8080 } = {}) {
return `${host}:${port}`;
}
console.log(processConfig()); // localhost:8080
console.log(processConfig({ port: 3000 })); // localhost:3000
console.log(processConfig({ host: "api.test.com" })); // api.test.com:8080
// Rename + default + nested in one signature
function setupUser({ name = "Anonymous", profile: { theme = "light" } = {} }) {
return `${name} prefers ${theme}`;
}
console.log(setupUser({ name: "Olena", profile: { theme: "dark" } }));
console.log(setupUser({ name: "Nikhil" }));
console.log(setupUser({}));
The second example is dense. Function parameter is destructured. The nested profile is destructured into theme. The nested object has a default = {} so calling without profile doesn't crash. The whole signature describes the function's contract in one place.
When you write code like this, comment it or extract pieces to make the intent clear. Power features can become illegibility traps if overused.
graph TD
A[Destructuring] --> B[Object destructuring<br/>extract by name]
A --> C[Array destructuring<br/>extract by position]
B --> D[Renaming with colon]
B --> E[Defaults with equals]
B --> F[Nested for deeper access]
C --> G[Skip with commas]
C --> H[Swap variables]
C --> I[Common in returns and hooks]
style A fill:#7c3aed,color:#fff
This diagram shows the two forms of destructuring. Object destructuring extracts properties by name and supports renaming with a colon, defaults with equals, and nested patterns for deeper access. Array destructuring extracts values by position and is commonly used for skipping elements with commas, swapping variables, and consuming function return values that include multiple values like in React hooks.
Common Pitfalls
Interactive animation: Null sources, null values - destructuring null throws, and a default never replaces a null value.
Warning: The destructuring traps.
Destructuring
undefined. Trying to destructureundefinedornullthrows a TypeError. Use a default like= {}or= []to allow missing input.Confusing nested destructuring with creating variables.
{ address: { city } } = userextractscity, notaddress. The nested destructure is a path, not a variable declaration.Overly dense destructuring. Three levels of nested destructuring with renames and defaults can become unreadable. Break dense patterns into multiple lines for clarity.
Order matters in arrays. Array destructuring is by position.
const [a, b] = objdoesn't work on an object.const [a, b] = "ab"works because strings are iterable.
Why This Matters in Interviews
Interview: Destructuring is expected fluency in modern JavaScript interviews. Code that uses verbose property access (
user.name,user.age,user.roleinstead of destructuring) signals stale style. Reaching for destructuring naturally — in function parameters, in variable declarations, infor...ofloops — signals modern fluency.Topic 10.7 — Interview Lens: Modern JavaScript Patterns — covers the deeper interview-prep treatment.
Try It
Exercise: Refactor this function to use destructuring as aggressively as it sensibly can:
function makeGreeting(user, options) { const name = user.name; const role = user.role; const formal = options ? options.formal : false; const greeting = options && options.greeting ? options.greeting : "Hello"; if (formal) { return greeting + ", " + role + " " + name; } return greeting + ", " + name; }The refactored version should: destructure
userandoptionsin the function signature, provide defaults for missing options, and read top-to-bottom without conditional property access. Compare your version to the original — count the lines and the readability.