javascript · common mistakes
Mistakes almost every beginner makes at least once — with the fix and the reasoning, not just the answer.
if (age == "18") {
console.log("Adult");
}
if (age === 18) {
console.log("Adult");
}
Why: == coerces types before comparing, so "18" == 18 is true. === compares value and type, avoiding surprising bugs.
const user = { name: "Alice" };
// "this can't change, it's const!"
user.name = "Bob"; // ...but it does
const user = Object.freeze({ name: "Alice" });
user.name = "Bob"; // silently ignored (fails in strict mode)
Why: const only locks the variable binding, not the object's contents. Use Object.freeze() if you need real immutability.
const timer = {
seconds: 0,
start() {
setInterval(function () {
this.seconds++; // `this` is not `timer` here
}, 1000);
}
};
const timer = {
seconds: 0,
start() {
setInterval(() => {
this.seconds++; // arrow fn keeps `this`
}, 1000);
}
};
Why: Regular function expressions get their own this. Arrow functions inherit this from where they're defined.
let cart = ["pen"];
function addItem(items) {
items.push("book"); // mutates the original
}
addItem(cart);
let cart = ["pen"];
function addItem(items) {
return [...items, "book"]; // returns a new array
}
cart = addItem(cart);
Why: Arrays are reference types — passing one into a function and mutating it changes the original everywhere else it's used. Prefer returning new arrays.
async function getUser() {
const res = fetch("/api/user"); // missing await
console.log(res.json); // undefined
}
async function getUser() {
const res = await fetch("/api/user");
const data = await res.json();
console.log(data);
}
Why: Without await, res is a pending Promise object, not the resolved value — a very common source of undefined bugs.
console.log(0.1 + 0.2 === 0.3); // false
const isClose = Math.abs((0.1 + 0.2) - 0.3) < Number.EPSILON;
console.log(isClose); // true
Why: Floating-point math can't represent 0.1 or 0.2 exactly in binary, so tiny rounding errors creep in. Compare with a small tolerance instead of ===.
const doubled = [1, 2, 3].map((n) => {
n * 2; // no return — result is undefined
});
const doubled = [1, 2, 3].map((n) => n * 2); // implicit return
Why: Arrow functions with a block body { } need an explicit return. Without it, every element becomes undefined.
function setName() {
username = "Alice"; // no let/const/var
}
setName();
console.log(username); // works — but shouldn't
function setName() {
let username = "Alice";
}
setName();
console.log(username); // ReferenceError (correct)
Why: Forgetting a declaration keyword silently creates a global variable in non-strict mode, which can clash with other code. Always declare with let/const.
fetch("/api/data")
.then((res) => res.json())
.then((data) => render(data));
// no .catch — errors vanish silently
fetch("/api/data")
.then((res) => res.json())
.then((data) => render(data))
.catch((err) => showError(err));
Why: An unhandled rejected Promise fails silently in the console (or crashes in Node). Always add a .catch() or wrap await in try/catch.
const items = ["a", "b", "c"];
for (let i = 1; i <= items.length; i++) {
console.log(items[i]); // skips "a", ends undefined
}
const items = ["a", "b", "c"];
for (let i = 0; i < items.length; i++) {
console.log(items[i]);
}
Why: Array indices are zero-based and the last valid index is length - 1. Starting at 1 or using <= reads past the end.