HuntExams Academy logo
JavaScriptBeginner#arrays

Difference between forEach and map?

Answer

forEach runs a callback for side effects and returns undefined, so it cannot be chained. map returns a new array of results and should not be used purely for side effects.

Example

const out = [1,2].forEach(x => x*2); // undefined
const arr = [1,2].map(x => x*2);     // [2,4]

Detailed explanation

Array.prototype.forEach() and Array.prototype.map() both visit existing array elements and invoke a callback, but their purposes and return values differ. forEach() is intended for side effects, such as logging values, updating an external counter, or modifying existing objects. It discards whatever the callback returns and returns undefined itself. map() is intended for transformation: it collects each callback result into a new array and returns that array. Choose between them based on whether you need transformed data or only an action performed for each element.

For example, const out = [1, 2].forEach(x => x * 2) assigns undefined to out: the callback calculates doubled values, but forEach() discards them. In contrast, const arr = [1, 2].map(x => x * 2) produces [2, 4]. Because map() returns an array, you can chain array methods, such as values.map(x => x * 2).filter(x => x > 3). You cannot directly chain an array method onto the result of forEach(), because that result is undefined. With a block-bodied map callback, remember to write return; otherwise, the corresponding output entries will be undefined.

Both methods pass the current element, index, and original array to the callback. They determine the iteration length before processing begins and skip missing slots in sparse arrays. map() creates an output array of that length, preserving holes where input slots are absent. Neither method inherently changes the source array, but callbacks can mutate it or the objects it contains. A new array from map() does not imply a deep copy: returning an existing object preserves its reference.

In interviews, explain the intended operation before discussing syntax. Using map() only to log values creates an unnecessary result array and communicates the wrong intent. Neither method supports normal early termination with break, and neither automatically waits for promises returned by an async callback. For early exit, consider a loop, some(), or every(); for asynchronous transformations, consider Promise.all(items.map(asyncCallback)).

Key points to remember

  • forEach() discards callback results and returns undefined; use it for side effects.
  • map() returns a new array containing callback results; use it for transformations.
  • Array methods can be chained after map(), but not directly after forEach().
  • Both callbacks receive the element, index, and source array, and both methods skip sparse-array holes.
  • Neither method inherently mutates the input, supports break, or waits for async callbacks.

Common mistakes candidates make

  • Expecting return inside a forEach() callback to build a result array or stop iteration.
  • Using map() only for side effects, or forgetting return in a block-bodied transformation callback.
  • Assuming map() deep-copies objects or guarantees that callbacks cannot mutate the source data.

Likely follow-up questions

Does map() always leave the original data unchanged?

map() itself creates a new array rather than modifying the source, but its callback can mutate source elements. For objects, use a transformation such as users.map(user => ({ ...user, active: true })) to create new top-level objects; nested references are still shared.

Can I stop forEach() or map() early?

Neither supports break, and returning from the callback only ends that callback invocation. Use for...of with break, or methods such as some(), every(), or find() when their stopping conditions match your task.

What happens with async callbacks?

forEach() ignores returned promises, so awaiting the forEach() call does not wait for the callbacks to finish. map() collects the promises, allowing await Promise.all(items.map(asyncCallback)); use for...of with await when operations must run sequentially.

Interview tip: Lead with purpose and return value, demonstrate the doubled-values example, and then mention chaining and mutation.

Related Interview Questions

1
JavaScriptBeginner#arrays

Difference between slice and splice?

Open
2
JavaScriptBeginner#arrays

What do find, findIndex, some and every do?

Open
3
JavaScriptBeginner#es6

What is destructuring?

Open