Problem Solved

JavaScript · destructuring

Home › Courses › JavaScript › Problems › Destructuring
🟨 Concept · 3 min read

Destructuring.
Extract values, one pattern.

Destructuring is a syntax for pulling values out of objects and arrays into named variables. Instead of three lines of const name = user.name, one line: const {'{ name, age }'} = user. It works in variable declarations, function parameters, and assignments. This page covers object destructuring, array destructuring, defaults, renaming, nested patterns, and the gotchas that come up in real code.

Short answer: Object: const {'{ name, age }'} = user;. Array: const [first, second] = items;. Defaults: const {'{ name = "Anonymous" }'} = user;. Rename: const {'{ name: userName }'} = user;. Nested: const {'{ user: { name } }'} = response;. Function params: function greet({'{ name }'}) {'{...}'}. Defaults only apply to undefined, not null. Add = {'{}'} to function params to handle missing arguments.

✅ Object destructuring

Extract by property name.

object-destructuring.js
const user = {
name: 'Alice',
age: 30,
email: 'alice@example.com'
};

// Without destructuring
const name1 = user.name;
const age1 = user.age;
const email1 = user.email;

// With destructuring — same result
const { name, age, email } = user;
console.log(name, age, email);
// Alice 30 alice@example.com

// Rename
const { name: userName, age: userAge } = user;
console.log(userName, userAge);
// Alice 30

// Default
const { name: n, city = 'Unknown' } = user;
console.log(n, city);
// Alice Unknown

// Nested
const response = {
user: { name: 'Alice', email: 'a@b.com' }
};
const { user: { name: nestedName } } = response;
console.log(nestedName);
// Alice
Pattern syntax:

{ a } → extract property a into variable a
{ a: b } → extract property a into variable b
{ a = 1 } → extract a, default to 1 if undefined
{ a: b = 1 } → extract a into b, default to 1
{ a: { b } } → extract a, then extract b from it

The colon and the = solve different problems:
: rename the variable
= provide a default value

You can use them together:

const { name: userName = 'Anonymous' } = user;
→ extract user.name into userName
→ if user.name is undefined, use 'Anonymous'

⚠️ Default only applies to undefined.
If the value is null, the default does not trigger.

const { name = 'Anonymous' } = { name: null };
→ name is null (default not used)

const { name = 'Anonymous' } = { name: undefined };
→ name is 'Anonymous' (default used)

If you need to handle null too:
const name = user.name ?? 'Anonymous';

✅ Array destructuring

Extract by position.

array-destructuring.js
const nums = [1, 2, 3, 4, 5];

// Basic — extract by index
const [first, second, third] = nums;
console.log(first, second, third);
// 1 2 3

// Skip values with commas
const [, , thirdValue] = nums;
console.log(thirdValue);
// 3

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

// Defaults
const [a, b, c, d, e, f = 100] = nums;
console.log(f);
// 100

// Swap without temporary variable
let x = 1, y = 2;
[x, y] = [y, x];
console.log(x, y); // 2 1

// From a function return
function getCoords() {
return [10, 20];
}
const [lat, lng] = getCoords();
console.log(lat, lng); // 10 20
Array destructuring reads left to right:

const [a, b, c] = [1, 2, 3];
↑ ↑ ↑
index 0, 1, 2

Skipping values:
const [a, , c] = [1, 2, 3]; // a=1, c=3
The empty slot between commas skips index 1.

Rest:
const [first, ...rest] = [1, 2, 3, 4];
first = 1, rest = [2, 3, 4]
rest must be last.

Swapping:
[x, y] = [y, x]
This is the standard way to swap in JS
without a temporary variable.

Function returns:
function getCoords() { return [10, 20]; }
const [lat, lng] = getCoords();
Common for functions that return multiple values.

Order matters for arrays.
For objects, order doesn't matter — you match by name.
This is the key difference: arrays are positional,
objects are named.

⚠️ Rest must be last:
const [...rest, last] = [1,2,3]; // SyntaxError

⚠️ Defaults work the same as objects:
const [a = 1] = []; // a = 1
const [a = 1] = [null]; // a = null (not 1)

✅ Function parameters

The most-used form of destructuring.

param-destructuring.js
// ❌ Without destructuring
function greetUser(user) {
return Hello, ${user.name}!`;
}

// ✅ With destructuring — parameter list documents usage
function greet({ name, greeting = 'Hello' }) {
return ``${greeting}, ${name}!;
}

greet({ name: 'Alice' });
// "Hello, Alice!"

greet({ name: 'Bob', greeting: 'Hi' });
// "Hi, Bob!"

// ⚠️ Without a default, calling with no argument throws
function risky({ name }) { return name; }
// risky(); // TypeError: Cannot destructure property 'name'

// ✅ With a default — handles missing argument
function safe({ name = 'Guest' } = {}) {
return Hello,${name}`;
}
safe();
// "Hello, Guest"

safe({ name: 'Alice' });
// "Hello, Alice"
Two different defaults:

function fn({ name = 'Guest' } = {}) { ... }
↑ ↑
| |
| default for the whole object
| (used if fn() called with no argument)
|
default for the property
(used if { name: undefined })

Both are needed:
• { name = 'Guest' } handles missing name property
• = {} handles missing object argument

Without = {} :
fn() → TypeError: Cannot destructure property 'name'
Because the parameter is undefined, and you can't
destructure undefined.

With = {} :
fn() → 'Guest'
The parameter becomes {}, then name is destructured
from {} → undefined → default 'Guest'.

This is the standard pattern for functions with
an options object:

function createUser({ name, email, role = 'user' } = {}) {
return { name, email, role };
}

createUser({ name: 'Alice', email: 'a@b.com' });
// { name: 'Alice', email: 'a@b.com', role: 'user' }

createUser();
// { name: undefined, email: undefined, role: 'user' }

The pattern documents itself: it says "I accept an
object with these fields, all optional, with these
defaults." That's more informative than
function createUser(options) { options.name ... }.

Don't destructure without a default when the argument can be missing. function greet({ name }) {'{...}'} throws if called as greet() — the parameter is undefined, and you can't destructure undefined. The fix is the = {'{}'} default: function greet({ name } = {'{}'}) {'{...}'}. This is the single most common destructuring bug in function parameters. It shows up in code review all the time because it only fails when someone calls the function without an argument — which doesn't happen in the developer's own tests, but does happen in production.

⚠️ Common mistakes

The destructuring mistakes that cost real time.

1. Function parameter without = {'{}'}

Throws if called with no argument. Fix: add = {'{}'}.

2. Defaults don't apply to null

{'{ name = "x" }'} with {'{ name: null }'} gives null. Fix: ?? after destructuring.

3. Nested destructuring without defaults

{'{ user: { name } }'} throws if user is undefined. Fix: {'{ user: { name } = {} }'}.

4. Destructuring null or undefined

TypeError. Fix: const {'{ x }'} = obj ?? {'{}'};.

5. Rest not last in destructuring

[...rest, last] — SyntaxError. Fix: rest must be last.

6. Assuming destructuring deep-copies

It extracts references. Fix: spread to copy: {'{ ...user }'}.

7. Nested destructuring for deep data

Gets unreadable. Fix: destructure one level at a time.

8. Destructuring in a loop without thinking

for (const {'{ id, name }'} of users) — clear and fine. But watch for null items.

❓ Frequently asked

What is destructuring in JavaScript?

Destructuring is a syntax for extracting values from objects and arrays into variables. Instead of const name = user.name; const age = user.age;, you write const { name, age } = user;. For arrays, const [first, second] = [1, 2];. Destructuring works in variable declarations, function parameters, and assignment expressions.

How do I set default values in destructuring?

Add = defaultValue after the variable name: const { name = 'Anonymous' } = user;. If user.name is undefined, name becomes 'Anonymous'. Defaults only apply when the value is undefined — not when it's null, 0, '', or false. This is a common gotcha.

How do I rename a variable while destructuring?

Use the syntax { originalName: newName }. Example: const { name: userName } = user; — extracts user.name into a variable called userName. You can combine renaming with defaults: const { name: userName = 'Anonymous' } = user;.

Can I destructure nested objects?

Yes: const { user: { name, email } } = response;. This extracts response.user.name and response.user.email. If user might be undefined, the destructuring throws — use defaults or optional chaining before destructuring. The pattern gets verbose for deep nesting.

Does destructuring copy the value or reference it?

It extracts the reference — the same as any other assignment. If you destructure an object, the variable holds a reference to the same object. To get a copy, combine destructuring with spread: const { user } = response; const copy = { ...user };. Destructuring does not deep-copy.

Can I destructure in function parameters?

Yes, and this is one of the most common uses. function greet({ name, age }) { ... } accepts an object and destructures it directly in the parameter list. Combine with defaults: function greet({ name = 'Guest' } = {}) { ... }. The = {} default is important — without it, calling greet() with no argument throws.

🔗 Continue learning

🗺️ JavaScript learning path
The full roadmap from variables to async — every concept in order

Next in this series:

📝 Template literals
Backticks, interpolation, tagged templates, and multi-line strings
← All JavaScript problems
Problem solved · JavaScript