What is destructuring and how does it work?

👨‍💻 Frontend Developer 🟠 May come up 🎚️ Medium
#JavaScript #JS Basics #Arrays #Objects

Quick Answer

Destructuring is an ES6 syntax that allows extracting values from arrays or properties from objects into separate variables. For arrays, the syntax [a, b] = array is used, for objects — {prop1, prop2} = object. Destructuring simplifies code, makes it more readable and allows easy work with complex data structures.

Key capabilities:

  • Arrays — extraction by position
  • Objects — extraction by property name
  • Default values — protection from undefined
  • Renaming — assigning new names to variables
  • Nested destructuring — working with complex structures

What is destructuring

Destructuring is a way of “unpacking” values from arrays or objects into separate variables. It’s syntactic sugar that makes code more concise and readable.

Before and after ES6

// Before ES6 — traditional way
const user = ['John', 'Smith', 25];
const firstName = user[0];
const lastName = user[1];
const age = user[2];
 
// ES6 — destructuring
const [firstName, lastName, age] = ['John', 'Smith', 25];
 
console.log(firstName); // 'John'
console.log(lastName); // 'Smith'
console.log(age); // 25

Array Destructuring

1. Basic Syntax

const colors = ['red', 'green', 'blue'];
const [first, second, third] = colors;
 
console.log(first); // 'red'
console.log(second); // 'green'
console.log(third); // 'blue'
 
// You can extract not all elements
const [primary] = colors;
console.log(primary); // 'red'
 
const [, , tertiary] = colors; // Skip first two
console.log(tertiary); // 'blue'

2. Default Values

const numbers = [1, 2];
const [a, b, c = 0] = numbers;
 
console.log(a); // 1
console.log(b); // 2
console.log(c); // 0 — default value
 
// Default values work only for undefined
const [x, y = 10] = [5, null];
console.log(x); // 5
console.log(y); // null (not 10!)
 
const [m, n = 10] = [5, undefined];
console.log(m); // 5
console.log(n); // 10

3. Swapping Variable Values

let a = 1;
let b = 2;
 
// Traditional way of swapping
let temp = a;
a = b;
b = temp;
 
// With destructuring
[a, b] = [b, a];
console.log(a); // 2
console.log(b); // 1
 
// Circular shift
let x = 1, y = 2, z = 3;
[x, y, z] = [z, x, y];
console.log(x, y, z); // 3 1 2

4. Rest Parameters

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]
 
// Rest must be last
const [head, ...tail] = numbers;
console.log(head); // 1
console.log(tail); // [2, 3, 4, 5]

5. Nested Arrays

const nested = [[1, 2], [3, 4], [5, 6]];
const [[a, b], [c, d]] = nested;
 
console.log(a); // 1
console.log(b); // 2
console.log(c); // 3
console.log(d); // 4
 
// Mixed destructuring
const matrix = [[1, 2, 3], [4, 5, 6]];
const [row1, [, middle]] = matrix;
console.log(row1); // [1, 2, 3]
console.log(middle); // 5

Object Destructuring

1. Basic Syntax

const user = {
  name: 'John',
  age: 25,
  city: 'New York'
};
 
const {name, age, city} = user;
console.log(name); // 'John'
console.log(age); // 25
console.log(city); // 'New York'
 
// Order doesn't matter
const {city: userCity, name: userName} = user;
console.log(userCity); // 'New York'
console.log(userName); // 'John'

2. Renaming Variables

const user = {
  name: 'Mary',
  age: 30,
  email: 'maria@example.com'
};
 
// Renaming during destructuring
const {name: fullName, age: years, email: contact} = user;
console.log(fullName); // 'Mary'
console.log(years); // 30
console.log(contact); // 'maria@example.com'
 
// Mixed usage
const {name, age: userAge} = user;
console.log(name); // 'Mary'
console.log(userAge); // 30

3. Default Values

const user = {
  name: 'Peter',
  age: 28
};
 
const {name, age, city = 'Not specified', country = 'USA'} = user;
console.log(name); // 'Peter'
console.log(age); // 28
console.log(city); // 'Not specified'
console.log(country); // 'USA'
 
// Combining with renaming
const {name: userName, profession = 'Not specified'} = user;
console.log(userName); // 'Peter'
console.log(profession); // 'Not specified'

4. Nested Objects

const user = {
  name: 'Anna',
  address: {
    street: 'Broadway',
    house: 10,
    city: 'New York'
  },
  contacts: {
    phone: '+1-123-456-78-90',
    email: 'anna@example.com'
  }
};
 
// Destructuring nested objects
const {
  name,
  address: {street, city},
  contacts: {email}
} = user;
 
console.log(name); // 'Anna'
console.log(street); // 'Broadway'
console.log(city); // 'New York'
console.log(email); // 'anna@example.com'
 
// With renaming and default values
const {
  address: {street: userStreet, country = 'USA'}
} = user;
console.log(userStreet); // 'Broadway'
console.log(country); // 'USA'

5. Rest Properties

const user = {
  id: 1,
  name: 'David',
  age: 35,
  email: 'david@example.com',
  phone: '+1-987-654-32-10'
};
 
const {id, name, ...otherInfo} = user;
console.log(id); // 1
console.log(name); // 'David'
console.log(otherInfo); // {age: 35, email: '...', phone: '...'}
 
// Excluding specific properties
const {id: userId, ...userWithoutId} = user;
console.log(userId); // 1
console.log(userWithoutId); // {name: 'David', age: 35, ...}

Practical Examples

1. Functions with Destructuring Parameters

// Destructuring in function parameters
function greetUser({name, age, city = 'Unknown'}) {
  return `Hello, ${name}! You are ${age} years old, living in ${city}.`;
}
 
const user = {name: 'Elena', age: 27, city: 'San Francisco'};
console.log(greetUser(user));
// 'Hello, Elena! You are 27 years old, living in San Francisco.'
 
// Array destructuring in parameters
function calculateDistance([x1, y1], [x2, y2]) {
  return Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);
}
 
const point1 = [0, 0];
const point2 = [3, 4];
console.log(calculateDistance(point1, point2)); // 5

2. Working with API Responses

// Typical API response
const apiResponse = {
  status: 'success',
  data: {
    users: [
      {id: 1, name: 'John', role: 'admin'},
      {id: 2, name: 'Mary', role: 'user'}
    ],
    total: 2,
    page: 1
  },
  meta: {
    timestamp: '2024-01-15T10:30:00Z',
    version: '1.0'
  }
};
 
// Extracting needed data
const {
  status,
  data: {users, total},
  meta: {timestamp}
} = apiResponse;
 
console.log(status); // 'success'
console.log(users); // array of users
console.log(total); // 2
console.log(timestamp); // '2024-01-15T10:30:00Z'
 
// Destructuring first user
const [{name: firstUserName, role: firstUserRole}] = users;
console.log(firstUserName); // 'John'
console.log(firstUserRole); // 'admin'

3. Working with Modules

// Importing specific functions
import {useState, useEffect, useCallback} from 'react';
 
// Destructuring exports
const {log, error, warn} = console;
log('Message'); // Same as console.log('Message')
 
// Destructuring function result
function getCoordinates() {
  return {x: 10, y: 20, z: 30};
}
 
const {x, y} = getCoordinates();
console.log(x, y); // 10 20

4. Array Processing

// Extracting first and last elements
const numbers = [1, 2, 3, 4, 5];
const [first, ...middle] = numbers;
const last = middle.pop();
console.log(first); // 1
console.log(last); // 5
 
// More elegant way
const [firstNum, , , , lastNum] = numbers;
console.log(firstNum, lastNum); // 1 5
 
// Grouping elements
const items = ['a', 'b', 'c', 'd', 'e', 'f'];
const [group1, group2, ...rest] = items;
console.log(group1); // 'a'
console.log(group2); // 'b'
console.log(rest); // ['c', 'd', 'e', 'f']

Advanced Techniques

1. Conditional Destructuring

function processUser(user) {
  // Check object existence
  if (!user) return null;
  
  const {name, email, profile = {}} = user;
  const {avatar, bio = 'Biography not specified'} = profile;
  
  return {
    displayName: name,
    contactEmail: email,
    userAvatar: avatar,
    userBio: bio
  };
}
 
// Safe destructuring with ?? operator
const user = null;
const {name = 'Guest'} = user ?? {};
console.log(name); // 'Guest'

2. Destructuring in Loops

const users = [
  {name: 'John', age: 25, city: 'New York'},
  {name: 'Mary', age: 30, city: 'San Francisco'},
  {name: 'Peter', age: 35, city: 'Chicago'}
];
 
// Destructuring in for...of
for (const {name, city} of users) {
  console.log(`${name} lives in ${city}`);
}
 
// Destructuring in array methods
const names = users.map(({name}) => name);
console.log(names); // ['John', 'Mary', 'Peter']
 
const adults = users.filter(({age}) => age >= 30);
console.log(adults); // users over 30

3. Dynamic Destructuring

const user = {
  firstName: 'Anna',
  lastName: 'Johnson',
  age: 28,
  email: 'anna@example.com'
};
 
// Dynamic property name
const prop = 'firstName';
const {[prop]: value} = user;
console.log(value); // 'Anna'
 
// Function to extract specific properties
function pick(obj, ...keys) {
  const result = {};
  for (const key of keys) {
    if (key in obj) {
      result[key] = obj[key];
    }
  }
  return result;
}
 
const userInfo = pick(user, 'firstName', 'email');
console.log(userInfo); // {firstName: 'Anna', email: 'anna@example.com'}

Comparison Table

TypeSyntaxExampleFeatures
Arrays[a, b] = array[x, y] = [1, 2]By position, order matters
Objects{a, b} = object{name, age} = userBy property name
Default values[a = 1] = array{name = 'Guest'} = userOnly for undefined
Renaming{a: newName}{name: userName} = userOnly for objects
Rest[a, ...rest]{id, ...other} = userCollects remaining elements
Nested[a, [b, c]]{user: {name}} = dataMulti-level structure

Best Practices

1. Code Readability

// ❌ Too complex destructuring
const {
  data: {
    users: [
      {
        profile: {
          contacts: {email: userEmail}
        }
      }
    ]
  }
} = complexApiResponse;
 
// ✅ Step-by-step destructuring
const {data} = complexApiResponse;
const {users} = data;
const [firstUser] = users;
const {profile} = firstUser;
const {contacts} = profile;
const {email: userEmail} = contacts;

2. Error Handling

// ❌ Unsafe destructuring
function getFullName(user) {
  const {firstName, lastName} = user; // Error if user = null
  return `${firstName} ${lastName}`;
}
 
// ✅ Safe destructuring
function getFullName(user = {}) {
  const {firstName = '', lastName = ''} = user;
  return `${firstName} ${lastName}`.trim() || 'Name not specified';
}
 
// ✅ With validation
function getFullName(user) {
  if (!user || typeof user !== 'object') {
    return 'Invalid data';
  }
  
  const {firstName = '', lastName = ''} = user;
  return `${firstName} ${lastName}`.trim() || 'Name not specified';
}

3. Performance

// ❌ Excessive destructuring in loops
for (let i = 0; i < users.length; i++) {
  const {name, email, profile: {avatar}} = users[i]; // Every iteration
  // processing...
}
 
// ✅ Optimized version
for (let i = 0; i < users.length; i++) {
  const user = users[i];
  const name = user.name;
  const email = user.email;
  const avatar = user.profile?.avatar; // Optional chaining
  // processing...
}
 
// ✅ Or use destructuring in array methods
const processedUsers = users.map(({name, email, profile}) => ({
  displayName: name,
  contactEmail: email,
  hasAvatar: Boolean(profile?.avatar)
}));

Modern Features

1. Optional Chaining with Destructuring

const user = {
  name: 'John',
  address: null
};
 
// ❌ Error when destructuring null
// const {street} = user.address; // TypeError
 
// ✅ Safe destructuring
const {street} = user.address ?? {};
console.log(street); // undefined
 
// ✅ With optional chaining
const street = user.address?.street;
console.log(street); // undefined

2. Destructuring with Private Fields

class User {
  #id;
  constructor(id, name) {
    this.#id = id;
    this.name = name;
  }
  
  getPublicData() {
    // Return only public data
    const {name} = this;
    return {name};
  }
}
 
const user = new User(1, 'Mary');
const {name} = user.getPublicData();
console.log(name); // 'Mary'

3. Destructuring with TypeScript

interface User {
  id: number;
  name: string;
  email?: string;
}
 
function processUser({id, name, email = 'not specified'}: User) {
  return `User ${name} (ID: ${id}, Email: ${email})`;
}
 
// Typed destructuring
const users: User[] = [
  {id: 1, name: 'John'},
  {id: 2, name: 'Mary', email: 'mary@example.com'}
];
 
const [{name: firstName}, {email: secondEmail}] = users;

Practice Tasks

Task 1: Swapping Values

Condition: Write a function that takes an array of three numbers and returns an array where the first and last elements are swapped.

function swapFirstLast(arr) {
  // Your code here
}
 
console.log(swapFirstLast([1, 2, 3])); // [3, 2, 1]
console.log(swapFirstLast([10, 20, 30])); // [30, 20, 10]
Solution
function swapFirstLast(arr) {
  const [first, middle, last] = arr;
  return [last, middle, first];
}
 
// Or more universal solution
function swapFirstLast(arr) {
  const [first, ...middle] = arr;
  const last = middle.pop();
  return [last, ...middle, first];
}

Task 2: Extracting User Data

Condition: Create a function that extracts name, age, and city from a user object, setting default values.

function getUserInfo(user) {
  // Your code here
  // Should return object {name, age, city}
  // Default values: name = 'Guest', age = 0, city = 'Not specified'
}
 
const user1 = {name: 'John', age: 25};
const user2 = {city: 'New York'};
const user3 = {};
 
console.log(getUserInfo(user1)); // {name: 'John', age: 25, city: 'Not specified'}
console.log(getUserInfo(user2)); // {name: 'Guest', age: 0, city: 'New York'}
console.log(getUserInfo(user3)); // {name: 'Guest', age: 0, city: 'Not specified'}
Solution
function getUserInfo(user = {}) {
  const {
    name = 'Guest',
    age = 0,
    city = 'Not specified'
  } = user;
  
  return {name, age, city};
}

Task 3: Working with Nested Structures

Condition: Write a function that extracts email from a nested user structure and safely handles missing data.

function getEmail(userData) {
  // Your code here
  // Should return email or 'Email not found'
}
 
const user1 = {
  profile: {
    contacts: {
      email: 'john@example.com'
    }
  }
};
 
const user2 = {
  profile: {
    contacts: {}
  }
};
 
const user3 = {};
 
console.log(getEmail(user1)); // 'john@example.com'
console.log(getEmail(user2)); // 'Email not found'
console.log(getEmail(user3)); // 'Email not found'
Solution
function getEmail(userData = {}) {
  const {
    profile: {
      contacts: {
        email = 'Email not found'
      } = {}
    } = {}
  } = userData;
  
  return email;
}
 
// Alternative solution with optional chaining
function getEmail(userData) {
  return userData?.profile?.contacts?.email ?? 'Email not found';
}

Conclusion

Destructuring is a powerful tool of modern JavaScript that:

  • Simplifies code — makes it more readable and concise
  • Increases safety — allows setting default values
  • Improves performance — avoids repeated property access
  • Supports functional style — simplifies working with immutable data

Mastering destructuring is critically important for modern JavaScript development, especially when working with React, APIs, and complex data structures. Use this syntax wisely, keeping in mind code readability and performance.


Want more articles for interview preparation? Subscribe to EasyAdvice, bookmark the site and improve every day 💪