Even experienced developers run into subtle JavaScript quirks around equality, numbers, object references, and asynchronous callbacks. Learning these classic pitfalls ahead of time saves hours of frustrating debugging.
Most subtle bugs come from implicit type conversions, default string sorting, or accidental assignment inside conditions. Review this reference table before shipping code:
| Mistake | Surprising Behavior | Reliable Fix |
|---|---|---|
0.1 + 0.2 === 0.3 |
Returns false (0.30000000000000004) |
Calculate in integer cents or round |
[10, 2, 1].sort() |
Returns [1, 10, 2] (alphabetical) |
Pass (a, b) => a - b comparator |
if (x = 5) |
Assigns 5 instead of comparing |
Always write === in conditions |
typeof null === "object" |
Returns true on null |
Check val !== null && typeof val === "object" |
return + newline |
Returns undefined due to ASI |
Keep { or value on the return line |
Another frequent trap happens when passing an object method directly as a callback (setTimeout(user.save, 100)), which drops this and causes a TypeError. Always wrap callbacks in an arrow function or bind them explicitly.
this, and Async PitfallsWatch out for returning an object literal on the line below return, or assuming forEach waits for await inside callbacks.
// Pitfall 1: Breaking line after return returns undefined!
function buildUserGood(name) {
return {
name,
active: true,
};
}
// Pitfall 2: Numeric sort requires a comparator
const nums = [100, 25, 5];
const sorted = [...nums].sort((a, b) => a - b);
// Pitfall 3: Object.isNaN vs Number.isNaN
console.log("Safe user:", buildUserGood("Lina"));
console.log("Sorted numbers:", sorted);
console.log("Number.isNaN('abc'):", Number.isNaN("abc")); // false
push() or sort() directly: Always clone first ([...items]) or use ES2023 immutable methods like toSorted().forEach(async item => ...) expecting sequential execution: forEach ignores returned Promises and runs everything simultaneously. Use for...of with await, or Promise.all(items.map(...)).a === b: [1, 2] === [1, 2] is always false because they are distinct objects in memory. Compare lengths and individual elements.return followed by a newline return undefined?JavaScript's Automatic Semicolon Insertion treats a bare return at the end of a line as a complete return; statement, ignoring the object literal on the line below.
"5" + 3 equal "53" while "5" - 3 equals 2?The + operator performs string concatenation whenever either side is a string, whereas - only performs arithmetic and converts "5" to 5.
TypeError: Cannot read properties of undefined?Use optional chaining (user?.profile?.avatar) paired with nullish coalescing (?? "/default.png") whenever reading nested API data.
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
Experiment with the code from this lesson in our interactive playground.