Syntax Cache
BlogMethodFeaturesHow It WorksBuild a Game
  1. Home
  2. JavaScript
  3. JavaScript Loops Practice
JavaScript20 exercises

JavaScript Loops Practice

Pick the right JavaScript loop: for, for...of, for...in, while, map/filter/reduce, find/some/every. Decision table + gotchas + practice drills.

Common ErrorsQuick ReferencePractice
Warm-up1 / 2

Can you write this from memory?

Write a for loop that iterates `i` from 0 to 4 (inclusive).

On this page
  1. 1Goal → Best tool decision table
  2. 2for...of vs for...in: the critical difference
  3. Why for...in is dangerous on arrays
  4. for...in for objects (the valid use case)
  5. 3Common gotchas
  6. 1. for...in includes inherited enumerable properties
  7. 2. forEach can't break and doesn't await
  8. 3. reduce without initial value throws on empty arrays
  9. 4Classic for loop: when you need full control
  10. When to use classic for vs for...of
  11. Labeled statements: breaking out of nested loops
  12. 5while and do...while
  13. do...while: always runs at least once
  14. 6Array methods: the functional toolkit
  15. Transformation: map
  16. Filtering: filter
  17. Pipeline pattern: filter + map
  18. Short-circuit methods: find, some, every
  19. Aggregation: reduce
  20. 7Object iteration patterns
  21. Object.entries: key-value pairs
  22. Object.keys: just keys
  23. Object.values: just values
  24. 8Quick reference
  25. 9References
Goal → Best tool decision tablefor...of vs for...in: the critical differenceCommon gotchasClassic for loop: when you need full controlwhile and do...whileArray methods: the functional toolkitObject iteration patternsQuick referenceReferences

Most JavaScript bugs around "loops" aren't about syntax. They're about picking the wrong tool.

Quick decision rules:

  • for...of iterates values of iterables (arrays, strings, sets) and supports break/continue
  • for...in iterates enumerable string keys (including inherited properties). Use only for objects, with caution
  • map/filter/reduce are for pure transformations (no mutation, returns new array)
  • find/some/every are your "stop early" tools
  • forEach is for side effects only. You cannot break and it doesn't await async callbacks

These drills make your loop choice automatic.

Related JavaScript Topics
JavaScript FunctionsJavaScript Async/AwaitJavaScript Collections

Pick the loop by what you need: map/filter for transforms, find/some/every for early exit, for...of for break/continue, reduce for aggregation (always with initial value).

What you want to doBest toolWhy
Transform every itemmapPure transform, returns new array
Filter itemsfilterReturns subset matching condition
Find first matchfind / findIndexStops at first match
Check if any matchsomeStops early on first true
Check if all matcheveryStops early on first false
Need break/continuefor / for...ofArray methods can't break
Need index accessfor or for...of + .entries()Direct index control
Iterate object keys/valuesObject.entries() + for...ofPredictable, no inherited props
Aggregate to single valuereduceWith initial value
Side effects (logging, etc)forEachWhen you don't need result
Unknown iteration countwhile / do...whileCondition-based
Async iterationfor await...ofFor async iterables (see async page)

Performance note: Native loops (for, while) are marginally faster than array methods (map, forEach) in micro-benchmarks, but the difference rarely matters in practice. Choose based on readability and intent. Optimize only if profiling shows a bottleneck. If you're coming from Python, the Python vs JavaScript loops comparison covers the key differences in iteration style.


Ready to practice?

Start practicing JavaScript Loops with spaced repetition

for...of iterates values (arrays, strings, sets). for...in iterates enumerable string keys including inherited properties. Use for...of for arrays; avoid for...in on arrays entirely.

const arr = ['a', 'b', 'c'];

// for...of iterates VALUES
for (const item of arr) {
  console.log(item);  // 'a', 'b', 'c'
}

// for...in iterates KEYS (as strings!)
for (const key in arr) {
  console.log(key);   // '0', '1', '2' (strings, not numbers!)
}

Why for...in is dangerous on arrays

// Add something to Array prototype (libraries sometimes do this)
Array.prototype.customMethod = function() {};

const arr = ['a', 'b', 'c'];

for (const key in arr) {
  console.log(key);
}
// Output: '0', '1', '2', 'customMethod' (includes inherited!)

Rule: Use for...of for arrays. Use for...in only for objects when you understand the implications.

for...in for objects (the valid use case)

const config = { host: 'localhost', port: 8080 };

// Works, but includes inherited enumerable properties
for (const key in config) {
  console.log(key, config[key]);
}

// Better: Object.entries gives you key-value pairs without inheritance issues
for (const [key, value] of Object.entries(config)) {
  console.log(key, value);
}

forEach can't break and doesn't await async callbacks. reduce without an initial value throws on empty arrays. Use for...of when you need control flow or sequential async.

1. for...in includes inherited enumerable properties

const parent = { inherited: true };
const child = Object.create(parent);
child.own = 'value';

for (const key in child) {
  console.log(key);  // 'own', 'inherited' (both appear!)
}

// Filter to own properties only:
for (const key in child) {
  if (Object.hasOwn(child, key)) {
    console.log(key);  // 'own' only
  }
}

2. forEach can't break and doesn't await

// CAN'T BREAK
items.forEach(item => {
  if (item.done) return;  // This only skips current iteration!
  // break; // SyntaxError: break doesn't work here
});

// DOESN'T AWAIT
items.forEach(async item => {
  await processItem(item);  // These all fire at once!
});
console.log('Done');  // Runs immediately, doesn't wait

Fix: Use for...of for break/continue and sequential async:

for (const item of items) {
  if (item.done) break;  // Works!
  await processItem(item);  // Sequential!
}

3. reduce without initial value throws on empty arrays

// THROWS on empty array
[].reduce((sum, n) => sum + n);  // TypeError!

// SAFE with initial value
[].reduce((sum, n) => sum + n, 0);  // Returns 0

The classic for loop gives you index access, break/continue, and full control:

// Index-based iteration
for (let i = 0; i < items.length; i++) {
  console.log(i, items[i]);
}

// Iterate backwards
for (let i = items.length - 1; i >= 0; i--) {
  console.log(items[i]);
}

// Skip every other item
for (let i = 0; i < items.length; i += 2) {
  console.log(items[i]);
}

// Early exit with break
for (let i = 0; i < items.length; i++) {
  if (found(items[i])) break;
}

When to use classic for vs for...of

Use classic for when...Use for...of when...
You need the index for logicYou only need values
Iterating backwardsForward iteration
Custom step (i += 2)Every element
Modifying index mid-loopSimple iteration

Labeled statements: breaking out of nested loops

When you need to break out of an outer loop from inside an inner loop, use a label:

outer: for (let i = 0; i < matrix.length; i++) {
  for (let j = 0; j < matrix[i].length; j++) {
    if (matrix[i][j] === target) {
      console.log(`Found at [${i}][${j}]`);
      break outer;  // Breaks out of BOTH loops
    }
  }
}

Labels are rare in modern code (extracting to a function with return is often cleaner), but they're the only way to break outer loops without flags.


Use while when iteration count is unknown:

// Retry with backoff
let attempts = 0;
while (attempts < maxRetries) {
  const result = await tryOperation();
  if (result.success) break;
  attempts++;
  await sleep(2 ** attempts * 100);
}

// Process queue until empty
while (queue.length > 0) {
  const item = queue.shift();
  process(item);
}

// Read until sentinel
let line;
while ((line = readline()) !== null) {
  process(line);
}

do...while: always runs at least once

let input;
do {
  input = prompt('Enter a number > 0:');
} while (isNaN(input) || input <= 0);

Transformation: map

Returns a new array with each element transformed:

const doubled = nums.map(n => n * 2);
const names = users.map(u => u.name);

Filtering: filter

Returns elements that pass the test:

const adults = users.filter(u => u.age >= 18);
const nonEmpty = strings.filter(s => s.length > 0);

Pipeline pattern: filter + map

const activeUserNames = users
  .filter(u => u.active)
  .map(u => u.name);

Short-circuit methods: find, some, every

These stop as soon as the answer is known:

// find: first match or undefined
const admin = users.find(u => u.role === 'admin');

// findIndex: index of first match or -1
const idx = users.findIndex(u => u.role === 'admin');

// some: does ANY element pass?
const hasAdmin = users.some(u => u.role === 'admin');

// every: do ALL elements pass?
const allActive = users.every(u => u.active);

Aggregation: reduce

// Sum (always include initial value!)
const total = nums.reduce((sum, n) => sum + n, 0);

// Build object from array
const byId = users.reduce((acc, user) => {
  acc[user.id] = user;
  return acc;
}, {});

Object.entries: key-value pairs

const config = { host: 'localhost', port: 8080 };

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

Object.keys: just keys

for (const key of Object.keys(config)) {
  console.log(key);
}

Object.values: just values

for (const value of Object.values(config)) {
  console.log(value);
}

All three methods return arrays, so you can use array methods. For a printable reference of all array methods, see the JavaScript array methods cheat sheet.

const hasPort = Object.keys(config).includes('port');
const values = Object.values(config).filter(v => v != null);

PatternCode
Iterate valuesfor (const x of arr)
With indexfor (const [i, x] of arr.entries())
Object key-valuefor (const [k, v] of Object.entries(obj))
Transformarr.map(x => f(x))
Filterarr.filter(x => test(x))
First matcharr.find(x => test(x))
Any match?arr.some(x => test(x))
All match?arr.every(x => test(x))
Aggregatearr.reduce((acc, x) => ..., init)
Side effectsarr.forEach(x => sideEffect(x))
Async iterationfor await (const x of asyncIterable)

  • MDN: Loops and iteration
  • MDN: for...of
  • MDN: for...in
  • MDN: Array methods
  • MDN: Object.entries()

When to Use JavaScript Loops

  • Use Array.map() to transform every element into a new array (pure transform).
  • Use Array.filter() to keep only elements that pass a test.
  • Use Array.find()/findIndex() when you need the first match and want to stop early.
  • Use Array.some()/every() for boolean checks that stop early when possible.
  • Use for...of (or classic for) when you need break/continue, early exit, or index access.
  • Use Object.entries()/Object.keys()/Object.values() to iterate plain objects predictably.
  • Use reduce for true aggregations, but always include an initial value.

Check Your Understanding: JavaScript Loops

Prompt

Return the names of active users from an array of objects.

What a strong answer looks like

Use a readable, non-mutating pipeline: ```js const names = users .filter(u => u.active) .map(u => u.name); ``` Mention: filter/map are clear for transformations; avoid mutation for maintainability.

What You'll Practice: JavaScript Loops

Classic for loop (index-based, break/continue)while / do...while for condition-based loopsfor...of for iterable values (arrays, strings, sets)for...of + .entries() for index + value togetherfor...in for object keys (with inherited-property caution)for await...of for async iterablesObject.keys / Object.values / Object.entries for plain objectsArray.map() for transformationArray.filter() for filteringArray.reduce() for aggregation (always with initial value)Array.find()/findIndex() for "first match"Array.some()/every() for short-circuit boolean checksArray.forEach() for side effects (no break; no async await)Labeled statements for breaking nested loops

Common JavaScript Loops Pitfalls

  • for...in iterates keys as strings and includes inherited enumerable properties
  • forEach cannot be broken out of (use for/for...of or find/some/every instead)
  • forEach doesn't await async callbacks (use for...of or map + Promise.all)
  • reduce without an initial value throws on empty arrays
  • Using map for side effects (creates unused array, confuses intent)
  • Mutating an array while iterating (skipped elements, weird bugs)
  • for...in on arrays gives string keys, not numeric indices

JavaScript Loops FAQ

What's the difference between for...of and for...in?

for...of iterates values of iterables (arrays, strings, sets, maps). for...in iterates enumerable string keys, including inherited enumerable properties. Use for...of for arrays; use for...in carefully for objects only.

Why not use for...in on arrays?

It iterates keys as strings ('0', '1', '2'), not values. It also includes inherited enumerable properties and doesn't guarantee order. Use for...of or a classic for loop for arrays.

Can I break out of forEach?

No. forEach always iterates every element. Use for/for...of for break/continue, or use find/some/every which stop early when the condition is met.

How do I loop over an object's key/value pairs?

Use `Object.entries(obj)` with `for...of`: `for (const [key, value] of Object.entries(obj)) { ... }`. This gives you predictable iteration without inherited properties. For `Map` and `Set` iteration, see the [collections page](/javascript/collections).

Why does reduce sometimes throw on empty arrays?

If you omit the initial value and the array is empty, reduce can't pick a starting accumulator and throws TypeError. Always provide an initial value: `arr.reduce((acc, x) => ..., initialValue)`.

Should I use map for side effects?

No. map is for transformation and returns a new array. Using it for side effects (logging, API calls) creates a discarded array and confuses intent. Use forEach for side effects, or a for loop when you need control flow.

When should I use a while loop?

Use while when the stopping condition isn't tied to iterating a collection: polling, retries, reading until EOF, game loops, or when you don't know iteration count upfront.

Why doesn't await work inside forEach?

forEach ignores the promises returned by async callbacks. They all fire simultaneously and forEach returns undefined immediately. Use `for...of` for sequential async iteration, `for await...of` for async iterables, or `map` + `Promise.all` for parallel execution. See the [async/await page](/javascript/async) for detailed patterns.

What do find, some, and every return?

find returns the first matching element (or undefined). some returns true if any element passes the test. every returns true only if all elements pass. All three stop early when the answer is determined.

How do I get the index in a for...of loop?

Use the `.entries()` method: `for (const [index, item] of items.entries()) { ... }`. This is the modern, idiomatic pattern that combines for...of's readability with index access. No need for forEach or manual counters. Use a classic for loop only when you need to manipulate the index (skip items, iterate backwards).

JavaScript Loops Syntax Quick Reference

Classic for (index loop)
for (let i = 0; i < items.length; i++) {
  const item = items[i];
  if (done) break;  // Can break early
}
for...of (values + break)
for (const item of items) {
  if (item.done) break;
  console.log(item);
}
for...of with index (entries)
for (const [i, item] of items.entries()) {
  console.log(i, item);
}
Object.entries + for...of
for (const [key, value] of Object.entries(obj)) {
  console.log(key, value);
}
find (stop at first match)
const firstAdult = users.find(u => u.age >= 18);
const index = users.findIndex(u => u.age >= 18);
some / every (short-circuit)
const hasAdmin = users.some(u => u.role === 'admin');
const allActive = users.every(u => u.active);
filter + map (pipeline)
const names = users
  .filter(u => u.active)
  .map(u => u.name);
reduce (with initial value)
const total = nums.reduce((sum, n) => sum + n, 0);
forEach (side effects only)
items.forEach(item => {
  console.log(item);  // Side effect
});
while (condition-based)
while (queue.length > 0) {
  const item = queue.shift();
  process(item);
}
for await...of (async iterables)
for await (const chunk of readableStream) {
  await process(chunk);
}

JavaScript Loops Sample Exercises

Example 1Difficulty: 1/5

Write a do-while loop that runs while `n > 0`.

do {
} while (n > 0);
Example 2Difficulty: 2/5

How many times does the body execute?

1
Example 3Difficulty: 1/5

Fill in the keyword that starts a loop where the body runs before the condition is checked.

do

+ 17 more exercises

Also in Other Languages

Python Loops Practice: range(), enumerate(), zip(), break/continue, for-else

Start practicing JavaScript Loops

Free daily exercises with spaced repetition. No credit card required.

← Back to JavaScript Syntax Practice
Syntax Cache

Build syntax muscle memory with spaced repetition.

Product

  • Pricing
  • Our Method
  • Daily Practice
  • Design Patterns
  • Interview Prep

Resources

  • Blog
  • Compare
  • Cheat Sheets
  • Vibe Coding
  • Muscle Memory

Languages

  • Python
  • JavaScript
  • TypeScript
  • Rust
  • SQL
  • GDScript

Legal

  • Terms
  • Privacy
  • Contact

© 2026 Syntax Cache

Cancel anytime in 2 clicks. Keep access until the end of your billing period.

No refunds for partial billing periods.