JavaScript is everywhere from simple UI interactions to full-scale applications.
If you’ve worked with JavaScript long enough, you’ve probably seen code that looks correct but behaves in unexpected ways.
In this article, we’ll explore some common JavaScript bugs, why they happen, and how to avoid them in real projects.
1. Undefined vs Null Confusion
This bug happens when a variable is accessed before it has been properly initialized.It often leads to runtime errors that are confusing for beginners.
Problematic Code
let user;
console.log(user.name);
Correct Approach
let user = null;
if (user !== null) {
console.log(user.name);
}
Why This Happens
undefined means a variable exists but has no value. Accessing properties on it causes an error.
- Best Practice
Always initialize variables and check them before use.
- Using == Instead of === Using loose equality can cause unexpected results due to type coercion.
Problematic Code
0 == false; // true
Correct Approach
0 === false; // false
Why This Happens
The == operator converts values before comparison, which can lead to logical bugs.
- Best Practice
Always use === for comparisons.
Scope Issues with var
Variables declared with var are not block-scoped, which can cause unexpected behavior.
if (true) {
var count = 10;
}
console.log(count);
if (true) {
let count = 10;
}
let and const respect block scope, making code safer and easier to debug. Modern JavaScript should avoid var.
Forgetting return in Functions
Functions return undefined by default if no return statement is provided.
function add(a, b) {
a + b;
}
function add(a, b) {
return a + b;
}
Always ensure your function explicitly returns the expected value.
Asynchronous Code Misunderstanding
JavaScript does not wait for asynchronous operations unless instructed.
let data;
fetch("/api/data").then(res => {
data = res;
});
console.log(data);
async function getData() {
const res = await fetch("/api/data");
console.log(res);
}
getData();
Using async and await makes asynchronous code easier to understand and prevents timing-related bugs.
Mutating Objects or Arrays by Mistake
Objects and arrays are passed by reference, not copied.
const users = [];
const newUsers = users;
newUsers.push("Ali");
const users = [];
const newUsers = [...users, "Ali"];
Creating copies prevents unintended side effects in applications.
Accessing Undefined Object Properties
Accessing deep object properties without checks can crash your application.
console.log(user.profile.age);
console.log(user?.profile?.age);
Optional chaining safely handles missing data and avoids runtime errors.
Misusing the this Keyword
The value of this depends on how a function is called.
setTimeout(function () {
console.log(this.name);
}, 1000);
setTimeout(() => {
console.log(this.name);
}, 1000);
Arrow functions do not bind their own this, making them safer inside callbacks.
Ignoring Error Handling
Unhandled errors can stop your entire application.
JSON.parse(data);
try {
JSON.parse(data);
} catch (error) {
console.error("Invalid JSON");
}
Proper error handling makes applications more stable and user-friendly.
Conclusion
Most JavaScript bugs are not complex problems. They come from small assumptions and misunderstandings. By understanding scope, equality, asynchronous behavior, and data handling, developers can write cleaner, safer, and more predictable JavaScript code.
About the Author
Muhammad Zaheer is a JavaScript and Web Developer with a strong focus on writing clean, maintainable, and practical code for real-world applications. He enjoys breaking down complex programming concepts into simple explanations that developers can easily understand and apply in their daily work.
He actively shares knowledge, learns from the developer community, and works on modern web solutions with a focus on performance, scalability, and user experience. You can connect with him on LinkedIn
to follow his work and professional journey.
To learn more about his background, skills, and experience, visit his official team profile.
Top comments (0)