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.
const literal = { name: "Bob", meanGrade: 79.47, codes: true };
const arr = ["Bob", 79.47, true];
console.log(typeof literal, Array.isArray(arr));You should see one line logging object true.
Classic loops (while, do-while, for)
while
const array = ["Bob", 79.47, true];
let count = 0;
while (count < array.length) {
console.log(array[count]);
count++;
}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.
const array = [];
let count = 1;
do {
console.log("In the loop");
count++;
} while (count < array.length);You should see one line logging In the loop.
for
const array = ["Bob", 79.47, true];
for (let count = 0; count < array.length; count++) {
console.log(array[count]);
}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.
const array = ["Bob", 79.47, true];
for (const value of array) {
console.log(value);
}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.
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]);
}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.
const array = ["Bob", 79.47, true];
array.forEach((element) => {
console.log(element);
});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:
const callbackFunction = (input) => {
console.log(input);
};
callbackFunction("from callback");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.
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]);
}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)—see JavaScript object has property for why in and Object.hasOwn diverge on the prototype chain:
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]);
}
}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:
const arr = [10, 20];
arr.label = "nums";
for (const k in arr) {
console.log(k, arr[k]);
}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)
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);
}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).
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);
}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:
console.log(
[1, 2, 3, 4].some((n) => {
console.log("visit", n);
return n === 2;
}),
);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...inas a key enumerator for objects (withObject.hasOwnwhen you mean “own keys only”), not your default array value iterator. - Treat
forEachas a readable side-effect tool on arrays when you do not needbreak/continuein the surrounding control flow. - Prefer
for...ofwhen you want values with full loop semantics (includingawaitin async functions in modern engines).
References
- MDN:
for...of - MDN:
for...in - MDN:
Array.prototype.forEach - if you are persisting loop output to disk
Frequently Asked Questions
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.
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.
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.
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.
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.
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.

