Learning Goals
3 minBy the end of this lesson you will be able to:
- Read an error message and name the file and line it came from.
- Set a breakpoint in the Sources panel and pause your code.
- Step through line by line and watch a variable's value change.
- Fix a bug you did not write, using evidence rather than guesswork.
Warm-Up · Read the Red Text
5 minYou have met several errors across Section A. Most people panic and skim them. Look at this one properly.
Uncaught TypeError: Cannot read properties of undefined (reading 'price')
at menuLine (menu.js:14:26)
at menu.js:22:15Predict together
- Which file and line should you open first?
- What exactly was
undefined— the price, or the thing holding it?
Reveal
Open menu.js at line 14, character 26. The thing that was undefined is whatever came before .price — most likely a .find() that matched nothing, exactly as in Lesson 11.
The lines below are the stack trace: line 22 called menuLine, and the trouble happened at line 14. The error already told you almost everything.
New Concept · Stop Guessing, Start Looking
12 minA mechanic with a strange engine noise does not replace parts at random. They listen, then look. Debugging is the same: gather evidence, then change one thing.
Reading an error
Every JavaScript error has three useful parts:
TypeErrorCannot read properties of undefinedmenu.js:14:26The three you will meet most in Section A:
- ReferenceError — a name that does not exist. Usually a typo, or a variable used before it was declared.
- TypeError — the value is real, but the wrong kind. You called
.toUpperCase()on a number (Lesson 3), or read a key fromundefined(Lesson 11). - SyntaxError — JavaScript could not even read your file. A missing bracket or quote. Nothing runs at all.
console.log is a real tool
Print the thing just before the line that breaks. Label it, or you will not know which log is which.
console.log('found is:', found);
console.log('typeof found:', typeof found);Print the variable, not your assumption about it. Half of all bugs are simply a value not being what you expected.
Breakpoints — pausing time
A breakpoint freezes your code on a chosen line, before it runs. While paused you can inspect every variable at that exact moment.
- Press F12 and open the Sources panel.
- Find your
.jsfile in the file tree on the left. - Click a line number. It turns blue — that is your breakpoint.
- Refresh the page. Execution stops there and the page greys out.
The four buttons
Step over F10
Run this line, then pause on the next.
Your everyday button.
Step into F11
If the line calls a function, go inside it.
Use when you suspect the function itself.
Resume (F8) continues until the next breakpoint. Step out (Shift+F11) finishes the current function and comes back out.
Scope — the panel that answers everything
While paused, the Scope panel on the right lists every variable and its value right now. Hovering over any variable in the code shows the same. No printing needed.
Why it matters
Section C brings network requests, where the data arrives from another computer and you cannot see it in your editor. React adds components re-running many times over. Both are far easier when you can pause and look — a skill that stays with you for your whole career.
Worked Example · Hunt a Real Bug
12 minFollow along in your own browser. Save this as debug.js, attach it to a page, and open it. It is meant to be broken.
// debug.js — the discount is wrong
const cart = [
{ name: 'nasi lemak', price: 5.5, qty: 2 },
{ name: 'teh tarik', price: 2.5, qty: 3 },
];
function cartTotal(items) {
let total = 0;
for (const item of items) {
total = total + item.price;
}
return total;
}
console.log('Total: RM', cartTotal(cart));Step 1 — Notice the symptom
Total: RM 8No error at all — just a wrong answer. Two nasi lemak and three teh tarik should be RM 18.50. This is the hardest kind of bug: silent.
Step 2 — Set a breakpoint
Open Sources, find debug.js, and click the line number for total = total + item.price;. Refresh the page. The code pauses.
Step 3 — Look at Scope
On the first pause, the Scope panel shows:
total: 0
item: { name: "nasi lemak", price: 5.5, qty: 2 }There is the evidence. item.qty is 2, but the line only adds item.price. The quantity is never used.
Step 4 — Confirm by stepping
Press F8 to resume to the next pass. Scope now shows:
total: 5.5
item: { name: "teh tarik", price: 2.5, qty: 3 }After one nasi lemak the total is 5.5, not 11. The bug is confirmed — not guessed.
Step 5 — Fix and verify
function cartTotal(items) {
let total = 0;
for (const item of items) {
total = total + item.price * item.qty; // quantity counts
}
return total;
}
console.log(`Total: RM ${cartTotal(cart).toFixed(2)}`);Total: RM 18.50What changed? Remove the breakpoint by clicking the blue line number again. Notice what you did not do: you never rewrote the loop hoping it would help. You looked at the values, spotted the unused key, and changed one line.
Try It Yourself
13 minUse breakpoints for all three — resist just reading the code.
Task 1 — Watch a loop count
Take your Lesson 7 loop. Put a breakpoint inside it and step through four passes with F8, writing down the counter each time.
Does it start and end exactly where you expected?
Task 2 — Step into a function
Open your Lesson 8
cinema.js. Breakpoint the line that callstotal(), then use F11 to step inside it. Watch each parameter arrive in the Scope panel.Task 3 — Break it on purpose
In your Lesson 11 file, change a
.find()so it matches nothing, then read the key from the result. Trigger the error, then write down its type, its message and its line.Now fix it with an
ifguard and confirm the error is gone.
🔥 Mini-Challenge · Three Bugs, One File
8 minImran's quiz scorer is wrong in three ways. Use breakpoints and the Scope panel — do not just stare at it.
// imran-quiz.js — buggy
const answers = [
{ q: 'Capital of Malaysia?', given: 'Kuala Lumpur', correct: 'Kuala Lumpur' },
{ q: 'Largest state?', given: 'Sabah', correct: 'Sarawak' },
{ q: 'Currency?', given: 'ringgit', correct: 'Ringgit' },
];
let score = 0;
for (let i = 1; i <= answers.length; i++) {
const row = answers[i];
if (row.given == row.correct) {
score = score + 1;
}
}
console.log('Score: ' + score + ' out of ' + answers.length);It works if: the score is 2 out of 3 — question 3 should count, because casing alone should not fail a learner.
Reveal the answer
Bug 1 — the loop starts at 1 and runs off the end.
Arrays start at 0 (Lesson 9). Starting at 1 skips question one; i <= answers.length then reaches index 3, which is undefined. Reading .given from it throws:
TypeError: Cannot read properties of undefined (reading 'given')Breakpoint the const row line and watch row in Scope — on the last pass it is plainly undefined.
Bug 2 — loose equality.
== should be === (Lesson 5). It happens to behave here, but it hides type mistakes and is banned in professional code.
Bug 3 — casing fails a correct answer.
'ringgit' and 'Ringgit' are different strings. Lower both sides before comparing, as in Lesson 5.
// imran-quiz.js — fixed
let score = 0;
for (const row of answers) { // no index to get wrong
if (row.given.toLowerCase() === row.correct.toLowerCase()) {
score = score + 1;
}
}
console.log(`Score: ${score} out of ${answers.length}`);Score: 2 out of 3One bug shouted (the TypeError), one was silent (the casing), and one was harmless today but dangerous later (==). Only the Scope panel makes the silent one obvious.
Recap
3 min- Every error names a type, a message and a file:line. Read all three.
- ReferenceError = no such name · TypeError = wrong kind of value · SyntaxError = unreadable file.
- Label your
console.logs, and print the variable rather than your assumption. - A breakpoint pauses before the highlighted line runs.
- F10 step over · F11 step into · F8 resume.
- The Scope panel shows every variable's value at that instant.
- A wrong answer with no error is the hardest bug — and where breakpoints help most.
New words
- Breakpoint — a marker that pauses code on a line.
- Stack trace — the list of calls that led to an error.
- Scope panel — DevTools' live list of variables while paused.
- Step over / into — run the next line, or go inside the function it calls.
📦 Homework
4 min to brief · ~20 min to doRequired
- Take any file from Section A and deliberately introduce three bugs: one ReferenceError, one TypeError and one silent wrong answer.
- Swap files with a classmate. Find their three bugs using breakpoints, and write one sentence per bug saying how you found it — not just what it was. Bring your notes next lesson.
Optional stretch
- Right-click a breakpoint and choose Edit breakpoint → Conditional. Set it to pause only when
i === 2. Why is that useful in a long loop? - Try
debugger;as a line in your code. What does it do when DevTools is open — and when it is closed?
End of Section A
You now have every piece of core JavaScript: values, decisions, loops, functions, arrays and objects — plus the means to find out why something is wrong. Section B puts it on the page, starting with the DOM.