JavaScript Destructuring
Extracting Data Elegantly
Explanation
What is Destructuring?
Destructuring is a syntax for extracting values from arrays and properties from objects into distinct variables. It makes code cleaner and more readable.
Key Concepts
- Array Destructuring: Extract by position
- Object Destructuring: Extract by property name
- Default Values: Fallbacks for missing values
- Rest Pattern: Collect remaining items
- Nested Destructuring: Extract from nested structures
Demonstration
Example 1: Array Destructuring
// Basic array destructuring
const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;
console.log(first); // 'red'
console.log(second); // 'green'
console.log(third); // 'blue'
// Skip elements
const [primary, , tertiary] = colors;
console.log(primary); // 'red'
console.log(tertiary); // 'blue'
// Default values
const [a, b, c, d = 'yellow'] = colors;
console.log(d); // 'yellow'
// Rest pattern
const numbers = [1, 2, 3, 4, 5];
const [head, ...tail] = numbers;
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]
// Swapping variables
let x = 1;
let y = 2;
[x, y] = [y, x];
console.log(x, y); // 2, 1
// From function returns
function getCoordinates() {
return [10, 20];
}
const [coordX, coordY] = getCoordinates();
console.log(coordX, coordY); // 10, 20
Example 2: Object Destructuring
// Basic object destructuring
const user = {
name: 'Arthur',
email: 'art@bpc.com',
role: 'admin'
};
const { name, email, role } = user;
console.log(name); // 'Arthur'
console.log(email); // 'art@bpc.com'
// Rename variables
const { name: userName, email: userEmail } = user;
console.log(userName); // 'Arthur'
console.log(userEmail); // 'art@bpc.com'
// Default values
const { name: n, age = 30 } = user;
console.log(n); // 'Arthur'
console.log(age); // 30
// Rename with default
const { status: userStatus = 'active' } = user;
console.log(userStatus); // 'active'
// Rest pattern
const { name: extractedName, ...rest } = user;
console.log(extractedName); // 'Arthur'
console.log(rest); // { email: 'art@bpc.com', role: 'admin' }
// Nested destructuring
const company = {
name: 'Big Poppa Code',
address: {
city: 'New York',
country: 'USA'
}
};
const { address: { city, country } } = company;
console.log(city); // 'New York'
console.log(country); // 'USA'
Example 3: Function Parameters
// Destructuring in function parameters
function createUser({ name, email, role = 'user' }) {
return { id: Date.now(), name, email, role };
}
const newUser = createUser({
name: 'Sarah',
email: 'sarah@example.com'
});
console.log(newUser);
// Array destructuring in parameters
function sum([a, b, c = 0]) {
return a + b + c;
}
console.log(sum([1, 2])); // 3
console.log(sum([1, 2, 3])); // 6
// Combined with rest
function logFirst(first, ...rest) {
console.log('First:', first);
console.log('Rest:', rest);
}
logFirst(1, 2, 3, 4);
// First: 1
// Rest: [2, 3, 4]
// Real-world: React component props
function UserCard({ user, onDelete, className = '' }) {
return (
<div className={`user-card ${className}`}>
<h2>{user.name}</h2>
<button onClick={() => onDelete(user.id)}>Delete</button>
</div>
);
}
// Express route handler
app.get('/users/:id', ({ params: { id }, query }, res) => {
res.json({ userId: id, query });
});
Example 4: Complex Patterns
// Deeply nested destructuring
const response = {
data: {
user: {
id: 1,
profile: {
name: 'Arthur',
avatar: 'url'
}
}
},
meta: {
status: 200
}
};
const {
data: {
user: {
id,
profile: { name, avatar }
}
},
meta: { status }
} = response;
console.log(id, name, status); // 1, 'Arthur', 200
// Mixed array and object
const apiResponse = {
results: [
{ id: 1, title: 'Post 1' },
{ id: 2, title: 'Post 2' }
],
total: 2
};
const { results: [firstPost, secondPost], total } = apiResponse;
console.log(firstPost.title); // 'Post 1'
// Dynamic key destructuring
const key = 'name';
const obj = { name: 'Arthur', age: 30 };
const { [key]: value } = obj;
console.log(value); // 'Arthur'
// Default function for missing property
const getUser = (user) => {
const {
name,
greet = () => `Hello, ${name}`
} = user;
return greet();
};
console.log(getUser({ name: 'Arthur' })); // Hello, Arthur
Example 5: Practical Use Cases
// Import specific exports
import { useState, useEffect, useRef } from 'react';
// API response handling
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const { data: user, error } = await response.json();
if (error) throw new Error(error);
return user;
}
// Configuration with defaults
function setupServer({
port = 3000,
host = 'localhost',
ssl = false,
routes = []
} = {}) {
console.log(`Server: ${host}:${port}, SSL: ${ssl}`);
return { port, host, ssl, routes };
}
setupServer(); // Uses all defaults
setupServer({ port: 8080 }); // Override port
// Event handling
document.addEventListener('click', ({ target, clientX, clientY }) => {
console.log(`Clicked ${target.tagName} at ${clientX}, ${clientY}`);
});
// Array method callbacks
const users = [
{ name: 'Arthur', age: 30 },
{ name: 'Sarah', age: 25 }
];
const names = users.map(({ name }) => name);
const adults = users.filter(({ age }) => age >= 18);
const totalAge = users.reduce((sum, { age }) => sum + age, 0);
// State management
const [state, setState] = useState({ count: 0, loading: false });
const { count, loading } = state;
Key Takeaways:
- Destructuring extracts values from arrays/objects
- Use defaults for missing values
- Rest pattern collects remaining items
- Works great with function parameters
- Simplifies working with API responses
Imitation
Challenge 1: Parse API Response
Task: Extract specific data from a complex API response.
const response = {
status: 'success',
data: {
users: [
{ id: 1, name: 'Alice', posts: [{ title: 'Hello' }] },
{ id: 2, name: 'Bob', posts: [] }
],
pagination: {
page: 1,
total: 10
}
}
};
Solution
const {
status,
data: {
users: [
{ name: firstName, posts: [firstPost = {}] = [] },
{ name: secondName }
],
pagination: { page, total }
}
} = response;
console.log(status); // 'success'
console.log(firstName); // 'Alice'
console.log(firstPost.title); // 'Hello'
console.log(secondName); // 'Bob'
console.log(page, total); // 1, 10
// Or more safely with optional chaining
const { data } = response;
const firstUser = data?.users?.[0];
const firstUserFirstPost = firstUser?.posts?.[0]?.title ?? 'No posts';
Challenge 2: Create Config Parser
Task: Write a function that accepts configuration with deep defaults.
Solution
function createConfig({
app: {
name = 'MyApp',
version = '1.0.0'
} = {},
server: {
port = 3000,
host = 'localhost',
ssl: {
enabled = false,
cert = null,
key = null
} = {}
} = {},
database: {
host: dbHost = 'localhost',
port: dbPort = 5432,
name: dbName = 'app'
} = {},
features = []
} = {}) {
return {
app: { name, version },
server: { port, host, ssl: { enabled, cert, key } },
database: { host: dbHost, port: dbPort, name: dbName },
features
};
}
// All defaults
console.log(createConfig());
// Partial override
console.log(createConfig({
server: { port: 8080 },
database: { name: 'production' }
}));
Practice
Exercise 1: State Reducer
Difficulty: Intermediate
Create a reducer function that uses destructuring to handle different action types cleanly.
Exercise 2: Query Parser
Difficulty: Advanced
Build a URL query string parser that returns a destructurable object with typed values.
Summary
What you learned:
- Array and object destructuring
- Default values and renaming
- Rest pattern for collecting items
- Nested destructuring
- Function parameter destructuring
Next Steps:
- Read: Spread and Rest
- Practice: Refactor code to use destructuring
- Explore: Optional chaining with destructuring
