What you'll be able to do
By the end of this page you should be able to walk an array with forEach when you only need a side effect, build a new array with map from each item’s return value, and avoid the habit of using map just to console.log or mutate in place.
Previous post: arrays — basics and mutation.
Who this is for
- People who write
forloops for every list walk and want the array methods that match the job - Beginners who use
mapfor printing and ignore the new array it returns - Anyone confused why
forEach“returns nothing useful”
You can skip this if you already pick map for transforms and forEach (or for...of) for side effects. Come back when a teammate asks why your map result is full of undefined.
forEach: do something with each item
forEach calls your function once per item. It does not build a new array for you. The useful work is usually a side effect: log, update the DOM, push somewhere else.
const tags = ["javascript", "tutorials"];
tags.forEach(function (tag) {
console.log(tag);
});
Scenario A — print every tag. forEach is fine; you do not need a new list.
Scenario B — update paragraphs in the page. Same idea: each call changes the outside world. That is impure on purpose.
forEach’s own return value is undefined. Do not assign const result = tags.forEach(...) and expect items.
map: build a new array from return values
map also visits each item, but it collects whatever you return into a brand-new array. The original array stays as it was (unless you mutate items inside — don’t, for learning).
const prices = [10, 20, 5];
const withTax = prices.map(function (price) {
return price * 1.1;
});
console.log(withTax); // [11, 22, 5.5]
console.log(prices); // [10, 20, 5]
Scenario A — double every score for display. map returns the new numbers; keep the source list clean.
Scenario B — turn ids into label strings. ids.map((id) => "user-" + id) gives a parallel list of strings.
If you forget return, every slot becomes undefined — a classic beginner bug.
The wrong habit: map only for side effects
// Awkward — map builds an unused array of undefined
["a", "b"].map(function (item) {
console.log(item);
});
Scenario A — “I heard map is modern.” For logging or DOM writes, prefer forEach or for...of.
Scenario B — you need both a new list and a log. Prefer map for the list; log separately, or log inside map only while debugging — the return value is still the point of map.
Rule of thumb: transform → map. Do a thing → forEach / loop.
Callback arguments you will see
Both methods pass (item, index, array). Beginners usually need item; index helps when the position matters.
["a", "b"].map(function (item, index) {
return index + ":" + item;
});
// ["0:a", "1:b"]
Scenario A — numbered labels. Use index.
Scenario B — ignore index. That is fine; omit it from the parameter list.
Arrow form works the same once you are comfortable: prices.map((p) => p * 2).
forEach cannot “return early” from the outer function the way people hope
return inside the callback only exits that one callback call, not a surrounding function. break does not work in forEach.
Scenario A — stop when you find a match. Use find, some, or a for...of loop with break (coming soon / loops you already know).
Scenario B — you only wanted side effects on every item. forEach is still fine.
A tiny practice file
Save as map-foreach.html.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>forEach and map</title>
</head>
<body>
<button id="log">forEach log</button>
<button id="double">map double</button>
<pre id="out"></pre>
<script>
const nums = [1, 2, 3];
const out = document.getElementById("out");
document.getElementById("log").addEventListener("click", function () {
const lines = [];
nums.forEach(function (n) {
lines.push("saw " + n);
});
out.textContent = lines.join("\n") + "\nnums still " + JSON.stringify(nums);
});
document.getElementById("double").addEventListener("click", function () {
const doubled = nums.map(function (n) {
return n * 2;
});
out.textContent =
"doubled " + JSON.stringify(doubled) + "\nnums still " + JSON.stringify(nums);
});
</script>
</body>
</html>
One button only logs; the other builds a new list and leaves nums alone.
Mistakes I see a lot
1. map without return. You get [undefined, undefined, ...].
2. Using map for console.log only. Prefer forEach.
3. Mutating the original inside map. Confusing; return a new value instead.
4. Expecting forEach to return a transformed array. It does not.
5. Trying to break out of forEach. Switch tools.
6. Chaining map then ignoring the result. Dead work — assign or return it.
What to try before the next post
forEachlog every item in["x","y"].mapstrings to uppercase withreturn.- Deliberately omit
returninmapand inspect the result. - Build the tiny HTML file.
Next in this series: filter, find, and findIndex — keep some items, or pick the first match.
Try this next outside the series
Arrays get easier when you see them render UI lists and move through real transforms.
- React hooks patterns — see arrays, mapping, and derived state inside everyday React code
- JSON Formatter — inspect array payloads from APIs without losing the shape