Each snippet below is broken on purpose. Find the bug, fix it, and press Run to check. Use the methods from the Debugging chapter: read the error, log values, isolate.
1. The variable that does not exist
const userName = "Maya";
console.log("Hello, " + username); // error on purpose
Show the fix
JavaScript is case sensitive. username is not the same as userName. Use the same spelling: console.log("Hello, " + userName).
2. The loop that reads one item too many
const scores = [80, 90, 70];
let total = 0;
for (let i = 0; i <= scores.length; i++) {
total += scores[i];
}
console.log("Average:", total / scores.length);
Show the fix
The loop condition should be i < scores.length. With <=, the last pass reads scores[3], which is undefined, so total becomes NaN. The array has indexes 0, 1 and 2.
3. The sum that became text
const inputs = ["10", "20", "30"]; // imagine these came from form fields
const total = inputs.reduce((sum, value) => sum + value, 0);
console.log(total); // expected 60
Show the fix
Form values are strings, so + joins them. Convert first: sum + Number(value). Better still, convert when you read the value from the field. See Parsing Numbers.
3b. The condition that is always true
let password = "wrong";
if (password = "secret") {
console.log("Access granted");
} else {
console.log("Access denied");
}
Show the fix
= assigns, === compares. The assignment password = "secret" gives "secret", which is truthy, so access is always granted. Write password === "secret". (Never compare passwords like this in real code. This is only about the operator.)
4. The method that lost its object
const timer = {
seconds: 0,
start() {
setTimeout(function () {
this.seconds++; // error on purpose in strict mode, or a silent bug otherwise
console.log("seconds:", this.seconds);
}, 50);
}
};
timer.start();
Show the fix
A normal function used as a callback has its own this, which is not timer. Use an arrow function, which keeps the this of the surrounding method: setTimeout(() => { this.seconds++; ... }, 50). See this in JavaScript.
5. The data that was not there yet
const wait = ms => new Promise(resolve => setTimeout(() => resolve({ name: "Maya" }), ms));
async function showUser() {
const user = wait(100);
console.log(user.name); // expected Maya
}
showUser();
Show the fix
wait(100) returns a promise, not the data. Without await, user.name is undefined. Write const user = await wait(100);. See async and await.
6. The list that did not update
<ul id="list"></ul>
<button id="add">Add</button>
<script>
const items = [];
document.querySelector("#add").addEventListener("click", () => {
items.push("Item " + (items.length + 1));
console.log(items.length, "items in the array");
});
</script>
Show the fix
The array grows, but nothing draws it on the page. State changes need a render() step that updates the DOM: after items.push(...), create an <li> with textContent and append it to #list, or rebuild the list from the array.
A habit to take away
For every bug, ask three questions: What did I expect? What happened instead? What is the first place where those two differ? That is where the bug lives.