JavaScript forEach vs for...in (and when to use for...of)

Compare forEach, for...in, and for...of in JavaScript: array methods vs enumerable keys, sparse arrays, Object.hasOwn, and break/continue. Short notes after snippets describe expected console output.

Published

Updated

Read time 7 min read

Reviewed byDeepak Prasad

JavaScript forEach vs for...in (and when to use for...of)

Tested on: Node.js v20.18.2. A short note after each runnable snippet describes what you should see in the console.

People often mix up JavaScript each vs in because forEach and for...in both sound like “loop over something,” yet they answer different questions: values with an array API versus enumerable property names (often keys on objects, or string indices on arrays). This guide walks from basic loops to foreach in JavaScript-style APIs, then compares javascript foreach vs for-style control flow, with short expected-output notes after each snippet.

For a compact field guide (including Object.hasOwn and sparse-array notes), start with JavaScript forEach vs for...in and return here for the longer narrative.


forEach vs for...in vs for...of (quick map)

Construct Typical target You get Easy break / continue?
array.forEach(fn) Arrays (and some other typed collections) Elements via callback (value, index, arr) No (use some/find/for...of if you must stop early)
for (const k in obj) Objects (and arrays as objects) Enumerable string keys k (may include extras on arrays) Yes
for (const v of iterable) Iterables (Array, Map, Set, String, …) Values from the iterator protocol Yes

Official framing: MDN contrasts for...of and for...in as values vs enumerable keys. Array.prototype.forEach is a method, not a statement.


Primitives vs objects (why “in” targets keys)

Modern JavaScript distinguishes primitive values (e.g. string, number, boolean, bigint, symbol) from objects (including plain objects, arrays, functions). Arrays are objects with numeric string keys ("0", "1", …) and a special length. That is why a key loop and a value loop behave differently on the same array.

javascript
const literal = { name: "Bob", meanGrade: 79.47, codes: true };
const arr = ["Bob", 79.47, true];

console.log(typeof literal, Array.isArray(arr));
Output

You should see one line logging object true.


Classic loops (while, do-while, for)

while

javascript
const array = ["Bob", 79.47, true];

let count = 0;

while (count < array.length) {
  console.log(array[count]);
  count++;
}
Output

You should see 3 lines, in order: Bob, 79.47, true.

do...while

The body runs at least once, even if the condition is false afterward.

javascript
const array = [];
let count = 1;

do {
  console.log("In the loop");
  count++;
} while (count < array.length);
Output

You should see one line logging In the loop.

for

javascript
const array = ["Bob", 79.47, true];

for (let count = 0; count < array.length; count++) {
  console.log(array[count]);
}
Output

You should see 3 lines, in order: Bob, 79.47, true.

These index loops are still useful when you need break, continue, or a custom step.


for...of (values from an iterable)

for...of is usually what you want for “foreach” in JavaScript over array values when you are not chaining a collection method.

javascript
const array = ["Bob", 79.47, true];

for (const value of array) {
  console.log(value);
}
Output

You should see 3 lines, in order: Bob, 79.47, true.


for...in (enumerable keys on an object)

for...in walks enumerable string keys on an object (own and inherited unless you filter). On arrays, keys are indices like "0", "1", … plus any enumerable properties you attach to the array object.

javascript
console.log("...Object...");

const literal = { name: "Bob", meanGrade: 79.47, codes: true };

for (const key in literal) {
  console.log(literal[key]);
}

console.log("\n...Array...");

const array = ["Lorem", 89.67, false];

for (const key in array) {
  console.log(array[key]);
}
Output

You should see 8 lines, in order: ...Object..., Bob, 79.47, true, ...Array..., Lorem, 89.67, false.


Higher-order methods and forEach

A higher-order function accepts another function. Array.prototype.forEach is the built-in “run this callback for each element” API—handy for side effects when you already have an array.

javascript
const array = ["Bob", 79.47, true];

array.forEach((element) => {
  console.log(element);
});
Output

You should see 3 lines, in order: Bob, 79.47, true.

The callback is just a function value—here is the same idea with a named function you could pass to forEach:

javascript
const callbackFunction = (input) => {
  console.log(input);
};

callbackFunction("from callback");
Output

You should see one line logging from callback.


Side-by-side: forEach vs for...in on a dense array

On a normal dense array, both walk the same underlying element order: forEach visits values via the method API, while for...in visits index keys and you read array[index] yourself.

javascript
const array = ["Bob", 79.47, true];

console.log("forEach:");
array.forEach((element) => console.log(element));

console.log("for...in:");
for (const index in array) {
  console.log(array[index]);
}
Output

You should see 8 lines, in order: forEach:, Bob, 79.47, true, for...in:, Bob, 79.47, true.


Pitfall 1 — for...in sees inherited enumerable keys

Use Object.hasOwn(obj, key) when you only want an object’s own keys (still includes custom own properties like arr.label, but skips inherited enumerables):

javascript
const parent = { inherited: 99 };
const child = Object.create(parent);
child.own = "a";

console.log("for...in (shows inherited enumerable):");
for (const k in child) {
  console.log(k, child[k]);
}

console.log("Object.hasOwn filter:");
for (const k in child) {
  if (Object.hasOwn(child, k)) {
    console.log(k, child[k]);
  }
}
Output

You should see 5 lines, in order: for...in (shows inherited enumerable):, own a, inherited 99, Object.hasOwn filter:, own a.


Pitfall 2 — extra properties on an array object

Because arrays are objects, for...in can surface non-index enumerable properties you added:

javascript
const arr = [10, 20];
arr.label = "nums";

for (const k in arr) {
  console.log(k, arr[k]);
}
Output

You should see 3 lines, in order: 0 10, 1 20, label nums.

For “values only,” prefer for...of or forEach.


Pitfall 3 — sparse arrays (foreach js / forin javascript behavior)

javascript
const sparse = ["a", , "c"];

console.log("forEach:");
sparse.forEach((x, i) => console.log(i, x));

console.log("for...in:");
for (const k in sparse) {
  console.log(k, sparse[k]);
}

console.log("for...of:");
for (const v of sparse) {
  console.log(v);
}
Output

You should see 10 lines, in order: forEach:, 0 a, 2 c, for...in:, 0 a, 2 c, for...of:, a, undefined, c.

forEach and for...in skip the missing index; for...of still yields undefined for the hole because it walks the array length.


Practical pattern — user list (mock API data, no network)

Older tutorials used fetch plus screenshots; here is the same lesson with mock data in the shape of the public reqres users payload—so the output is reproducible offline. On a dense array, forEach and for...in print the same avatar URLs in the same order (because for...in walks indices 0…n-1 in order).

javascript
const users = [
  { id: 1, avatar: "https://reqres.in/img/faces/1-image.jpg" },
  { id: 4, avatar: "https://reqres.in/img/faces/4-image.jpg" },
  { id: 2, avatar: "https://reqres.in/img/faces/2-image.jpg" },
  { id: 6, avatar: "https://reqres.in/img/faces/6-image.jpg" },
  { id: 3, avatar: "https://reqres.in/img/faces/3-image.jpg" },
  { id: 5, avatar: "https://reqres.in/img/faces/5-image.jpg" },
];

console.log("--- forEach (element order) ---");
users.forEach((u) => console.log(u.avatar));

console.log("--- for...in (index order on dense array) ---");
for (const i in users) {
  console.log(users[i].avatar);
}
Output

You should see 14 lines of console output in print order, starting with --- forEach (element order) ---.

If you mix in fs.appendFile (async) inside forEach, disk order can race even when the in-memory array order is clear—use appendFileSync, for...of with await, or Promise.all when file ordering must match visitation order.


JavaScript for vs forEach — control flow and short-circuiting

forEach cannot break out of the outer loop; return inside the callback only exits that invocation. To stop early on arrays, use a for / for...of loop, or a method like some:

javascript
console.log(
  [1, 2, 3, 4].some((n) => {
    console.log("visit", n);
    return n === 2;
  }),
);
Output

You should see 3 lines, in order: visit 1, visit 2, true.


Takeaways

Default to for...of for values, use forEach for side effects without early exit, and reserve for...in for object keys (with Object.hasOwn when inheritance matters).

  • Treat for...in as a key enumerator for objects (with Object.hasOwn when you mean “own keys only”), not your default array value iterator.
  • Treat forEach as a readable side-effect tool on arrays when you do not need break/continue in the surrounding control flow.
  • Prefer for...of when you want values with full loop semantics (including await in async functions in modern engines).

References


Frequently Asked Questions

1. In JavaScript, what is the difference between forEach and for...in?

forEach is an Array method: it visits elements (with an index argument) using a callback and does not give you the language-level break/continue of a for loop. for...in enumerates enumerable string keys of an object—including array indices as strings and any extra enumerable properties you added to an array—so it is a key loop, not a dedicated "array values" API.

2. Should I use for...in to loop an array in JavaScript?

Usually no. Prefer for...of for values, or forEach/map/filter when a collection method fits. If you truly need keys, consider arr.keys() or a classic index loop; if you use for...in on objects, combine with Object.hasOwn to skip inherited enumerable properties you do not want.

3. JavaScript foreach vs for: which supports break?

A classic for (or for...of / for...in) supports break and continue in the loop body. Array.prototype.forEach always runs the callback for each visited element (no early exit); to short-circuit, use a for loop, for...of with a guard, or methods like some/find.

4. Does forEach skip holes in sparse arrays?

Yes. In modern V8/Node, Array.prototype.forEach skips empty slots, similar to for...in over indices. for...of still produces undefined for a hole because it walks the array length.

5. Is for each in JavaScript the same as forEach?

No. "For each" informally means iterate everything, but forEach is specifically the ES5 Array method. for...of is the ES6 value loop, and for...in is the key/enumeration loop—three different mechanisms.

6. Where can I read a shorter cheat sheet on the same topic?

See the companion page javascript-each-vs-in on this site for a compact, Node-tested summary.
Steve Alila

Specializes in web design, WordPress development, and data analysis, with proficiency in Python, JavaScript, and data extraction tools. Additionally, he excels in web API development, AI integration, …