JavaScript Object Destructuring Complete Guide

Master JavaScript object destructuring from basic syntax to advanced patterns. Covers extracting properties, renaming variables, default values, nested destructuring, rest syntax, and function parameter patterns with real-world examples.

JavaScriptbeginner
15 min read

Object destructuring is an ES6 feature that lets you extract properties from objects and assign them to variables in a single statement. Instead of accessing each property one by one with dot notation, destructuring pulls out exactly what you need in a clean, readable syntax. It is one of the most-used ES6 features in modern JavaScript and appears everywhere: variable declarations, function parameters, loop iterations, and import statements.

This guide covers every destructuring pattern from basic extraction to nested objects, default values, renaming, and advanced real-world usage.

Basic Object Destructuring

Wrap the property names you want to extract in curly braces on the left side of the assignment:

javascriptjavascript
const user = {
  name: "Alice",
  age: 28,
  email: "alice@example.com",
  role: "developer"
};
 
// Extract specific properties
const { name, age, email } = user;
 
console.log(name);  // "Alice"
console.log(age);   // 28
console.log(email);  // "alice@example.com"

The variable names must match the property names. Order does not matter:

javascriptjavascript
const { email, name } = user; // Same result, different order
console.log(name);  // "Alice"
console.log(email); // "alice@example.com"

Renaming Variables During Destructuring

Use a colon after the property name to assign it to a differently named variable:

javascriptjavascript
const apiResponse = {
  user_name: "alice_j",
  user_email: "alice@example.com",
  account_type: "premium"
};
 
// Rename snake_case to camelCase
const {
  user_name: userName,
  user_email: userEmail,
  account_type: accountType
} = apiResponse;
 
console.log(userName);    // "alice_j"
console.log(userEmail);   // "alice@example.com"
console.log(accountType); // "premium"
// Note: user_name, user_email, account_type are NOT defined as variables

Renaming to Avoid Conflicts

javascriptjavascript
const userA = { name: "Alice", score: 92 };
const userB = { name: "Bob", score: 85 };
 
const { name: nameA, score: scoreA } = userA;
const { name: nameB, score: scoreB } = userB;
 
console.log(nameA, scoreA); // "Alice" 92
console.log(nameB, scoreB); // "Bob" 85

Default Values

Provide fallback values for properties that might not exist on the object:

javascriptjavascript
const config = {
  theme: "dark",
  language: "en"
};
 
const {
  theme,
  language,
  fontSize = 16,          // Default: not in config
  showSidebar = true,     // Default: not in config
  notifications = false   // Default: not in config
} = config;
 
console.log(theme);         // "dark" (from object)
console.log(fontSize);      // 16 (default applied)
console.log(showSidebar);   // true (default applied)
console.log(notifications); // false (default applied)

Defaults with Renaming Combined

javascriptjavascript
const response = {
  status_code: 200,
  data: { items: [] }
};
 
const {
  status_code: statusCode = 500,
  error_message: errorMessage = "No error",
  data: responseData = null
} = response;
 
console.log(statusCode);    // 200 (from object, renamed)
console.log(errorMessage);  // "No error" (default, renamed)
console.log(responseData);  // { items: [] } (from object, renamed)

When Defaults Apply

Defaults only apply when the property value is undefined, not null or other falsy values:

javascriptjavascript
const data = {
  a: undefined,
  b: null,
  c: 0,
  d: "",
  e: false
};
 
const {
  a = "default",  // "default" — undefined triggers default
  b = "default",  // null — default NOT applied
  c = "default",  // 0 — default NOT applied
  d = "default",  // "" — default NOT applied
  e = "default"   // false — default NOT applied
} = data;
 
console.log(a); // "default"
console.log(b); // null
console.log(c); // 0
console.log(d); // ""
console.log(e); // false

Nested Object Destructuring

Extract properties from objects inside objects:

javascriptjavascript
const order = {
  id: "ORD-001",
  customer: {
    name: "Alice",
    email: "alice@example.com",
    address: {
      street: "123 Main St",
      city: "San Francisco",
      state: "CA",
      zip: "94102"
    }
  },
  total: 159.99
};
 
const {
  id,
  customer: {
    name: customerName,
    address: { city, state, zip }
  },
  total
} = order;
 
console.log(id);           // "ORD-001"
console.log(customerName); // "Alice"
console.log(city);         // "San Francisco"
console.log(state);        // "CA"
console.log(total);        // 159.99

Important: When destructuring nested properties, the intermediate variable (like customer and address above) is NOT created as a variable. Only the leaf values become variables.

Safe Nested Destructuring with Defaults

javascriptjavascript
const user = {
  name: "Alice"
  // No address property
};
 
// Without default: would throw TypeError
// const { address: { city } } = user; // ERROR!
 
// With default: safe
const { address: { city = "Unknown" } = {} } = user;
console.log(city); // "Unknown"

Rest Pattern (...rest)

The rest pattern collects remaining properties into a new object:

javascriptjavascript
const user = {
  name: "Alice",
  age: 28,
  email: "alice@example.com",
  role: "admin",
  department: "engineering"
};
 
const { name, email, ...rest } = user;
 
console.log(name);  // "Alice"
console.log(email); // "alice@example.com"
console.log(rest);  // { age: 28, role: "admin", department: "engineering" }

Removing Sensitive Fields

javascriptjavascript
const userRecord = {
  id: 42,
  name: "Alice",
  email: "alice@example.com",
  password: "hashed_secret",
  ssn: "123-45-6789",
  apiKey: "sk_live_abc123"
};
 
// Remove sensitive fields, keep the rest
const { password, ssn, apiKey, ...safeUser } = userRecord;
 
console.log(safeUser);
// { id: 42, name: "Alice", email: "alice@example.com" }
 
// Original unchanged
console.log(userRecord.password); // "hashed_secret"

Destructuring in Function Parameters

One of the most powerful uses of destructuring is in function parameters. Instead of receiving an object and accessing properties inside the function body, you destructure directly in the parameter list:

javascriptjavascript
// Without destructuring
function displayUser(user) {
  console.log(`${user.name} (${user.age}) - ${user.email}`);
}
 
// With destructuring
function displayUser({ name, age, email }) {
  console.log(`${name} (${age}) - ${email}`);
}
 
displayUser({ name: "Alice", age: 28, email: "alice@example.com" });
// "Alice (28) - alice@example.com"

Function Parameters with Defaults

javascriptjavascript
function createButton({
  text = "Click me",
  color = "blue",
  size = "medium",
  disabled = false
} = {}) {
  return `<button class="${color} ${size}" ${disabled ? "disabled" : ""}>${text}</button>`;
}
 
// All defaults
console.log(createButton());
// '<button class="blue medium" >Click me</button>'
 
// Partial override
console.log(createButton({ text: "Submit", color: "green" }));
// '<button class="green medium" >Submit</button>'

The = {} at the end allows calling the function with no arguments at all.

Destructuring in Array Methods

javascriptjavascript
const users = [
  { name: "Alice", age: 28, active: true },
  { name: "Bob", age: 35, active: false },
  { name: "Charlie", age: 22, active: true }
];
 
// Destructure in map
const names = users.map(({ name }) => name);
console.log(names); // ["Alice", "Bob", "Charlie"]
 
// Destructure in filter
const activeUsers = users.filter(({ active }) => active);
console.log(activeUsers.length); // 2
 
// Destructure in forEach
users.forEach(({ name, age }) => {
  console.log(`${name} is ${age} years old`);
});
 
// Destructure in reduce
const totalAge = users.reduce((sum, { age }) => sum + age, 0);
console.log(totalAge); // 85

Destructuring in Loops

for...of Loop

javascriptjavascript
const products = [
  { name: "Laptop", price: 999, category: "electronics" },
  { name: "Shirt", price: 29, category: "clothing" },
  { name: "Book", price: 15, category: "education" }
];
 
for (const { name, price } of products) {
  console.log(`${name}: $${price}`);
}
// Laptop: $999
// Shirt: $29
// Book: $15

With Object.entries()

javascriptjavascript
const scores = {
  Alice: 92,
  Bob: 85,
  Charlie: 97
};
 
for (const [name, score] of Object.entries(scores)) {
  console.log(`${name} scored ${score}`);
}
// Alice scored 92
// Bob scored 85
// Charlie scored 97

Destructuring with Computed Property Names

Use bracket notation inside destructuring to extract dynamically named properties:

javascriptjavascript
const data = {
  firstName: "Alice",
  lastName: "Johnson",
  email: "alice@example.com"
};
 
const field = "email";
const { [field]: extractedEmail } = data;
 
console.log(extractedEmail); // "alice@example.com"

Swapping Variables

While primarily an array destructuring technique, it highlights destructuring's flexibility:

javascriptjavascript
let a = "hello";
let b = "world";
 
// Swap without temp variable
[a, b] = [b, a];
 
console.log(a); // "world"
console.log(b); // "hello"

Common Patterns Comparison

PatternSyntaxUse Case
Basic extractconst { a, b } = objPull specific properties
Renameconst { a: x } = objAvoid naming conflicts, camelCase
Defaultconst { a = 5 } = objFallback for missing properties
Nestedconst { a: { b } } = objDeep property extraction
Restconst { a, ...rest } = objSeparate known from unknown props
Paramfunction f({ a, b }) {}Clean function signatures
Computedconst { [key]: val } = objDynamic property extraction

Best Practices

  1. Destructure early: Extract needed properties at the top of a function, not inline
  2. Use defaults for optional properties to avoid undefined checks later
  3. Rename API fields at the destructuring boundary (convert snake_case to camelCase once)
  4. Limit nesting depth to 2 levels; deeper nesting hurts readability
  5. Use rest for "everything else" patterns like removing sensitive fields
  6. Add = {} to function params when the entire object argument is optional

Common Mistakes to Avoid

Destructuring without a Declaration Keyword

javascriptjavascript
// WRONG: interpreted as a block, not destructuring
// { name, age } = user; // SyntaxError
 
// CORRECT: wrap in parentheses for existing variables
let name, age;
({ name, age } = { name: "Alice", age: 28 });
console.log(name); // "Alice"

Forgetting Default for Nested Destructuring

javascriptjavascript
const data = {};
 
// WRONG: throws TypeError because data.user is undefined
// const { user: { name } } = data;
 
// CORRECT: provide default empty object
const { user: { name = "Guest" } = {} } = data;
console.log(name); // "Guest"
Rune AI

Rune AI

Key Insights

  • Basic syntax: const { a, b } = obj extracts properties into matching variable names
  • Renaming: const { old: newName } = obj maps property names to different variable names
  • Defaults: const { a = fallback } = obj provides values when properties are undefined
  • Nested: const { a: { b } } = obj extracts from nested objects in one statement
  • Rest pattern: const { known, ...rest } = obj separates specific properties from everything else
RunePowered by Rune AI

Frequently Asked Questions

Does destructuring create a copy of the object?

Destructuring creates new variables that hold the same values. For [primitive values](/tutorials/programming-languages/javascript/primitive-vs-reference-types-in-js-full-guide) (strings, numbers, booleans), the variables are independent copies. For reference values (objects, arrays), the variables point to the same reference. Modifying a destructured object property affects the original.

Can I destructure and keep the whole object too?

Yes. Simply assign the object to a variable and destructure separately, or destructure in one line and reference the original: `const user = data.user; const { name, age } = user;`. Both `user` and the destructured variables remain available.

Is there a performance cost to destructuring?

Modern JavaScript engines optimize destructuring to equivalent property access code. The performance difference compared to manual `obj.prop` access is negligible. Use destructuring wherever it improves readability without worrying about performance.

Can I use destructuring with `Map` or `Set` objects?

Not directly with the object pattern `{}`. Maps use `[key, value]` array pairs, so you would use array destructuring in a `for...of` loop: `for (const [key, value] of map) {}`. Sets can be spread into arrays first if needed.

How does destructuring interact with TypeScript?

TypeScript fully supports destructuring and infers types automatically. You can add type annotations after the destructuring pattern: `const { name, age }: { name: string; age: number } = user;`. TypeScript will warn you if you try to destructure a property that does not exist on the type.

Conclusion

Object destructuring transforms verbose, repetitive property access into clean, declarative variable extraction. Basic destructuring handles simple extractions, renaming tackles naming conflicts and API conversions, defaults prevent undefined surprises, nested destructuring reaches deep into object hierarchies, and rest syntax splits objects into known and unknown parts. Combined with function parameter destructuring, these patterns make JavaScript code significantly more readable and maintainable.