Javascript Objects: Iteration, Destructuring, Spread & Rest Explained with Examples

Sangy KSangy K
10 min read

Working with objects is at the heart of modern Javascript development. Whether you're manipulating API responses, organising user data, or building powerful, dynamic web applications, understanding how to iterate over objects, destructure their properties, and harness the spread/rest operators can take your code from "functional" to "elegant."

The thing isβ€”objects aren't like arrays. They don't come with built-in indexing, and they have their own quirks when it comes to iteration, property access, and manipulation. If you've ever wondered when to use for...in versus Object.keys(), how destructuring can make your code cleaner, or why the spread operator feels like magic, this deep dive is for you.

In this article, we'll break down the different ways to work with object properties, explore safe and efficient patterns for iteration, learn how to extract data with destructuring, and see how spread/rest syntax can supercharge your object workflows. Along the way, we'll sprinkle in practical examples, performance tips, and a few "gotchas" to keep you out of trouble. πŸš€

πŸ” Iterating Over Objects

When working with objects in Javascript, iterating over their properties is a common task, but it works differently than with arrays.

They don't come with built-in indexing or iteration mechanisms. So, when you loop through object properties, the approach you choose matters β€” both for readability and performance.

for...in β€” Old but Flexible

The for...in loop, which we have briefly looked up in Control Structures in Javascript: Steering Your Code Like a Pro, enumerates all enumerable properties, including inherited ones (from the prototype chain). This means it can give you more than you expect. Also, while powerful, it's risky and slower for large datasets.

const user = { name: "Luna", age: 28 };

for (const key in user) {
    if (Object.hasOwn(user, key)) {
        console.log(key, user[key]);
    }
}
  • Why the Object.hasOwn check? It ensures we only iterate over the object's own properties, not inherited ones.

  • When to use? This is primarily the case when you need to include inherited properties intentionally. Otherwise, there are safer and more precise options.

Object.keys, Object.values, and Object.entries β€” Modern & Safer

These methods give you direct access to an object's own properties, making them cleaner and more predictable.

These methods extract data first and then let you iterate using array methods or for...of.

Object.keys(user).forEach(key => console.log(key)); // name, age
Object.values(user).forEach(value => console.log(value)); // "Luna", 28

for (const [key, value] of Object.entries(user)) {
    console.log(`${key}: ${value}`); // name: Luna, age: 28
}
  • Object.keys β†’ Returns an array of property names.

  • Object.values β†’ Returns an array of property values.

  • Object.entries β†’ Returns an array of [key, value] pairs, perfect for destructuring in loops.

Why for...of Doesn't Work Directly on Objects

Objects are not iterable by default, so this will fail:

const user = { name: "Luna", age: 28 };

for (const item of user) {
    // ❌ TypeError: user is not iterable
}

But you can pair for...of with Object.entries to iterate smoothly:

for (const [key, value] of Object.entries(user)) {
    console.log(key, value);
}

This approach is fast and expressive because for...of is optimised for iterables in modern Javascript engines.

Filtering Properties Efficiently

When working with external data (e.g., API responses), you may want to filter out specific properties. Sometimes, you only need certain properties. Javascript makes this easy with Object.entries and Object.fromEntries:

const data = { id: 1, name: "Luna", password: "secret" };

const safeData = Object.fromEntries(
    Object.entries(data).filter(([key]) => key !== "password")
);

console.log(safeData); // { id: 1, name: "Luna" }

This approach is straightforward and avoids manual loops, making it great for working with APIs or sanitising objects.

What happens here?

Object.entries(data)

This takes our data object and turns it into an array of key-value pairs:

[
  ["id", 1],
  ["name", "Luna"],
  ["password", "secret"]
]

.filter(([key]) => key !== "password")

Here, we're using the filter method β€” an array method that lets us select only certain items based on a condition.

  • In this case, we're keeping only the entries where the key is not "password".

  • Note: We'll explore .filter() in detail in our article on arrays, so for now, remember it's a way to pick only the elements we want.

Object.fromEntries(...)

This takes our filtered array of key-value pairs and converts it back into an object.

After filtering, our array becomes:

[
  ["id", 1],
  ["name", "Luna"]
]

Converting it back gives us:

{ id: 1, name: "Luna" }

Result:

We end up with a "safe" version of our object that excludes sensitive information like passwords β€” perfect for sending clean data to a front-end or API.

A Quick Note on Performance

  • For everyday objects, all these methods perform well enough β€” pick what's clearest and safest.

  • For huge objects (thousands of properties), Object.keys with a classic for loop can be slightly faster because it avoids creating callbacks for each iteration.

  • When performance really matters, consider using Maps data structure instead of objects β€” they're optimised for frequent iteration and updates.

🧩 Object Destructuring Basics

Object destructuring lets us unpack properties from objects directly into variables. It's one of those Javascript features that feels magical once you get the hang of it. ✨

Basic Destructuring

Without destructuring, accessing properties can get repetitive:

const user = { name: "Luna", age: 28 };

const name = user.name;
const age = user.age;
console.log(name, age); // Luna 28

With destructuring, we can write the same thing more cleanly:

const { name, age } = user;
console.log(name, age); // Luna 28
  • { name, age } β†’ tells Javascript: "Find these keys in the object and assign their values to variables with the same names."

  • Cleaner, shorter, and much easier to read.

Providing Defaults

What if a property doesn't exist in the object? Without defaults, destructuring will return undefined:

const { role } = user;
console.log(role); // undefined

We can provide fallback values using =:

const { role = "guest" } = user;
console.log(role); // "guest"

This is great for optional data (e.g., user roles, settings, or API responses that may not always include specific fields).

Renaming Variables

Sometimes you want the variable name to be different from the property name (to avoid conflicts or make it more descriptive):

const { name: fullName } = user;
console.log(fullName); // Luna

Here:

  • name: fullName β†’ read the property name but store it in a variable called fullName.

Gotchas & Best Practices

Typos don't throw errors.

const { nmae } = user;
console.log(nmae); // undefined 😬

Always double-check property names β€” destructuring silently returns undefined for missing keys.

Avoid deep nesting

const { address: { city } } = user;

This works, but it breaks if the address is missing (Cannot destructure property city).

Consider using optional chaining or flattening your data structure for better results.

Don't mutate shared objects unintentionally.

Destructuring copies the values, not the object. If you modify nested objects, changes affect the original. For safe copies, use cloning (we'll cover that in later articles about advanced Objects).

Use const for destructured variables.

const { name, age } = user;

Keeps variables predictable and avoids accidental reassignment.

πŸš€ Spread and Rest with Objects

The spread (...) and rest (...) operators work beautifully with objects, giving us powerful ways to copy, merge, and exclude properties.

Copying Objects with Spread

The spread operator creates a shallow copy of an object:

const user = { name: "Luna", age: 28 };
const copy = { ...user };

console.log(copy); // { name: "Luna", age: 28 }

Why use spread instead of =?

const alias = user;
alias.age = 30;

console.log(user.age); // 30 😱

Assignments don't copy objects; they copy references. Modifying alias changes user. Spread(…) avoids this by making a new object (but still shallow β€” nested objects aren't cloned).

Merging Objects

Spread(…) can also merge multiple objects:

const defaults = { role: "guest", theme: "light" };
const settings = { theme: "dark" };
const merged = { ...defaults, ...settings };

console.log(merged); // { role: "guest", theme: "dark" }
  • Order matters: later spreads overwrite earlier ones.

  • Perfect for configuration objects or combining API data.

Rest(…) Syntax to Exclude Keys

Sometimes you want everything except a few properties. Rest syntax makes this easy:

const userProfile = { id: 1, name: "Luna", password: "secret" };
const { password, ...safeProfile } = userProfile;

console.log(safeProfile); // { id: 1, name: "Luna" }

Excellent for sanitising objects (e.g., removing sensitive data before sending to the front-end).

Performance Tips

  • For small to medium objects, spread/rest is perfect β€” it's concise and readable.

  • For huge objects or frequent merges, mutating a new object manually can be slightly faster, but readability usually wins in everyday projects.

πŸ§ͺ Practical Example: Putting It All Together

Let's combine everything we've learned about accessing, modifying, and destructuring objects into one clean example.

const product = {
    id: 101,
    name: "Coffee Mug",
    price: 9.99,
    metadata: {
        color: "black",
        inStock: true
    }
};

// Access properties
console.log(product.name);

// Add and update
product.discount = 0.2;
product.price = 8.99;

// Destructure
const { name: title, price, metadata: { inStock } } = product;
console.log(`${title} - $${price} - Available: ${inStock}`);

Step-by-Step Breakdown

1. Creating the Object

We define a product object with keys like id, name, price, and a nested metadata object. This kind of structure mirrors real-world data β€” like a product in an e-commerce system.

2. Accessing Properties

console.log(product.name);
  • Dot notation is clean and readable for known keys.

  • If we used product["name"], it would achieve the same result β€” but bracket notation shines when property names are dynamic or not valid identifiers.

3. Adding and Updating Properties

product.discount = 0.2; // Adding a new property
product.price = 8.99; // Updating an existing property
  • Objects in Javascript are dynamic β€” you can add or modify properties anytime.

  • Gotcha: Modifying the object here affects all references to it, since objects are passed by reference.

4. Destructuring with Renaming and Nested Access

const { name: title, price, metadata: { inStock } } = product;
  • name: title β†’ grabs product.name but stores it as title.

  • metadata: { inStock } β†’ dives into the nested metadata object and extracts the inStock property.

  • price β†’ directly pulls the price property.

This approach is cleaner than writing:

const title = product.name;
const price = product.price;
const inStock = product.metadata.inStock;

5. Using the Extracted Values

console.log(`${title} - $${price} - Available: ${inStock}`);

This prints something like:

Coffee Mug - $8.99 - Available: true

Why This Matters

This simple example shows why objects are essential:

  • They keep data organised (everything about the product lives in one place).

  • They're dynamic (you can add discounts, update prices).

  • They're expressive (destructuring makes your code shorter and more readable).

Best Practices Highlighted

  1. Use dot notation for most cases, but switch to bracket notation for dynamic keys.

  2. Avoid unnecessary mutations by cloning when needed (we'll explore this in future articles on Objects).

  3. Use destructuring to make your code more declarative and avoid repetitive property access.

  4. Keep nested objects manageable β€” deeply nested destructuring can hurt readability.

Wrapping Up: Objects Are Just the Beginning

We've covered a lot of ground here β€” from creating objects and accessing their properties to modifying them, checking for keys, looping through their data, and even making our code cleaner with destructuring and shorthand tricks. 🎯

Objects are the backbone of Javascript. They're how we structure data, model real-world entities, and build more complex applications.

But believe it or not, we've only scratched the surface. There's a whole world of advanced object concepts β€” prototypes, inheritance, getters and setters, immutability, and design patterns β€” waiting for us to explore. πŸš€

But before we take that deep dive into advanced Javascript concepts, we need to visit Javascript Arrays and then something that brings our code to life: the DOM (Document Object Model).

So, take a breather, let this sink in, and get ready β€” next up, we'll step into the Javascript Arrays ✨

0
Subscribe to my newsletter

Read articles from Sangy K directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sangy K
Sangy K

An Introvert Coder | Trying to be a Full-stack Developer | A Wannabe Content Creator