📦 Destructuring

ES6 Unpacking Values from Arrays and Objects

What is Destructuring?

Destructuring allows you to extract values from arrays or properties from objects into distinct variables using a concise syntax.

📋 Array Destructuring

Basic Array Destructuring

// Traditional way
let colors = ['red', 'green', 'blue'];
let first = colors[0];
let second = colors[1];
let third = colors[2];

// Destructuring
let [first, second, third] = ['red', 'green', 'blue'];
console.log(first);   // 'red'
console.log(second);  // 'green'
console.log(third);   // 'blue'

// Skip elements
let [first, , third] = ['red', 'green', 'blue'];
console.log(first);  // 'red'
console.log(third);  // 'blue'

// Get first elements
let [x, y] = [1, 2, 3, 4, 5];
console.log(x);  // 1
console.log(y);  // 2

// More elements than variables
let [a, b] = [1, 2, 3];
console.log(a);  // 1
console.log(b);  // 2
// Third element is ignored

Default Values

// If value is undefined, use default
let [a = 1, b = 2] = [10];
console.log(a);  // 10 (from array)
console.log(b);  // 2 (default)

let [x = 5, y = 10] = [undefined, 20];
console.log(x);  // 5 (default, because undefined)
console.log(y);  // 20 (from array)

// null doesn't trigger default
let [m = 1] = [null];
console.log(m);  // null (not default)

// Complex defaults
let [name = 'Guest', age = 18] = ['John'];
console.log(name);  // 'John'
console.log(age);   // 18

// Default can be expression
let [first = getDefaultValue()] = [];
function getDefaultValue() {
    console.log('Called');
    return 'default';
}

Rest Pattern

// Collect remaining elements
let [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first);  // 1
console.log(rest);   // [2, 3, 4, 5]

// Rest must be last
let [a, b, ...others] = [1, 2, 3, 4];
console.log(others);  // [3, 4]

// Rest with no remaining elements
let [x, y, ...remaining] = [1, 2];
console.log(remaining);  // []

// Practical: split array
let [head, ...tail] = [1, 2, 3, 4];
console.log(head);  // 1
console.log(tail);  // [2, 3, 4]

Swapping Variables

// Old way (with temp variable)
let a = 1, b = 2;
let temp = a;
a = b;
b = temp;

// Destructuring way
let a = 1, b = 2;
[a, b] = [b, a];
console.log(a);  // 2
console.log(b);  // 1

// Swap multiple
let x = 1, y = 2, z = 3;
[x, y, z] = [z, x, y];
console.log(x, y, z);  // 3, 1, 2

Nested Arrays

// Destructure nested arrays
let nested = [1, [2, 3], 4];
let [a, [b, c], d] = nested;
console.log(a);  // 1
console.log(b);  // 2
console.log(c);  // 3
console.log(d);  // 4

// Deep nesting
let deep = [1, [2, [3, 4]]];
let [x, [y, [z, w]]] = deep;
console.log(z);  // 3

// Mixed with skip
let [first, [, second]] = [1, [2, 3]];
console.log(first);   // 1
console.log(second);  // 3

🗃️ Object Destructuring

Basic Object Destructuring

// Traditional way
let user = { name: 'John', age: 30 };
let name = user.name;
let age = user.age;

// Destructuring (variable names match property names)
let { name, age } = { name: 'John', age: 30 };
console.log(name);  // 'John'
console.log(age);   // 30

// Order doesn't matter
let { age, name } = { name: 'John', age: 30 };
console.log(name);  // 'John'

// Select specific properties
let { name } = { name: 'John', age: 30, city: 'NYC' };
console.log(name);  // 'John'

// Non-existent property
let { job } = { name: 'John' };
console.log(job);  // undefined

Renaming Variables

// Syntax: { propertyName: newVariableName }
let { name: userName, age: userAge } = { name: 'John', age: 30 };
console.log(userName);  // 'John'
console.log(userAge);   // 30
// console.log(name);   // ReferenceError

// Useful for conflicting names
let name = 'Global';
let { name: personName } = { name: 'John' };
console.log(name);        // 'Global'
console.log(personName);  // 'John'

// Multiple renames
let { firstName: first, lastName: last } = {
    firstName: 'John',
    lastName: 'Doe'
};
console.log(first);  // 'John'
console.log(last);   // 'Doe'

Default Values

// If property is undefined, use default
let { name = 'Guest', age = 18 } = { name: 'John' };
console.log(name);  // 'John' (from object)
console.log(age);   // 18 (default)

// Rename with default
let { name: userName = 'Guest', age: userAge = 18 } = {};
console.log(userName);  // 'Guest'
console.log(userAge);   // 18

// null doesn't trigger default
let { value = 10 } = { value: null };
console.log(value);  // null (not default)

// Complex defaults
let { x = getDefault() } = {};

// Practical: config with defaults
let config = { timeout: 5000 };
let {
    timeout = 3000,
    retries = 3,
    debug = false
} = config;
console.log(timeout);  // 5000
console.log(retries);  // 3 (default)
console.log(debug);    // false (default)

Rest Properties

// Collect remaining properties (ES2018)
let { name, ...rest } = {
    name: 'John',
    age: 30,
    city: 'NYC',
    job: 'Developer'
};
console.log(name);  // 'John'
console.log(rest);  // { age: 30, city: 'NYC', job: 'Developer' }

// Extract some, keep others
let { id, createdAt, ...userData } = {
    id: 1,
    name: 'John',
    age: 30,
    createdAt: '2025-01-01'
};
console.log(userData);  // { name: 'John', age: 30 }

// Rest must be last
let { a, b, ...others } = { a: 1, b: 2, c: 3, d: 4 };
console.log(others);  // { c: 3, d: 4 }

Nested Objects

// Destructure nested objects
let user = {
    name: 'John',
    address: {
        city: 'NYC',
        country: 'USA'
    }
};

let { name, address: { city, country } } = user;
console.log(name);     // 'John'
console.log(city);     // 'NYC'
console.log(country);  // 'USA'
// console.log(address);  // ReferenceError (not extracted)

// Get both parent and children
let {
    address,
    address: { city }
} = user;
console.log(address);  // { city: 'NYC', country: 'USA' }
console.log(city);     // 'NYC'

// Deep nesting
let data = {
    user: {
        profile: {
            name: 'John',
            age: 30
        }
    }
};

let { user: { profile: { name, age } } } = data;
console.log(name);  // 'John'
console.log(age);   // 30

// With defaults
let { address: { zip = '00000' } = {} } = {};
console.log(zip);  // '00000'

🔧 Function Parameters

Destructuring Parameters

// Traditional
function printUser(user) {
    console.log(user.name);
    console.log(user.age);
}

// Destructuring in parameters
function printUser({ name, age }) {
    console.log(name);
    console.log(age);
}

printUser({ name: 'John', age: 30 });

// Array destructuring in parameters
function sum([a, b]) {
    return a + b;
}

console.log(sum([5, 10]));  // 15

// With defaults
function greet({ name = 'Guest', greeting = 'Hello' }) {
    console.log(`${greeting}, ${name}!`);
}

greet({ name: 'John' });          // "Hello, John!"
greet({ greeting: 'Hi' });        // "Hi, Guest!"
greet({});                        // "Hello, Guest!"

// Default for entire parameter
function configure(options = {}) {
    let { timeout = 3000, debug = false } = options;
    console.log(timeout, debug);
}

configure();                    // 3000, false
configure({ timeout: 5000 });   // 5000, false

Named Parameters Pattern

// Without destructuring (hard to remember order)
function createUser(name, age, email, role, active) {
    // ...
}
createUser('John', 30, 'john@example.com', 'admin', true);

// With destructuring (self-documenting)
function createUser({ name, age, email, role = 'user', active = true }) {
    console.log(`Creating ${role}: ${name}`);
    return { name, age, email, role, active };
}

createUser({
    name: 'John',
    email: 'john@example.com',
    age: 30
});

// Partial options
function fetchData({ url, method = 'GET', headers = {}, body }) {
    console.log(`${method} ${url}`);
    // Make request
}

fetchData({
    url: '/api/users',
    method: 'POST',
    body: { name: 'John' }
});

Return Value Destructuring

// Return multiple values as array
function getCoordinates() {
    return [10, 20];
}

let [x, y] = getCoordinates();
console.log(x, y);  // 10, 20

// Return multiple values as object
function getUser() {
    return {
        name: 'John',
        age: 30,
        email: 'john@example.com'
    };
}

let { name, age } = getUser();
console.log(name, age);  // 'John', 30

// Practical: API response
async function fetchUser(id) {
    let response = await fetch(`/api/users/${id}`);
    return response.json();
}

let { data, error } = await fetchUser(1);

// Error handling
function divide(a, b) {
    if (b === 0) {
        return { success: false, error: 'Division by zero' };
    }
    return { success: true, result: a / b };
}

let { success, result, error } = divide(10, 2);
if (success) {
    console.log('Result:', result);
} else {
    console.error('Error:', error);
}

💡 Practical Examples

API Response Handling

// Extract data from API response
async function loadUser(userId) {
    let response = await fetch(`/api/users/${userId}`);
    let {
        data: { name, email, profile: { avatar, bio } },
        meta: { timestamp }
    } = await response.json();
    
    console.log(`${name} (${email})`);
    console.log(`Bio: ${bio}`);
    console.log(`Loaded at: ${timestamp}`);
}

// Multiple API calls
async function loadDashboard() {
    let [
        { data: users },
        { data: posts },
        { data: stats }
    ] = await Promise.all([
        fetch('/api/users').then(r => r.json()),
        fetch('/api/posts').then(r => r.json()),
        fetch('/api/stats').then(r => r.json())
    ]);
    
    return { users, posts, stats };
}

React/Vue Component Props

// React component with destructured props
function UserCard({ name, age, email, avatar = '/default.png', onEdit }) {
    return `
        
${name}

${name}

Age: ${age}

Email: ${email}

`; } // Vue component const UserProfile = { props: ['user'], setup({ user }) { let { name, age, email } = user; return { name, age, email }; } }; // Nested props function Dashboard({ user: { name, role }, settings: { theme, language } }) { console.log(`${name} (${role})`); console.log(`Theme: ${theme}, Language: ${language}`); }

Array Manipulation

// Get first and last
let numbers = [1, 2, 3, 4, 5];
let [first, , , , last] = numbers;
console.log(first, last);  // 1, 5

// Split array
let [head, ...tail] = [1, 2, 3, 4];
console.log(head);  // 1
console.log(tail);  // [2, 3, 4]

// Matrix operations
let matrix = [[1, 2], [3, 4], [5, 6]];
let [[a, b], [c, d], [e, f]] = matrix;

// Iterate with destructuring
let users = [
    { id: 1, name: 'John' },
    { id: 2, name: 'Jane' }
];

for (let { id, name } of users) {
    console.log(`${id}: ${name}`);
}

// Map with destructuring
let formatted = users.map(({ id, name }) => `#${id} ${name}`);

Configuration Objects

// Server configuration
function startServer({
    port = 3000,
    host = 'localhost',
    ssl = false,
    database: {
        host: dbHost = 'localhost',
        port: dbPort = 5432,
        name: dbName = 'myapp'
    } = {}
} = {}) {
    console.log(`Server: ${host}:${port}`);
    console.log(`SSL: ${ssl}`);
    console.log(`Database: ${dbName} at ${dbHost}:${dbPort}`);
}

startServer({
    port: 8080,
    database: {
        name: 'production'
    }
});

// Feature flags
function initializeApp({
    features: {
        darkMode = false,
        analytics = true,
        betaFeatures = false
    } = {}
} = {}) {
    console.log('Features:', { darkMode, analytics, betaFeatures });
}

initializeApp({
    features: { darkMode: true }
});

Object Property Filtering

// Remove sensitive data
function sanitizeUser(user) {
    let { password, ssn, creditCard, ...safe } = user;
    return safe;
}

let user = {
    id: 1,
    name: 'John',
    email: 'john@example.com',
    password: 'secret',
    ssn: '123-45-6789'
};

let safeUser = sanitizeUser(user);
// { id: 1, name: 'John', email: 'john@example.com' }

// Pick specific fields
function pick(obj, ...keys) {
    return keys.reduce((acc, key) => {
        if (key in obj) acc[key] = obj[key];
        return acc;
    }, {});
}

let picked = pick(user, 'id', 'name', 'email');

// Rename keys
function renameKeys(obj, keyMap) {
    return Object.entries(obj).reduce((acc, [key, value]) => {
        let newKey = keyMap[key] || key;
        acc[newKey] = value;
        return acc;
    }, {});
}

let renamed = renameKeys(
    { firstName: 'John', lastName: 'Doe' },
    { firstName: 'first', lastName: 'last' }
);
// { first: 'John', last: 'Doe' }

Event Handlers

// Extract event properties
button.addEventListener('click', ({ target, clientX, clientY }) => {
    console.log('Clicked element:', target);
    console.log('Position:', clientX, clientY);
});

// Form handling
form.addEventListener('submit', (event) => {
    event.preventDefault();
    
    let formData = new FormData(event.target);
    let data = Object.fromEntries(formData);
    
    let { email, password, remember } = data;
    console.log('Login:', { email, password, remember });
});

// Keyboard shortcuts
document.addEventListener('keydown', ({ key, ctrlKey, shiftKey, altKey }) => {
    if (ctrlKey && key === 's') {
        console.log('Save');
    } else if (ctrlKey && shiftKey && key === 'P') {
        console.log('Command palette');
    }
});

🎯 Key Takeaways