Home Module 13 Destructuring

1. Introduction

Destructuring is a syntax that lets you unpack values from arrays or properties from objects into individual variables — in a single concise statement. You have already seen it used with objects in Lesson 3 of Module 12. This lesson covers the full depth: array destructuring, object destructuring, defaults, renaming, nesting, rest patterns, and function parameter destructuring.

2. Theory

2.1 Array destructuring

const colours = ['red', 'green', 'blue'];

// Old way
const first  = colours[0];
const second = colours[1];

// Destructuring
const [first2, second2] = colours;
console.log(first2);  // 'red'
console.log(second2); // 'green'

// Skip elements with empty commas
const [, , third] = colours;
console.log(third); // 'blue'

// With rest — collect remaining into a new array
const [head, ...tail] = colours;
console.log(head); // 'red'
console.log(tail); // ['green', 'blue']

// Default values
const [a = 1, b = 2, c = 3, d = 99] = [10, 20];
console.log(a); // 10
console.log(b); // 20
console.log(c); // 3  — undefined in array, default used
console.log(d); // 99 — no value at index 3, default used

2.2 Swap variables — classic trick

let x = 1;
let y = 2;

// Without destructuring — needs temp variable
let temp = x; x = y; y = temp;

// With destructuring — no temp needed
[x, y] = [y, x];
console.log(x); // 2
console.log(y); // 1

2.3 Object destructuring

const user = { name: 'Alice', age: 28, email: 'alice@example.com', role: 'admin' };

// Basic
const { name, age } = user;
console.log(name); // 'Alice'
console.log(age);  // 28

// Rename while destructuring
const { name: userName, role: userRole } = user;
console.log(userName); // 'Alice'
console.log(userRole); // 'admin'

// Default values
const { theme = 'light', lang = 'en' } = user;
console.log(theme); // 'light' — user.theme is undefined
console.log(lang);  // 'en'

// Rename AND default
const { score: points = 0 } = user;
console.log(points); // 0 — user.score is undefined, renamed to points

// Rest pattern
const { name: n, ...rest } = user;
console.log(n);    // 'Alice'
console.log(rest); // { age: 28, email: '...', role: 'admin' }

2.4 Nested destructuring

const order = {
  id: 101,
  customer: {
    name: 'Bob',
    address: {
      city: 'London',
      zip:  'SW1A 1AA'
    }
  },
  items: ['Shirt', 'Jeans']
};

// Nested object destructuring
const { id, customer: { name: custName, address: { city } } } = order;
console.log(id);       // 101
console.log(custName); // 'Bob'
console.log(city);     // 'London'

// Mix array and object destructuring
const { items: [firstItem] } = order;
console.log(firstItem); // 'Shirt'

2.5 Destructuring in function parameters

// Without destructuring — verbose
function displayUser(user) {
  console.log(`${user.name} (${user.role})`);
}

// With destructuring in parameters — documents what you use
function displayUser({ name, role }) {
  console.log(`${name} (${role})`);
}

displayUser({ name: 'Alice', role: 'admin', age: 28 }); // 'Alice (admin)'

// With defaults in parameters
function connect({ host = 'localhost', port = 3000, secure = false } = {}) {
  console.log(`${secure ? 'https' : 'http'}://${host}:${port}`);
}
connect({ host: 'example.com', port: 443, secure: true }); // 'https://example.com:443'
connect(); // 'http://localhost:3000' — entire argument defaults to {}

// Array parameter destructuring
function first([head]) { return head; }
console.log(first([10, 20, 30])); // 10

2.6 Destructuring function return values

// Return multiple values as array
function minMax(arr) {
  return [Math.min(...arr), Math.max(...arr)];
}
const [min, max] = minMax([3, 1, 8, 2, 9, 4]);
console.log(min, max); // 1 9

// Return multiple values as object (self-documenting)
function parseDate(str) {
  const [year, month, day] = str.split('-').map(Number);
  return { year, month, day };
}
const { year, month } = parseDate('2026-06-01');
console.log(year, month); // 2026 6

// Object return is usually better — callers pick what they need
const { day } = parseDate('2026-06-01');
console.log(day); // 1

2.7 for...of with destructuring

const users = [
  { name: 'Alice', score: 95 },
  { name: 'Bob',   score: 87 },
  { name: 'Carol', score: 92 }
];

// Destructure each object in the loop
for (const { name, score } of users) {
  console.log(`${name}: ${score}`);
}

// Object.entries with destructuring
const settings = { theme: 'dark', lang: 'en', fontSize: 16 };
for (const [key, value] of Object.entries(settings)) {
  console.log(`${key} = ${value}`);
}

3. Real World Example

// API response handling with destructuring
async function fetchUser(id) {
  const response = await fetch(`/api/users/${id}`);
  const {
    name,
    email,
    role = 'user',
    profile: { avatar = '/default.png', bio = '' } = {},
    permissions = []
  } = await response.json();

  return {
    displayName: name,
    email,
    role,
    avatar,
    bio,
    canEdit:   permissions.includes('edit'),
    canDelete: permissions.includes('delete')
  };
}

// Using the result
fetchUser(1).then(({ displayName, role, canEdit }) => {
  console.log(`${displayName} is a ${role} — can edit: ${canEdit}`);
});

// Array destructuring from fetch
async function fetchTopProducts() {
  const data = await fetch('/api/products?sort=sales&limit=3').then(r => r.json());
  const [best, second, third] = data.products;
  return { best, second, third };
}

4. Code Example

<script>
  // Simulate an API response
  const apiResponse = {
    status: 'success',
    data: {
      user: {
        id:    42,
        name:  'Alice Johnson',
        email: 'alice@example.com',
        preferences: {
          theme:    'dark',
          language: 'en'
        }
      },
      posts: [
        { id: 1, title: 'Hello World', likes: 10 },
        { id: 2, title: 'ES6 Tips',    likes: 25 },
        { id: 3, title: 'CSS Grid',    likes: 18 }
      ]
    }
  };

  // Destructure everything we need in one go
  const {
    status,
    data: {
      user: { name, email, preferences: { theme } },
      posts: [topPost, ...otherPosts]
    }
  } = apiResponse;

  console.log(status);   // 'success'
  console.log(name);     // 'Alice Johnson'
  console.log(email);    // 'alice@example.com'
  console.log(theme);    // 'dark'
  console.log(topPost.title);  // 'Hello World'
  console.log(otherPosts.length); // 2

  // Swap coordinates
  let [lat, lng] = [51.5074, -0.1278];
  console.log(`Before: ${lat}, ${lng}`); // 51.5074, -0.1278
  [lat, lng] = [lng, lat];
  console.log(`After:  ${lat}, ${lng}`); // -0.1278, 51.5074

  // Function with destructured params and defaults
  function renderCard({ title, subtitle = '', badge = null, highlighted = false }) {
    return `
      <div class="${highlighted ? 'card highlighted' : 'card'}">
        ${badge ? `<span class="badge">${badge}</span>` : ''}
        <h2>${title}</h2>
        ${subtitle ? `<p>${subtitle}</p>` : ''}
      </div>
    `;
  }

  console.log(renderCard({ title: 'Featured', badge: 'NEW', highlighted: true }));
</script>

5. Code Breakdown

Deep nested destructuring in one statement

The single destructuring of apiResponse reaches three levels deep — extracting status from the top, name/email/theme from inside data.user.preferences, and the first post from the posts array. This is more readable than writing six separate variable assignments.

Array destructuring with rest in the loop

const [topPost, ...otherPosts] separates the first element from the remaining elements using the rest pattern — the same syntax used in function parameters.

Default values in function parameters

The renderCard function uses object destructuring with defaults directly in the parameter. Callers only pass what they care about; missing fields automatically get safe defaults. This is far more readable than a long list of positional parameters.

6. Common Mistakes

Mistake 1 — Declaring variables after destructuring

// Bug — destructuring without const/let reassigns global or errors
{ name } = user; // SyntaxError (block statement, not destructuring)

// Fix — always include const/let/var
const { name } = user;

// For re-assignment after declaration, wrap in parens
let name;
({ name } = user); // parens required to avoid block interpretation

Mistake 2 — Destructuring null or undefined

const { name } = null; // TypeError: Cannot destructure property 'name' of null

// Fix — guard before destructuring
const user = getUser(); // may return null
const { name = 'Guest' } = user ?? {}; // safe fallback to empty object

Mistake 3 — Forgetting = {} default for function parameter

// Bug — calling with no argument throws
function init({ host, port }) { ... }
init(); // TypeError: Cannot destructure property 'host' of undefined

// Fix — default the entire parameter to {}
function init({ host = 'localhost', port = 3000 } = {}) { ... }
init(); // works — uses all defaults

Mistake 4 — Renaming confuses order in arrays

// Array destructuring uses POSITION, not name — you cannot rename meaningfully
const [x, y] = [10, 20];
// x = 10, y = 20 — position-based, not key-based like objects

7. Best Practices

  1. Destructure in function parameters to document which properties you use.
  2. Always default the whole parameter to {} when destructuring object parameters — prevents errors on empty calls.
  3. Use rest (...rest) to collect "everything else" without listing every property.
  4. Keep nested destructuring shallow — more than 2 levels deep becomes hard to read. Extract to intermediate variables instead.
  5. Use object return values over array returns when a function produces multiple named outputs — callers pick only what they need.
  6. Guard against null/undefined with ?? {} before destructuring data from external sources.

8. Practice Exercise

  1. Given const point = { x: 5, y: 10, z: 15 }, destructure all three into variables, rename z to depth, and give a default of 0 for a non-existent w property.
  2. Given a function that returns [min, max, average], destructure the result and log all three.
  3. Write a displayEvent({ type, target: { id, tagName }, timeStamp }) function that logs a formatted string. Call it with a mock event object.

9. Assignment

Build a "Data Transformer" page.

  1. Define a complex nested JavaScript object representing an e-commerce order (with customer, items array, shipping address, payment info).
  2. Write individual functions using destructuring to: extract customer details, calculate order total, format a shipping label, and summarise items.
  3. Use for...of with destructuring to iterate items and display them in a table.
  4. Display all extracted data in the DOM without ever using dot notation after the destructuring line.

Deliverable: One HTML file.

10. Interview Questions

  1. What is destructuring?
    A JavaScript syntax that extracts values from arrays (by position) or properties from objects (by key) into named variables in a single statement — cleaner than writing multiple separate assignments.
  2. How do you rename a property while destructuring?
    Use a colon: const { originalKey: newName } = obj. The variable created is newName, not originalKey.
  3. What is the rest pattern in destructuring?
    Using ...rest after the last binding collects all remaining elements (array) or properties (object) into a new array or object. Example: const [first, ...rest] = [1,2,3] gives first=1 and rest=[2,3].
  4. How do you provide default values in destructuring?
    Use = in the binding: const { name = 'Guest' } = user. The default is used only when the value is undefined — not for null or other falsy values.

11. Additional Resources

  • MDN — Destructuring assignment
  • javascript.info — Destructuring assignment
  • MDN — Default parameters