JavaScript is full of quirks and edge cases. Even experienced developers fall into these traps. Let's look at 7 common mistakes and how to avoid them.
1. Not Handling Promise Rejections
// BAD - Unhandled rejection
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
return response.json(); // What if fetch fails?
}
// GOOD - Always handle errors
async function fetchUser(id) {
try {
const response = await fetch(`/api/users/${id}`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
} catch (error) {
console.error("Failed to fetch user:", error);
return null;
}
}2. Comparing with == Instead of ===
// Surprising results with ==
0 == "" // true
0 == "0" // true
"" == "0" // false (wait, what?)
false == "false" // false
false == "0" // true
null == undefined // true
// Always use === for predictable behavior
0 === "" // false
0 === "0" // false3. Mutating Objects Passed as Arguments
// BAD - Mutates the original
function addDiscount(product) {
product.price *= 0.9; // This changes the original object!
return product;
}
// GOOD - Create a new object
function addDiscount(product) {
return { ...product, price: product.price * 0.9 };
}4. forEach with Async/Await
// BAD - forEach doesn't await
async function processItems(items) {
items.forEach(async (item) => {
await saveToDatabase(item); // These run in parallel, not sequentially!
});
console.log("Done!"); // This runs BEFORE saves complete
}
// GOOD - Use for...of for sequential
async function processItems(items) {
for (const item of items) {
await saveToDatabase(item);
}
console.log("Done!"); // Now this is correct
}
// GOOD - Use Promise.all for parallel
async function processItems(items) {
await Promise.all(items.map(item => saveToDatabase(item)));
console.log("Done!");
}5. Memory Leaks with Event Listeners
// BAD - Never cleaned up
function setupComponent() {
window.addEventListener("resize", handleResize);
document.addEventListener("click", handleClick);
}
// GOOD - Clean up when done
function setupComponent() {
const controller = new AbortController();
window.addEventListener("resize", handleResize, { signal: controller.signal });
document.addEventListener("click", handleClick, { signal: controller.signal });
// Clean up all listeners at once
return () => controller.abort();
}6. Floating Point Arithmetic
// The classic gotcha
0.1 + 0.2 === 0.3 // false! (it's 0.30000000000000004)
// Solution: Use epsilon comparison or integers
Math.abs(0.1 + 0.2 - 0.3) < Number.EPSILON // true
// Or work with cents (integers)
const priceInCents = 1099; // $10.997. Ignoring Optional Chaining and Nullish Coalescing
// OLD - Verbose null checks
const city = user && user.address && user.address.city
? user.address.city
: "Unknown";
// MODERN - Clean and readable
const city = user?.address?.city ?? "Unknown";
// Works with methods too
const result = api?.getData?.() ?? defaultValue;Key Takeaways
- Always use
===for comparisons - Handle async errors explicitly
- Never mutate function arguments
- Clean up event listeners and subscriptions
- Use modern syntax features for safety
No comments yet. Be the first!