JavaScript Interview Prep
Objects & Arrays

for...in vs for...of

Keys vs Values

LinkedIn Hook

Two loops that look nearly identical. Two very different jobs. Swap them and your code breaks in silent, infuriating ways.

for...in walks keys — every enumerable property on an object, including ones inherited from the prototype chain. Use it on an array and you get "0", "1", "2" as strings, plus any stray properties someone added to Array.prototype.

for...of walks values — but only on things that are iterable via the Symbol.iterator protocol (arrays, strings, Maps, Sets, generators). Plain objects? Not iterable out of the box. Try it and you'll get TypeError: obj is not iterable.

In this lesson you'll see the prototype-pollution gotcha that has bitten production code for years, the safe patterns with Object.keys and Object.entries, and how to implement Symbol.iterator so your own objects can be walked with for...of.

Read the full lesson -> [link]

#JavaScript #ForOf #ForIn #Iterables #SymbolIterator #InterviewPrep #Frontend


for...in vs for...of thumbnail


What You'll Learn

  • Why for...in returns keys and for...of returns values — and which one to use on objects vs arrays
  • The prototype pollution pitfall of for...in and the safer Object.keys / Object.entries alternatives
  • How to implement Symbol.iterator to make your own objects iterable with for...of

Confusingly Similar

These look similar but iterate over completely different things. Confusing them is a classic interview pitfall.

for...in — Iterates Over Keys (Enumerable Properties)

const person = { name: "Rakibul", age: 25, city: "Dhaka" };

for (const key in person) {
  console.log(key, person[key]);
}
// "name" "Rakibul"
// "age" 25
// "city" "Dhaka"

for...of — Iterates Over Values (Iterables)

const colors = ["red", "green", "blue"];

for (const color of colors) {
  console.log(color);
}
// "red"
// "green"
// "blue"

The Pitfall: for...in on Arrays

const arr = [10, 20, 30];

// DON'T use for...in on arrays!
for (const index in arr) {
  console.log(typeof index); // "string" — not number!
  console.log(index);        // "0", "1", "2"
}

// USE for...of on arrays
for (const value of arr) {
  console.log(value); // 10, 20, 30
}

The Prototype Pollution Problem with for...in

// Someone adds to Object.prototype (bad practice but happens)
Object.prototype.customMethod = function() {};

const user = { name: "Rakibul", age: 25 };

for (const key in user) {
  console.log(key);
}
// "name"
// "age"
// "customMethod" — UNEXPECTED! Inherited from prototype!

// Fix: always guard with hasOwnProperty
for (const key in user) {
  if (user.hasOwnProperty(key)) {
    console.log(key); // "name", "age" only
  }
}

// Better fix: use Object.keys() instead
Object.keys(user).forEach(key => {
  console.log(key); // "name", "age" only
});

// Clean up
delete Object.prototype.customMethod;

for...of Works with Any Iterable

// Strings
for (const char of "hello") {
  console.log(char); // "h", "e", "l", "l", "o"
}

// Maps
const map = new Map([["a", 1], ["b", 2]]);
for (const [key, value] of map) {
  console.log(key, value); // "a" 1, "b" 2
}

// Sets
const set = new Set([1, 2, 3]);
for (const value of set) {
  console.log(value); // 1, 2, 3
}

// Plain objects are NOT iterable by default
const obj = { a: 1, b: 2 };
// for (const value of obj) {} // TypeError: obj is not iterable

// Fix: use Object.entries()
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value); // "a" 1, "b" 2
}

Symbol.iterator — Making Objects Iterable

const range = {
  start: 1,
  end: 5,
  [Symbol.iterator]() {
    let current = this.start;
    const end = this.end;
    return {
      next() {
        return current <= end
          ? { value: current++, done: false }
          : { done: true };
      }
    };
  }
};

for (const num of range) {
  console.log(num); // 1, 2, 3, 4, 5
}

console.log([...range]); // [1, 2, 3, 4, 5]

for...in vs for...of visual 1


Common Mistakes

  • Reaching for for...in on arrays — you get string indices and risk inherited properties; use for...of or for (let i = 0; i < arr.length; i++).
  • Forgetting plain objects aren't iterable — for (const x of obj) throws; iterate Object.keys(obj), Object.values(obj), or Object.entries(obj) instead.
  • Skipping hasOwnProperty (or preferring Object.keys) when using for...in — inherited prototype keys sneak in and produce surprising results.

Interview Questions

Q: What is the difference between for...in and for...of?

for...in iterates over enumerable property keys (including inherited ones) — works on objects. for...of iterates over values of iterables (arrays, strings, Maps, Sets) using the Symbol.iterator protocol.

Q: Why should you avoid for...in on arrays?

Two reasons: (1) it gives string keys instead of numeric indices, and (2) it iterates over ALL enumerable properties including inherited ones from the prototype chain, which can produce unexpected results.

Q: How do you make a plain object work with for...of?

Implement Symbol.iterator on the object that returns an iterator object with a next() method, or convert to entries first: for (const [k, v] of Object.entries(obj)).

Q: What does for...in iterate? What about for...of?

for...in iterates all enumerable string-keyed properties of an object, including inherited ones. for...of iterates the values produced by an object's Symbol.iterator — available on built-ins like Array, String, Map, Set, TypedArray, and generators.

Q: Why is for...in dangerous on arrays?

It yields string indices, not numbers, and includes any enumerable properties on the prototype chain or added to the array itself (e.g., arr.foo = 1), so you get elements you didn't ask for and have to coerce the indices with Number(i).

Q: What is the prototype pollution problem with for...in?

If any library or code adds an enumerable property to Object.prototype (or Array.prototype), every for...in loop on objects (or arrays) suddenly iterates that key too. Guard with obj.hasOwnProperty(key) or use Object.keys / Object.entries to only see own keys.


Quick Reference — Cheat Sheet

for...in vs for...of — QUICK MAP

for...in  -> KEYS
  const k in obj
  yields enumerable string keys, incl. inherited
  use on plain objects (with hasOwnProperty guard)
  NEVER use on arrays

for...of  -> VALUES
  const v of iterable
  uses Symbol.iterator protocol
  works on: Array, String, Map, Set, TypedArray, generators
  throws TypeError on plain objects

Safe patterns:
  Object.keys(obj).forEach(k => ...)
  for (const [k, v] of Object.entries(obj)) { ... }
  for (const item of arr) { ... }

Making your own iterable:
  obj[Symbol.iterator] = function*() { yield 1; yield 2; }
  or return { next() { ... } } from [Symbol.iterator]

Previous: map, filter, reduce -> The Array Transformation Trio Next: Optional Chaining & Nullish Coalescing -> Safer Access, Smarter Defaults


This is Lesson 7.6 of the JavaScript Interview Prep Course — 14 chapters, 87 lessons.

On this page