Skip to content
JavaScript js control-flow 3 min read

for...in

The for...in loop walks over the enumerable string keys of an object, giving you each property name one at a time. It is the idiomatic way to inspect a plain object’s own properties, but it carries two famous traps: it climbs the prototype chain by default, and it is the wrong tool for arrays. Understanding both is the difference between code that quietly breaks and code you can trust.

Basic syntax

for...in binds a variable to each key (always a string) and runs the body once per key. You read the value with bracket notation.

const user = { name: "Ada", role: "engineer", active: true };

for (const key in user) {
  console.log(`${key} = ${user[key]}`);
}

Output:

name = Ada
role = engineer
active = true

Use const for the loop variable — it is reassigned fresh on each iteration, so there is no need for let. The order of keys is well defined in modern engines: integer-like keys come first in ascending numeric order, then string keys in insertion order.

The inherited-property pitfall

for...in does not stop at the object’s own properties. It visits every enumerable property along the prototype chain. With plain object literals this rarely bites, but the moment you add to a prototype — or iterate an instance of a custom class with enumerable inherited props — the extra keys show up.

const base = { shared: "from prototype" };
const obj = Object.create(base);
obj.own = "from instance";

for (const key in obj) {
  console.log(key);
}

Output:

own
shared

To restrict the loop to the object’s own properties, guard each key with Object.prototype.hasOwnProperty. Calling it via Object.prototype (or the modern Object.hasOwn) is safer than obj.hasOwnProperty(key), because the object might itself define a property named hasOwnProperty.

for (const key in obj) {
  if (Object.hasOwn(obj, key)) {
    console.log(key); // only "own"
  }
}

Tip: Object.hasOwn(obj, key) (ES2022) is the preferred replacement for Object.prototype.hasOwnProperty.call(obj, key). It is shorter, never shadowed, and reads naturally.

A cleaner alternative: Object.keys

In most code you do not actually want prototype keys, so reaching for for...in plus a guard is busywork. Object.keys returns an array of the object’s own enumerable string keys — already filtered for you — which you can iterate with for...of.

const config = { host: "localhost", port: 8080, tls: false };

for (const key of Object.keys(config)) {
  console.log(`${key}: ${config[key]}`);
}

// Or grab key/value pairs together:
for (const [key, value] of Object.entries(config)) {
  console.log(`${key}: ${value}`);
}
ToolIteratesIncludes inherited?Returns
for...inkeysYes (enumerable)string keys, one per iteration
Object.keys()keysNo (own only)array of keys
Object.entries()key/valueNo (own only)array of [key, value] pairs
Object.getOwnPropertyNames()keysNoarray incl. non-enumerable

Why not to use it on arrays

Arrays are objects, so for...in technically “works” on them — and that is exactly the problem. It iterates the index keys as strings, in a spec order that historically was not guaranteed, and it will happily include any extra enumerable properties you (or a library) attached to the array.

const colors = ["red", "green", "blue"];
colors.tag = "primary"; // a stray property

for (const i in colors) {
  console.log(i, typeof i);
}

Output:

0 string
1 string
2 string
tag string

Two things go wrong: the indices arrive as strings ("0", not 0), and the unrelated tag property is enumerated. For ordered, value-based iteration of an array, use for...of or array methods like forEach, map, and filter.

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

for (const color of colors) {
  console.log(color); // red, green, blue — values, in order
}

Warning: Never use for...in to loop over an array’s elements. Reach for for...of (values) or colors.entries() (index + value) instead.

Best practices

  • Use for...in only to enumerate the keys of a plain object, not arrays, Maps, or Sets.
  • Guard with Object.hasOwn(obj, key) whenever inherited properties are possible.
  • Prefer Object.keys() / Object.entries() with for...of — they are own-only by default and read more clearly.
  • For arrays, use for...of, forEach, or .entries(); never for...in.
  • Remember that keys from for...in are always strings, even numeric-looking ones.
  • Avoid mutating the object inside a for...in loop; add or delete keys before or after iterating.
Last updated June 1, 2026
Was this helpful?