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...inwalks 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 toArray.prototype.
for...ofwalks values — but only on things that are iterable via theSymbol.iteratorprotocol (arrays, strings, Maps, Sets, generators). Plain objects? Not iterable out of the box. Try it and you'll getTypeError: 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.keysandObject.entries, and how to implementSymbol.iteratorso your own objects can be walked withfor...of.Read the full lesson -> [link]
#JavaScript #ForOf #ForIn #Iterables #SymbolIterator #InterviewPrep #Frontend
What You'll Learn
- Why
for...inreturns keys andfor...ofreturns values — and which one to use on objects vs arrays - The prototype pollution pitfall of
for...inand the saferObject.keys/Object.entriesalternatives - How to implement
Symbol.iteratorto make your own objects iterable withfor...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]
Common Mistakes
- Reaching for
for...inon arrays — you get string indices and risk inherited properties; usefor...oforfor (let i = 0; i < arr.length; i++). - Forgetting plain objects aren't iterable —
for (const x of obj)throws; iterateObject.keys(obj),Object.values(obj), orObject.entries(obj)instead. - Skipping
hasOwnProperty(or preferringObject.keys) when usingfor...in— inherited prototype keys sneak in and produce surprising results.
Interview Questions
Q: What is the difference between for...in and for...of?
for...initerates over enumerable property keys (including inherited ones) — works on objects.for...ofiterates 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.iteratoron the object that returns an iterator object with anext()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...initerates all enumerable string-keyed properties of an object, including inherited ones.for...ofiterates the values produced by an object'sSymbol.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 withNumber(i).
Q: What is the prototype pollution problem with for...in?
If any library or code adds an enumerable property to
Object.prototype(orArray.prototype), everyfor...inloop on objects (or arrays) suddenly iterates that key too. Guard withobj.hasOwnProperty(key)or useObject.keys/Object.entriesto 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.