15 Common JavaScript Mistakes (and How to Avoid Them)
From == vs === to losing 'this', the async forEach trap, and floating-point surprises — here are the most common JavaScript mistakes beginners make and exactly how to avoid each one.
Most JavaScript bugs come from the same short list of traps — loose equality, losing this, the var loop, mutating while iterating, and the async forEach mistake among them. Knowing these in advance saves hours of debugging and is exactly the kind of thing interviewers probe. Here are 15 of the most common, with the fix for each.
1. Using == instead of ===
== coerces types, producing surprises like 0 == "" and "5" == 5 being true. Always use === (and !==), which compares value and type.
2. Losing this
Detach a method (pass it as a callback) and this becomes undefined:
setTimeout(obj.method, 100); // `this` is lost
setTimeout(() => obj.method(), 100); // fixed with an arrow
The real fix is understanding how this works.
3. The var loop bug
var in a loop shares one binding, so async callbacks all see the final value. Use let. See var vs let vs const.
4. forEach with await
forEach ignores the promise, so it doesn’t wait:
items.forEach(async (x) => { await save(x); }); // does NOT wait
for (const x of items) { await save(x); } // sequential
await Promise.all(items.map(save)); // parallel
5. Floating-point math
0.1 + 0.2 === 0.3 is false (it’s 0.30000000000000004). Compare within a tolerance, or use integers (cents) for money.
6. Mutating an array while iterating it
Removing items mid-loop skips elements. Iterate backward, or build a new array with filter.
7. Confusing null and undefined
undefined means “never set”; null means “intentionally empty.” Use ?? and optional chaining ?. to handle both safely.
8. Forgetting map/reduce return values
No return inside map gives an array of undefined. See array methods.
9. typeof null === "object"
A historical quirk — null reports as "object". To check for null, compare directly: x === null.
10. Reassigning a const object’s contents and expecting an error
const blocks reassignment, not mutation — const arr = []; arr.push(1) is fine. Only arr = [] throws.
11. Comparing objects with ===
{} === {} is false — objects compare by reference, not value. To compare contents, compare the relevant fields.
12. Not handling promise rejections
An unhandled rejected promise can fail silently. Always try/catch around await or .catch() a chain. See async/await.
13. NaN comparisons
NaN === NaN is false. Use Number.isNaN(x) to detect it.
14. Off-by-one and array bounds
arr[arr.length] is undefined (indices stop at length - 1). Be deliberate with loop conditions.
15. Truthy/falsy surprises
0, "", null, undefined, NaN, and false are all falsy. if (count) skips a valid 0 — check if (count != null) when zero is meaningful.
===, default to const/let over var, and understand how this is set. Those alone eliminate the majority of beginner JavaScript bugs.
forEach trap (#4) is especially sneaky because the code looks correct and often seems to work in testing, then fails under real timing. Use for...of or Promise.all instead.
Where this fits
Avoiding these is woven through the JavaScript roadmap, and most trace back to scope, this, or async.
Every one of these gotchas is pre-empted with a diagram and a fix in JavaScript in One Month and the job-ready JavaScript in Three Months — the “one gotcha per page” approach is the whole point.
Learn the traps before they bite, and you’ll debug far less.
Frequently asked questions
What is the most common JavaScript mistake?
Using == instead of ===. The loose equality operator == performs type coercion, leading to surprising results like 0 == '' being true. Always use === (strict equality), which compares value and type without coercion, to avoid a whole class of subtle bugs.
Why is 0.1 + 0.2 not equal to 0.3 in JavaScript?
Because JavaScript uses binary floating-point numbers, which cannot represent some decimals exactly. 0.1 + 0.2 gives 0.30000000000000004. To compare, check that the difference is within a tiny tolerance, or work in integers (cents instead of dollars) for money.
Why does forEach not wait for await?
Array.forEach ignores the promise returned by an async callback, so it does not wait for asynchronous work to finish. Use a for...of loop with await for sequential work, or Promise.all with map for parallel work. This is one of the most common async mistakes.
How do I avoid 'this is undefined' errors?
The error usually happens when a method is detached from its object and called plainly, often as a callback. Fix it by using an arrow function (which inherits this from its surroundings) or by binding the method with .bind(this). Understanding how this is set is the real fix.