When you use `for...in` over a plain object, which keys can show up that you never put on that object, and how do you filter them out?
answer
- the loop does not stop at the object
- enumerable, string-keyed, chain-wide
- a plain literal usually looks clean
- guard with Object.hasOwn
- own-keys helpers avoid the problem
basics
~20 sfor...in visits enumerable string keys from the object's prototype chain as well as its own, so anything enumerable on a custom prototype or added to a built-in prototype appears. Guard each key with Object.hasOwn(obj, key), or skip for...in and use Object.keys, values, or entries.
solid answer
~50 s`for...in` enumerates enumerable string-keyed properties of the object **and** of every object on its prototype chain. With a plain `{}` literal you usually see only your own keys, because the built-in `Object.prototype` methods are non-enumerable. The extras appear when the object was created with `Object.create(someProto)`, when it is a class instance whose prototype got enumerable properties assigned to it, or when some library did `Object.prototype.foo = ...` — after which `foo` shows up in every `for...in` in the program. The guard is `Object.hasOwn(obj, key)` (ES2022); before that you wrote `Object.prototype.hasOwnProperty.call(obj, key)`, which is worth knowing because a bare `obj.hasOwnProperty(key)` throws on an `Object.create(null)` object and can be shadowed by a key literally named `hasOwnProperty`. In practice the better move is to avoid the guard entirely: `Object.keys`, `Object.values`, and `Object.entries` already return only own enumerable string keys.
code
javascript · 17 linesconst proto = { inherited: 1 };
const obj = Object.create(proto);
obj.own = 2;
for (const k in obj) console.log('in:', k); // own, inherited
for (const k in obj) {
if (!Object.hasOwn(obj, k)) continue;
console.log('guarded:', k); // own
}
console.log(Object.keys(obj)); // ['own']
console.log(Object.entries(obj)); // [['own', 2]]
const dict = Object.create(null);
dict.a = 1;
console.log(Object.hasOwn(dict, 'a')); // true; dict.hasOwnProperty would throwgo deeper
Know that for...in can surface keys from the prototype chain and that Object.keys or Object.entries with for...of gives you only the object's own keys.
Explain the enumeration rule — enumerable, string-keyed, own plus inherited — and why built-in Object.prototype methods stay invisible while assignment onto a custom prototype does not.
Justify the guard spelling: name null-prototype dictionaries and a data key called hasOwnProperty as the concrete failure cases, and argue for eliminating the guard by choosing own-key helpers instead.
Frame it as a data-shape decision: null-prototype objects for untrusted dictionaries and own-key iteration as the codebase default make prototype pollution and inherited-key leakage structurally impossible rather than defended against per loop.
## What for...in reaches `for...in` does not enumerate an object; it enumerates a *chain*. Starting at the object, it visits each enumerable string-keyed property, then moves to the object's prototype and does the same, and so on to the end of the chain — skipping any name it has already yielded, so a shadowed key appears once. Symbol keys are never included, and non-enumerable properties are never included. For a plain literal that is usually invisible, because everything on `Object.prototype` (`toString`, `hasOwnProperty`, `valueOf`, …) is defined as non-enumerable: ```js const o = { a: 1, b: 2 }; for (const k in o) console.log(k); // a, b — nothing else ``` ## Where the surprise keys come from **A custom prototype.** The most direct case: ```js const proto = { shared: 'yes' }; const obj = Object.create(proto); obj.own = 1; for (const k in obj) console.log(k); // own, shared ``` **Assignment onto a prototype.** Properties created by plain assignment are enumerable by default, so this leaks: ```js function Point(x) { this.x = x; } Point.prototype.describe = function () { return `x=${this.x}`; }; for (const k in new Point(1)) console.log(k); // x, describe ``` Note that methods defined with `class` syntax are non-enumerable, so a `class Point { describe() {} }` does **not** leak `describe` into `for...in`. That asymmetry between the two ways of writing the same class trips people up. **Built-in prototype extension.** The historical worst case: a script does `Array.prototype.last = function () {...}` or `Object.prototype.each = ...`, and now every `for...in` anywhere in the program yields an extra key. This is why defensive `hasOwnProperty` guards litter older codebases. ## The guard, and why the obvious spelling is unsafe The modern form is `Object.hasOwn`, added in ES2022: ```js for (const k in obj) { if (!Object.hasOwn(obj, k)) continue; // ... own keys only } ``` Before ES2022 the correct spelling was the call form: ```js if (!Object.prototype.hasOwnProperty.call(obj, k)) continue; ``` That call form exists for two concrete reasons, and being able to name them is what separates a memorised idiom from understanding: 1. **Null-prototype objects.** `Object.create(null)` produces an object with no prototype at all — a common choice for dictionaries precisely because nothing is inherited. It therefore has no `hasOwnProperty` method, so `obj.hasOwnProperty(k)` throws a `TypeError`. 2. **Shadowing.** If the data itself contains a key named `hasOwnProperty` — entirely possible when the object came from parsed JSON or user input — then `obj.hasOwnProperty` is that data, not the method, and calling it fails or lies. `Object.hasOwn(obj, k)` sidesteps both, which is exactly why it was added. ## The better default: skip for...in Almost every loop that needs a `hasOwn` guard would be clearer written with an own-keys helper and `for...of`: ```js for (const k of Object.keys(obj)) { /* own enumerable string keys */ } for (const v of Object.values(obj)) { /* their values */ } for (const [k, v] of Object.entries(obj)) { /* both */ } ``` All three return arrays containing only **own**, enumerable, string-keyed properties — the inherited-key problem simply does not arise, and you get real array methods (`map`, `filter`, `sort`) for free. Because they are arrays you can also `break` out of the `for...of`, which is a bonus over `forEach`. The cases where `for...in` is genuinely the right tool are narrow: you actually want inherited enumerable properties, or you are working with an object whose keys you cannot materialise into an array for memory reasons. Both are rare. ## A note on completeness None of these — `for...in`, `Object.keys`, `Object.values`, `Object.entries` — see symbol keys or non-enumerable properties. If you need everything an object owns, that is what `Object.getOwnPropertyNames` and `Object.getOwnPropertySymbols` are for. Reaching for those is a signal you are doing reflection rather than ordinary data iteration.
- Why is `obj.hasOwnProperty(key)` considered unsafe as a guard?Two reasons. An object made with `Object.create(null)` inherits nothing, so it has no `hasOwnProperty` method and the call throws. And if the data itself contains a key named `hasOwnProperty` — easy with parsed JSON — the property shadows the method. `Object.hasOwn(obj, key)`, or `Object.prototype.hasOwnProperty.call(obj, key)` before ES2022, is immune to both.
- Do class methods show up in a for...in over an instance?No. Methods declared with `class` syntax are non-enumerable on the prototype, so `for...in` skips them; only the instance's own fields appear. Methods attached the older way — `Ctor.prototype.method = function () {}` — are created by plain assignment and therefore enumerable, so those do show up. Same apparent design, different enumeration behaviour.
- When is for...in still the right choice over Object.keys?When you genuinely want inherited enumerable properties — walking a config object layered over a defaults prototype, for example — or when materialising all keys into an array is a real memory concern. Both are uncommon; for ordinary data iteration `Object.keys`/`values`/`entries` with `for...of` is clearer and needs no guard.
saying these in an interview costs you the question
- Says for...in only visits own properties
- Uses obj.hasOwnProperty on a null-prototype object
- Thinks for...in enumerates symbol keys
- Assumes class methods appear in for...in
- Believes Object.keys includes inherited keys