Learning Objectives

  • Extract values from arrays with array destructuring
  • Extract properties from objects with object destructuring
  • Use destructuring in function parameters
  • Apply default values and renaming
  • Handle nested destructuring patterns

What is Destructuring?

Destructuring is a JavaScript expression that unpacks values from arrays or properties from objects into distinct variables. It provides a cleaner, more readable way to extract data.

Array Destructuring

Basic Array Destructuring

// Without destructuring
const colors = ['red', 'green', 'blue'];
const first = colors[0];
const second = colors[1];

// With destructuring
const [first, second, third] = colors;
console.log(first);  // 'red'
console.log(second); // 'green'
console.log(third);  // 'blue'

Skipping Elements

const colors = ['red', 'green', 'blue', 'yellow'];

// Skip elements with commas
const [first, , third] = colors;
console.log(first); // 'red'
console.log(third); // 'blue'

// Get first and rest
const [primary, ...others] = colors;
console.log(primary); // 'red'
console.log(others);  // ['green', 'blue', 'yellow']

Default Values

const colors = ['red'];

// Without default
const [first, second] = colors;
console.log(second); // undefined

// With default
const [first, second = 'green'] = colors;
console.log(second); // 'green'

Swapping Variables

let a = 1;
let b = 2;

// Old way
let temp = a;
a = b;
b = temp;

// With destructuring
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1

Object Destructuring

Basic Object Destructuring

const user = {
    name: 'John',
    age: 30,
    email: 'john@example.com'
};

// Without destructuring
const name = user.name;
const age = user.age;

// With destructuring
const { name, age, email } = user;
console.log(name);  // 'John'
console.log(age);   // 30
console.log(email); // 'john@example.com'

Renaming Variables

const user = {
    name: 'John',
    age: 30
};

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

Default Values

const user = {
    name: 'John'
};

// With default values
const { name, age = 25, country = 'USA' } = user;
console.log(name);    // 'John'
console.log(age);     // 25 (default)
console.log(country); // 'USA' (default)

Combining Rename and Default

const user = {
    name: 'John'
};

const { name: userName, age: userAge = 25 } = user;
console.log(userName); // 'John'
console.log(userAge);  // 25

Nested Destructuring

Nested Objects

const user = {
    name: 'John',
    address: {
        street: '123 Main St',
        city: 'New York',
        coordinates: {
            lat: 40.7128,
            lng: -74.0060
        }
    }
};

// Destructure nested properties
const {
    name,
    address: {
        city,
        coordinates: { lat, lng }
    }
} = user;

console.log(name); // 'John'
console.log(city); // 'New York'
console.log(lat);  // 40.7128
console.log(lng);  // -74.0060

// Note: 'address' and 'coordinates' are not variables
// console.log(address); // ReferenceError

Nested Arrays

const data = [
    'John',
    ['red', 'green', 'blue'],
    { age: 30 }
];

const [name, [primaryColor, ...otherColors], { age }] = data;

console.log(name);         // 'John'
console.log(primaryColor); // 'red'
console.log(otherColors);  // ['green', 'blue']
console.log(age);          // 30

Function Parameter Destructuring

Object Parameters

// Without destructuring
function displayUser(user) {
    console.log(`${user.name} is ${user.age} years old`);
}

// With destructuring
function displayUser({ name, age }) {
    console.log(`${name} is ${age} years old`);
}

displayUser({ name: 'John', age: 30 });
// "John is 30 years old"

Default Parameter Values

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

console.log(createUser({ name: 'John' }));
// { name: 'John', age: 18, role: 'user' }

console.log(createUser({ name: 'Jane', age: 25, role: 'admin' }));
// { name: 'Jane', age: 25, role: 'admin' }

Handling Undefined Parameters

// This will error if called without argument
function greet({ name }) {
    console.log(`Hello, ${name}!`);
}

// greet(); // TypeError: Cannot destructure property 'name' of 'undefined'

// Solution: default to empty object
function greet({ name } = {}) {
    console.log(`Hello, ${name || 'Guest'}!`);
}

greet(); // "Hello, Guest!"

Rest Operator with Destructuring

Array Rest

const numbers = [1, 2, 3, 4, 5];

const [first, second, ...rest] = numbers;
console.log(first);  // 1
console.log(second); // 2
console.log(rest);   // [3, 4, 5]

Object Rest

const user = {
    name: 'John',
    age: 30,
    email: 'john@example.com',
    country: 'USA'
};

const { name, age, ...otherInfo } = user;
console.log(name);      // 'John'
console.log(age);       // 30
console.log(otherInfo); // { email: 'john@example.com', country: 'USA' }

Real-World Examples

API Response Handling

async function fetchUser(id) {
    const response = await fetch(`/api/users/${id}`);
    const { data: user, status, message } = await response.json();
    
    if (status === 'success') {
        const { name, email, profile: { avatar } } = user;
        return { name, email, avatar };
    }
    
    throw new Error(message);
}

React Component Props

// Destructure props in function component
function UserCard({ name, age, email, avatar = '/default-avatar.png' }) {
    return (
        <div className="user-card">
            <img src={avatar} alt={name} />
            <h2>{name}</h2>
            <p>Age: {age}</p>
            <p>Email: {email}</p>
        </div>
    );
}

Array Methods

const users = [
    { id: 1, name: 'John', age: 30 },
    { id: 2, name: 'Jane', age: 25 },
    { id: 3, name: 'Bob', age: 35 }
];

// Destructure in map
const names = users.map(({ name }) => name);
console.log(names); // ['John', 'Jane', 'Bob']

// Destructure in filter
const adults = users.filter(({ age }) => age >= 30);
console.log(adults); // [{ id: 1, ... }, { id: 3, ... }]

// Destructure in forEach
users.forEach(({ name, age }) => {
    console.log(`${name} is ${age} years old`);
});

Configuration Objects

function initializeApp({
    apiUrl = 'https://api.example.com',
    timeout = 5000,
    retries = 3,
    headers = {},
    debug = false
} = {}) {
    console.log('Initializing with config:');
    console.log({ apiUrl, timeout, retries, headers, debug });
    
    // Initialize app with config
}

// Use with custom config
initializeApp({
    apiUrl: 'https://custom-api.com',
    debug: true
});

// Use with defaults
initializeApp();

Common Pitfalls

Pitfall 1: Destructuring undefined/null
const user = null;
// const { name } = user; // TypeError!

// Solution: use optional chaining or default
const { name } = user || {};
const { name } = user ?? {};
Pitfall 2: Variable already declared
let name = 'Alice';
// let { name } = user; // SyntaxError: Identifier 'name' has already been declared

// Solution: use different name or reassign
let { name: userName } = user;
// or
({ name } = user); // Note the parentheses!
Pitfall 3: Rest must be last
// Wrong
// const [...rest, last] = array; // SyntaxError

// Right
const [first, ...rest] = array;

Best Practices

1. Use destructuring for cleaner code
// Instead of
function getFullName(user) {
    return user.firstName + ' ' + user.lastName;
}

// Use
function getFullName({ firstName, lastName }) {
    return `${firstName} ${lastName}`;
}
2. Provide defaults for optional properties
function createConfig({ port = 3000, host = 'localhost' } = {}) {
    return { port, host };
}
3. Use meaningful variable names when renaming
// Good
const { name: userName, id: userId } = user;

// Avoid
const { name: n, id: i } = user;

Key Takeaways