skip to content

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?

level: middleimportance: should knowfreq 52%

answer

  1. the loop does not stop at the object
  2. enumerable, string-keyed, chain-wide
  3. a plain literal usually looks clean
  4. guard with Object.hasOwn
  5. own-keys helpers avoid the problem

basics

~20 s

for...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 lines
javascript
const 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 throw

go deeper

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

context