JavaScript Tutorial

For...in Loop in JavaScript: Syntax, Examples and Best Practices

Master the JavaScript for...in loop with clear syntax breakdown, iterating over object keys, nested objects, real-world examples, common mistakes, and best practices.

Welcome back! šŸ‘‹ In the previous lesson, you mastered the for...of loop for iterating over values of arrays and iterables. Now let's explore the final loop in our series — the for...in loop!

The for...in loop is specifically designed for one job — iterating over the keys (property names) of an object. Every time you need to inspect, display, or process the properties of an object dynamically, for...in is your tool.

Let's break it down completely!


What is a for...in Loop?

A for...in loop iterates over all the enumerable property keys of an object. Each iteration gives you one key (property name) as a string — and you can use that key to access the corresponding value.

let person = {
  name: "Mihir",
  age: 25,
  city: "Mumbai",
};

for (let key in person) {
  console.log(key, ":", person[key]);
}
// Output:
// name : Mihir
// age  : 25
// city : Mumbai

Syntax

for (let key in object) {
  // use key or object[key]
}
  • key — a variable that holds the current property name (as a string) on each iteration
  • in — the keyword that separates the variable from the object
  • object — the plain object whose keys you want to loop over

How JavaScript Executes a for...in Loop

let car = {
  brand: "Toyota",
  model: "Camry",
  year: 2023,
  color: "White",
};

for (let key in car) {
  console.log(`${key} → ${car[key]}`);
}

Step by step:

  • Iteration 1: key = "brand" → car["brand"] = "Toyota"
  • Iteration 2: key = "model" → car["model"] = "Camry"
  • Iteration 3: key = "year" → car["year"] = 2023
  • Iteration 4: key = "color" → car["color"] = "White"
  • Done — no more keys
// Output:
// brand → Toyota
// model → Camry
// year  → 2023
// color → White

Each key comes out as a string — even if the property name looks like a number.


Accessing Keys and Values

Keys Only

let config = {
  theme: "dark",
  language: "en",
  fontSize: 16,
  notifications: true,
};

console.log("Config keys:");
for (let key in config) {
  console.log(" -", key);
}
// Output:
// Config keys:
//  - theme
//  - language
//  - fontSize
//  - notifications

Values Only

console.log("Config values:");
for (let key in config) {
  console.log(" -", config[key]);
}
// Output:
// Config values:
//  - dark
//  - en
//  - 16
//  - true

Keys and Values Together

console.log("Full config:");
for (let key in config) {
  console.log(`  ${key}: ${config[key]}`);
}
// Output:
// Full config:
//   theme: dark
//   language: en
//   fontSize: 16
//   notifications: true

Checking Own Properties with hasOwnProperty()

When working with objects that may inherit properties from a prototype, use hasOwnProperty() to make sure you only process the object's own properties — not inherited ones.

let student = {
  name: "Priya",
  marks: 88,
  grade: "B",
};

for (let key in student) {
  if (student.hasOwnProperty(key)) {
    console.log(`${key}: ${student[key]}`);
  }
}
// Output:
// name: Priya
// marks: 88
// grade: B

This is especially important when dealing with objects created from classes or constructors where inherited properties might show up unexpectedly.


Nested Objects

for...in only iterates over the top-level keys of an object. For nested objects, you need another loop inside.

One Level Deep

let employee = {
  name: "Rahul",
  role: "Developer",
  skills: ["JavaScript", "React", "Node.js"],
  salary: 75000,
};

for (let key in employee) {
  console.log(`${key}: ${employee[key]}`);
}
// Output:
// name: Rahul
// role: Developer
// skills: JavaScript,React,Node.js  (array prints as comma-separated)
// salary: 75000

Handling Nested Objects

let userProfile = {
  name: "Sara",
  age: 28,
  address: {
    city: "Pune",
    state: "Maharashtra",
    pin: "411001",
  },
  contact: {
    email: "sara@example.com",
    phone: "9876543210",
  },
};

for (let key in userProfile) {
  let value = userProfile[key];

  if (typeof value === "object" && !Array.isArray(value)) {
    console.log(`${key}:`);
    for (let nestedKey in value) {
      console.log(`  ${nestedKey}: ${value[nestedKey]}`);
    }
  } else {
    console.log(`${key}: ${value}`);
  }
}
// Output:
// name: Sara
// age: 28
// address:
//   city: Pune
//   state: Maharashtra
//   pin: 411001
// contact:
//   email: sara@example.com
//   phone: 9876543210

Dynamic Object Inspection

One of the most powerful uses of for...in — inspecting objects whose structure you don't know in advance.

function inspectObject(obj, label = "Object") {
  console.log(`\n=== ${label} ===`);
  console.log(`Total properties: ${Object.keys(obj).length}`);

  for (let key in obj) {
    let value = obj[key];
    let type = typeof value;
    console.log(`  ${key} (${type}): ${value}`);
  }
}

let laptop = {
  brand:    "Dell",
  model:    "XPS 15",
  ram:      "16GB",
  storage:  "512GB",
  price:    89999,
  inStock:  true,
};

inspectObject(laptop, "Laptop Specs");
// Output:
// === Laptop Specs ===
// Total properties: 6
//   brand (string): Dell
//   model (string): XPS 15
//   ram (string): 16GB
//   storage (string): 512GB
//   price (number): 89999
//   inStock (boolean): true

Real-World Examples

Example 1: Display User Settings

let userSettings = {
  theme:         "dark",
  language:      "English",
  fontSize:      14,
  notifications: true,
  autoSave:      true,
  currency:      "INR",
};

console.log("āš™ļø  Your Current Settings:");
console.log("─".repeat(35));

for (let setting in userSettings) {
  let value = userSettings[setting];
  let display = typeof value === "boolean"
    ? (value ? "āœ… Enabled" : "āŒ Disabled")
    : value;

  console.log(`  ${setting.padEnd(16)}: ${display}`);
}
// Output:
// āš™ļø  Your Current Settings:
// ───────────────────────────────────
//   theme           : dark
//   language        : English
//   fontSize        : 14
//   notifications   : āœ… Enabled
//   autoSave        : āœ… Enabled
//   currency        : INR

Example 2: Count Object Properties by Type

let product = {
  name:       "Wireless Headphones",
  brand:      "Sony",
  price:      4999,
  rating:     4.5,
  inStock:    true,
  reviews:    1240,
  onSale:     false,
  category:   "Electronics",
};

let typeCounts = {};

for (let key in product) {
  let type = typeof product[key];
  typeCounts[type] = (typeCounts[type] || 0) + 1;
}

console.log("Property Type Summary:");
for (let type in typeCounts) {
  console.log(`  ${type}: ${typeCounts[type]} property/properties`);
}
// Output:
// Property Type Summary:
//   string: 3 property/properties
//   number: 3 property/properties
//   boolean: 2 property/properties

Example 3: Compare Two Objects

function compareObjects(obj1, obj2, name1 = "Object 1", name2 = "Object 2") {
  let allKeys = new Set([...Object.keys(obj1), ...Object.keys(obj2)]);

  console.log(`\n${"Key".padEnd(15)} | ${name1.padEnd(15)} | ${name2.padEnd(15)} | Match`);
  console.log("─".repeat(65));

  for (let key of allKeys) {
    let val1   = obj1[key] ?? "—";
    let val2   = obj2[key] ?? "—";
    let match  = obj1[key] === obj2[key] ? "āœ…" : "āŒ";
    console.log(`${key.padEnd(15)} | ${String(val1).padEnd(15)} | ${String(val2).padEnd(15)} | ${match}`);
  }
}

let plan1 = { storage: "10GB", support: "Email",   price: 499,  api: false };
let plan2 = { storage: "50GB", support: "Priority", price: 999, api: true  };

compareObjects(plan1, plan2, "Basic Plan", "Pro Plan");
// Output:
// Key             | Basic Plan      | Pro Plan        | Match
// ─────────────────────────────────────────────────────────────────
// storage         | 10GB            | 50GB            | āŒ
// support         | Email           | Priority        | āŒ
// price           | 499             | 999             | āŒ
// api             | false           | true            | āŒ

Example 4: Calculate Cart Total from Object

let cart = {
  notebook:  { price: 120, qty: 3 },
  pen:       { price: 20,  qty: 10 },
  eraser:    { price: 10,  qty: 5  },
  ruler:     { price: 35,  qty: 2  },
};

let grandTotal = 0;

console.log("===== CART SUMMARY =====");
for (let item in cart) {
  let { price, qty } = cart[item];
  let subtotal = price * qty;
  grandTotal += subtotal;
  console.log(`${item.padEnd(12)}: ₹${price} Ɨ ${qty} = ₹${subtotal}`);
}
console.log("========================");
console.log(`GRAND TOTAL: ₹${grandTotal}`);
// Output:
// ===== CART SUMMARY =====
// notebook    : ₹120 Ɨ 3 = ₹360
// pen         : ₹20 Ɨ 10 = ₹200
// eraser      : ₹10 Ɨ 5 = ₹50
// ruler       : ₹35 Ɨ 2 = ₹70
// ========================
// GRAND TOTAL: ₹680

Example 5: Build a Summary Report from Data Object

let salesReport = {
  January:  45000,
  February: 52000,
  March:    48000,
  April:    61000,
  May:      57000,
  June:     70000,
};

let total = 0;
let highest = { month: "", amount: 0 };
let lowest  = { month: "", amount: Infinity };

for (let month in salesReport) {
  let amount = salesReport[month];
  total += amount;

  if (amount > highest.amount) highest = { month, amount };
  if (amount < lowest.amount)  lowest  = { month, amount };
}

let average = total / Object.keys(salesReport).length;

console.log("=== šŸ“Š Sales Report (H1) ===");
for (let month in salesReport) {
  let bar = "ā–ˆ".repeat(Math.round(salesReport[month] / 5000));
  console.log(`${month.padEnd(10)}: ₹${salesReport[month].toLocaleString().padStart(7)} ${bar}`);
}
console.log("\nšŸ“ˆ Total Revenue  : ₹" + total.toLocaleString());
console.log("šŸ“Š Monthly Average: ₹" + Math.round(average).toLocaleString());
console.log(`šŸ† Best Month     : ${highest.month} (₹${highest.amount.toLocaleString()})`);
console.log(`šŸ“‰ Lowest Month   : ${lowest.month} (₹${lowest.amount.toLocaleString()})`);
// Output:
// === šŸ“Š Sales Report (H1) ===
// January   : ₹ 45,000 ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ
// February  : ₹ 52,000 ā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆā–ˆ
// ...
// šŸ“ˆ Total Revenue  : ₹3,33,000
// šŸ† Best Month     : June (₹70,000)
// šŸ“‰ Lowest Month   : January (₹45,000)

for...in vs for...of — Know the Difference

This is the most important distinction to remember:

let person = { name: "Mihir", age: 25, city: "Mumbai" };
let colors = ["Red", "Green", "Blue"];

// for...in on OBJECT — gives keys āœ…
for (let key in person) {
  console.log(key); // "name", "age", "city"
}

// for...of on ARRAY — gives values āœ…
for (let color of colors) {
  console.log(color); // "Red", "Green", "Blue"
}

// āŒ for...in on ARRAY — gives string indexes, not values!
for (let key in colors) {
  console.log(key); // "0", "1", "2"  ← not what you want!
}

// āŒ for...of on OBJECT — TypeError: object is not iterable!
for (let val of person) {
  console.log(val); // TypeError āŒ
}
LoopUse onGives you
for...inObjects āœ…Keys (property names)
for...inArrays āš ļøString indexes — avoid!
for...ofArrays āœ…Values
for...ofObjects āŒTypeError — not iterable

Simple rule: for...in → in an object (keys). for...of → of an array (values).


Common Mistakes

Mistake 1: Using for...in on an Array

let fruits = ["Apple", "Banana", "Mango"];

// āŒ Gives string indexes — not what you want for arrays
for (let key in fruits) {
  console.log(key);          // "0", "1", "2" — strings!
  console.log(fruits[key]);  // Works, but bad practice
}

// āœ… Use for...of for arrays
for (let fruit of fruits) {
  console.log(fruit); // Apple, Banana, Mango āœ…
}

Mistake 2: Forgetting Keys Are Always Strings

let scores = { 1: "Gold", 2: "Silver", 3: "Bronze" };

for (let key in scores) {
  console.log(typeof key); // "string" — always string, even for number keys!
  console.log(key + 1);    // "11", "21", "31" — string concatenation, not addition!
}

// āœ… Convert to number when needed
for (let key in scores) {
  let rank = Number(key);
  console.log(`Rank ${rank}: ${scores[key]}`); // Rank 1: Gold āœ…
}

Mistake 3: Iterating Inherited Properties Unintentionally

function Animal(name) {
  this.name = name;
}
Animal.prototype.type = "Mammal"; // Inherited property

let dog = new Animal("Rex");
dog.breed = "Labrador";

// āŒ for...in picks up inherited "type" property too!
for (let key in dog) {
  console.log(key); // "name", "breed", "type" ← "type" is inherited!
}

// āœ… Filter with hasOwnProperty
for (let key in dog) {
  if (dog.hasOwnProperty(key)) {
    console.log(key); // "name", "breed" āœ… — own properties only
  }
}

Mistake 4: Expecting a Specific Order

let data = { b: 2, a: 1, c: 3 };

// āš ļø for...in order is not always guaranteed for all engines
// For string keys, most modern engines do maintain insertion order —
// but don't rely on it for logic-critical code

for (let key in data) {
  console.log(key); // Usually: b, a, c — but not guaranteed in all cases
}

// āœ… If order matters, use an array or sort explicitly
let sorted = Object.keys(data).sort();
for (let key of sorted) {
  console.log(`${key}: ${data[key]}`); // a: 1, b: 2, c: 3 āœ…
}

Practical Exercise

Create a file called for-in-loop.js:

// šŸŽÆ For...in Loop Practice

// 1. Student profile display
console.log("=== šŸ§‘ā€šŸŽ“ Student Profile ===");
let student = {
  name:        "Mihir",
  rollNo:      42,
  department:  "Computer Science",
  semester:    5,
  cgpa:        8.7,
  isHosteler:  true,
  backlog:     false,
};

for (let field in student) {
  let value = student[field];
  let display = typeof value === "boolean"
    ? (value ? "Yes āœ…" : "No āŒ")
    : value;
  console.log(`  ${field.padEnd(14)}: ${display}`);
}


// 2. Find keys with specific value type
console.log("\n=== šŸ” Number Properties ===");
let mixedData = {
  username:    "priya_dev",
  age:         26,
  score:       94.5,
  city:        "Bangalore",
  isPremium:   true,
  loginCount:  142,
  bio:         "Full-stack developer",
};

let numberKeys = [];
for (let key in mixedData) {
  if (typeof mixedData[key] === "number") {
    numberKeys.push(key);
    console.log(`  ${key}: ${mixedData[key]}`);
  }
}
console.log(`Found ${numberKeys.length} number properties.`);


// 3. Copy and transform an object
console.log("\n=== šŸ’° Price List with Tax ===");
let basePrices = {
  Laptop:  55000,
  Phone:   18000,
  Tablet:  25000,
  Watch:   8000,
  Earbuds: 3500,
};

let taxRate = 0.18; // 18% GST
let finalPrices = {};

for (let item in basePrices) {
  finalPrices[item] = Math.round(basePrices[item] * (1 + taxRate));
}

console.log(`${"Item".padEnd(10)} | ${"Base Price".padEnd(12)} | ${"With 18% GST".padEnd(12)}`);
console.log("─".repeat(40));
for (let item in basePrices) {
  console.log(
    `${item.padEnd(10)} | ₹${String(basePrices[item]).padEnd(11)} | ₹${finalPrices[item]}`
  );
}


// 4. Nested object — Employee directory
console.log("\n=== šŸ‘„ Employee Directory ===");
let directory = {
  E001: { name: "Mihir",  dept: "Engineering", salary: 85000 },
  E002: { name: "Priya",  dept: "Design",      salary: 72000 },
  E003: { name: "Rahul",  dept: "Marketing",   salary: 65000 },
  E004: { name: "Sara",   dept: "Engineering", salary: 91000 },
};

for (let id in directory) {
  let emp = directory[id];
  console.log(`[${id}] ${emp.name.padEnd(8)} | ${emp.dept.padEnd(14)} | ₹${emp.salary.toLocaleString()}`);
}

Run it:

node for-in-loop.js

Expected Output:

=== šŸ§‘ā€šŸŽ“ Student Profile ===
  name          : Mihir
  rollNo        : 42
  department    : Computer Science
  semester      : 5
  cgpa          : 8.7
  isHosteler    : Yes āœ…
  backlog       : No āŒ

=== šŸ” Number Properties ===
  age: 26
  score: 94.5
  loginCount: 142
Found 3 number properties.

=== šŸ’° Price List with Tax ===
Item       | Base Price   | With 18% GST
────────────────────────────────────────
Laptop     | ₹55000       | ₹64900
Phone      | ₹18000       | ₹21240
...

=== šŸ‘„ Employee Directory ===
[E001] Mihir    | Engineering   | ₹85,000
[E002] Priya    | Design        | ₹72,000

Key Takeaways

Congratulations! šŸŽ‰ You now fully understand the for...in loop — and with it, you've completed the entire loops series!

āœ… for...in iterates over the enumerable keys of an object — one property name per iteration.

āœ… Keys are always strings — even numeric-looking keys like { 1: "Gold" }.

āœ… Use hasOwnProperty() to filter out inherited properties when working with class instances or constructor functions.

āœ… Nested objects — for...in only goes one level deep; nest another loop for nested values.

āœ… Never use for...in on arrays — use for...of or a classic for loop instead.

āœ… Order is not guaranteed — if order matters, sort keys manually with Object.keys().sort().

āœ… for...in vs for...of:

  • for...in → object keys
  • for...of → array/iterable values

Best Practices

  1. āœ… Use for...in only on plain objects — never on arrays
  2. āœ… Always use hasOwnProperty() when there's any chance of inherited properties
  3. āœ… Use Object.keys(), Object.values(), or Object.entries() when you need array methods on object data
  4. āœ… Remember — keys are always strings — convert with Number(key) when needed
  5. āœ… If order matters, sort keys first: Object.keys(obj).sort()
  6. āœ… For nested objects, use a nested for...in or a recursive function
  7. āœ… Prefer for...of with Object.entries() over for...in in modern code — it's cleaner
  8. āœ… Keep the loop body focused — process one property at a time

šŸŽ‰ Loops Series Complete!

Amazing work! You've now mastered all five loop types in JavaScript:

LoopBest For
forKnown number of iterations
whileCondition-driven, unknown count
do...whileMust run body at least once
for...ofValues of arrays, strings, iterables
for...inKeys of plain objects

What's Next?

You've completed the entire Loops in JavaScript series — that's a huge milestone! šŸ†

Next up, we move into one of the most important topics in all of JavaScript — Functions → — where you'll learn how to organize, reuse, and scale your code like a professional developer!

Let's keep going! šŸ’Ŗ