Complete JavaScript Course Module 16: Advanced: Professional JavaScript

Common JavaScript Mistakes and How to Avoid Them

  • Beginner
  • Lesson 16.5
  • 7 min read
  • 75 of 76 in the course

In short

These are the most common JavaScript mistakes beginners and even experienced developers make. For each one you will see the wrong code, why it breaks, and the right way to write it. Comparison mistakes: = vs ===, NaN and comparing objects Mistake 1: Using = instead of === in a condition.

These are the most common JavaScript mistakes beginners and even experienced developers make. For each one you will see the wrong code, why it breaks, and the right way to write it.

Comparison mistakes: = vs ===, NaN and comparing objects

Mistake 1: Using = instead of === in a condition. One = assigns a value. Three === compares. An assignment inside if changes your variable and is almost always "true".

// Wrong
let role = "guest";
if (role = "admin") {             // this SETS role to "admin"
  console.log("Welcome, admin!"); // Output: Welcome, admin!  (oops)
}

// Right
let userRole = "guest";
if (userRole === "admin") {
  console.log("Welcome, admin!");
} else {
  console.log("Access denied");    // Output: Access denied
}

Also avoid loose ==, which converts types first: "1" == 1 and 0 == "" are both true. Use === everywhere.

Mistake 2: Checking for NaN with ===. NaN ("Not a Number") is the only value that is not equal to itself. So x === NaN is always false.

const price = Number("abc");          // NaN, because "abc" is not a number

// Wrong
console.log(price === NaN);           // Output: false (always!)
console.log(isNaN("hello"));          // Output: true  (old isNaN converts text first)

// Right
console.log(Number.isNaN(price));     // Output: true
console.log(Number.isNaN("hello"));   // Output: false (it is a string, not NaN)

Mistake 3: Comparing objects or arrays with ===. Objects are compared by reference: two objects are only equal if they are the very same object in memory, like two identical houses at different addresses.

// Wrong
console.log({ id: 1 } === { id: 1 }); // Output: false
console.log([1, 2] === [1, 2]);       // Output: false

// Right: compare the values you care about
const a = { id: 1, name: "Pen" };
const b = { id: 1, name: "Pen" };
console.log(a.id === b.id);                              // Output: true
console.log(JSON.stringify(a) === JSON.stringify(b));    // Output: true (simple objects, same key order)

Number mistakes: money, parseInt and sort

Mistake 4: Using decimals for money. As you saw in the numbers lesson, computers store decimals in binary, so 0.1 + 0.2 is not exactly 0.3. With money, those tiny errors add up.

// Wrong
console.log(0.1 + 0.2);              // Output: 0.30000000000000004
console.log(1.10 * 3);               // Output: 3.3000000000000003 (three items at $1.10)

// Right: work in whole cents, format only when you show the price
const priceCents = 1999;
const totalCents = priceCents * 3;   // 5997, exact
const money = new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" });
console.log(money.format(totalCents / 100)); // Output: $59.97

Mistake 5: Using parseInt without a radix. The radix is the number base (10 for normal numbers). Without it, parseInt guesses from the text, and in map it gets the array index as the base by accident.

// Wrong
console.log(["10", "10", "10"].map(parseInt)); // Output: [10, NaN, 2]
console.log(parseInt("0x1A"));                 // Output: 26 (read as hexadecimal)

// Right
console.log(["10", "10", "10"].map((s) => parseInt(s, 10))); // Output: [10, 10, 10]
console.log(["10", "10", "10"].map(Number));                 // Output: [10, 10, 10]

Mistake 6: Sorting numbers without a compare function. sort() compares items as text by default, so "100" comes before "5".

// Wrong
console.log([10, 1, 5, 100].sort());                // Output: [1, 10, 100, 5]

// Right
console.log([10, 1, 5, 100].sort((a, b) => a - b)); // Output: [1, 5, 10, 100]

Loop mistakes: off-by-one, changing arrays and var

Mistake 7: Off-by-one errors. Arrays start at index 0, so the last index is length - 1. Using <= goes one step too far.

const colors = ["red", "green", "blue"];

// Wrong
for (let i = 0; i <= colors.length; i++) console.log(colors[i]);
// Output: red, green, blue, undefined

// Right
for (let i = 0; i < colors.length; i++) console.log(colors[i]);
// Output: red, green, blue
for (const color of colors) console.log(color); // even safer: no index at all

Mistake 8: Removing items from an array while looping over it. When you splice an item, everything after it moves one place left, and the loop skips the next item.

// Wrong
const cart = ["apple", "sold-out", "sold-out", "bread"];
for (let i = 0; i < cart.length; i++) {
  if (cart[i] === "sold-out") cart.splice(i, 1);
}
console.log(cart); // Output: ["apple", "sold-out", "bread"]  (one was skipped)

// Right: build a new array with filter
const basket = ["apple", "sold-out", "sold-out", "bread"];
const available = basket.filter((item) => item !== "sold-out");
console.log(available); // Output: ["apple", "bread"]

Mistake 9: Using var in a loop with callbacks. var creates one shared variable for the whole function. By the time the timers run, the loop has finished and i is 3. let creates a fresh i for each round of the loop.

// Wrong
for (var i = 0; i < 3; i++) {
  setTimeout(() => console.log(i), 100);
}
// Output: 3, 3, 3

// Right
for (let j = 0; j < 3; j++) {
  setTimeout(() => console.log(j), 100);
}
// Output: 0, 1, 2

Function mistakes: missing await, arrow returns and this

Mistake 10: Forgetting await. Without await, you get a Promise (a "box that will contain the answer later"), not the answer itself.

// Wrong
async function loadUser() {
  const response = fetch("https://jsonplaceholder.typicode.com/users/1");
  console.log(response); // Output: Promise {<pending>}
  // response.json() would fail: TypeError: response.json is not a function
}

// Right
async function loadUserFixed() {
  const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
  const user = await response.json();
  console.log(user.name); // Output: Leanne Graham
}
loadUser();
loadUserFixed();

Mistake 11: Forgetting return in an arrow function with braces. An arrow function returns automatically only without braces. With { }, you must write return. To return an object directly, wrap it in parentheses.

const nums = [1, 2, 3];

// Wrong
console.log(nums.map((n) => { n * 2 }));     // Output: [undefined, undefined, undefined]
console.log(nums.map((n) => { value: n }));  // Output: [undefined, undefined, undefined]

// Right
console.log(nums.map((n) => n * 2));             // Output: [2, 4, 6]
console.log(nums.map((n) => { return n * 2; })); // Output: [2, 4, 6]
console.log(nums.map((n) => ({ value: n })));    // Output: [{value: 1}, {value: 2}, {value: 3}]

Mistake 12: Losing this in a callback. A normal function passed as a callback gets its own this, which is not your object. Arrow functions keep the this of the place where they were written.

// Wrong
const timer = {
  seconds: 0,
  start() {
    setTimeout(function () {
      this.seconds++;            // "this" is not timer here
      console.log(this.seconds); // Output: NaN
    }, 100);
  }
};
timer.start();

// Right
const stopwatch = {
  seconds: 0,
  start() {
    setTimeout(() => {
      this.seconds++;            // arrow function: "this" is still stopwatch
      console.log(this.seconds); // Output: 1
    }, 100);
  }
};
stopwatch.start();

Security mistake: innerHTML with user input

Mistake 13: Putting user input into innerHTML. innerHTML turns text into real HTML. If a user types HTML with JavaScript inside it, that code runs on your page. This attack is called XSS (cross-site scripting), and it can steal your users' data.

<input id="comment">
<button id="post">Post comment</button>
<p id="wrong"></p>
<p id="right"></p>

<script>
  // Pretend an attacker typed this into the comment box
  document.getElementById("comment").value = `<img src="x" onerror="alert('hacked')">`;

  document.getElementById("post").addEventListener("click", () => {
    const text = document.getElementById("comment").value;

    // Wrong: the browser runs the onerror code and shows "hacked"
    document.getElementById("wrong").innerHTML = text;

    // Right: the text is shown exactly as typed, nothing runs
    document.getElementById("right").textContent = text;
  });
</script>

When you click the button, the "wrong" paragraph runs the attacker's code and an alert pops up. The "right" paragraph simply shows the text. Use textContent for any text that came from a user, a URL or an API. If you must build HTML, create elements with document.createElement.

Tip: ESLint catches several of these mistakes for you, including = in conditions (no-cond-assign), == (eqeqeq), x === NaN (use-isnan) and missing await patterns. Turn it on in every project.

MistakeQuick fix
= in a conditionUse ===
x === NaNNumber.isNaN(x)
obj1 === obj2 for contentCompare the properties
Decimals for moneyUse whole cents and Intl.NumberFormat
parseInt(s)parseInt(s, 10) or Number(s)
numbers.sort()numbers.sort((a, b) => a - b)
i <= arr.lengthi < arr.length or for...of
splice inside a loopfilter()
var in a looplet
No awaitawait every promise you need the result of
{ } arrow with no returnRemove braces or add return
function callback losing thisUse an arrow function
innerHTML = userTexttextContent = userText

Practice exercise

This code has four mistakes. Find and fix them: const scores = ["40", "9", "100"]; const nums = scores.map(parseInt); const sorted = nums.sort(); for (let i = 0; i <= sorted.length; i++) { if (sorted[i] = 100) console.log("Perfect score!"); }. The fixed version should sort the numbers as [9, 40, 100] and print "Perfect score!" exactly once.

Hint: look for the parseInt radix, the sort compare function, the loop condition and the comparison operator.

Key takeaways

  • Use === for comparisons, Number.isNaN() for NaN, and compare objects by their properties.
  • Store money in whole cents, pass a radix to parseInt, and give sort() a compare function for numbers.
  • Use < with length, filter() instead of removing items in a loop, and let instead of var.
  • Remember await, add return in arrow functions with braces, and use arrow callbacks to keep this.
  • Never put user input into innerHTML; use textContent instead.