Chapter roadmap
Direct execution with conditions and loops, handle uncertain input, and build functions that are predictable across normal and edge cases. Work through the concepts in order, type every example yourself, and keep a short debugging log that records predictions, errors, fixes, and what each fix taught you.
Learning objectives
- Use conditional branches
- Choose loops and iteration helpers
- Validate and guard inputs
- Understand scope and closures
02.1
Conditionals and truthiness
if/else selects a path based on truthiness. Falsy values include false, 0, an empty string, null, undefined, and NaN. Use explicit comparisons when zero or an empty string is valid data so truthiness does not collapse meaningful cases.
Before running the example, trace each expression and write down the expected state or result. Then run it, compare the actual result, and change one input or rule to confirm that you understand which part controls the behavior.
Exampleconst score = 88;
let grade;
if (score >= 90) grade = "A";
else if (score >= 80) grade = "B";
else grade = "Keep practicing";
console.log(grade);
ResultB
Check your understandingExplain the example in plain language, identify one boundary or failure case, and revise it so the output changes in a predictable way.
02.2
Loops and iteration
for, while, and for...of handle repeated work. for...of reads iterable values such as arrays and strings; for...in enumerates property keys and is not the normal choice for arrays. Ensure loop conditions change and state the intended first and last iterations.
Before running the example, trace each expression and write down the expected state or result. Then run it, compare the actual result, and change one input or rule to confirm that you understand which part controls the behavior.
Examplelet total = 0;
for (const value of [4, 7, 2, 9]) {
total += value;
}
console.log(total);Result22
Check your understandingExplain the example in plain language, identify one boundary or failure case, and revise it so the output changes in a predictable way.
02.3
Guard clauses and errors
A guard clause handles an invalid or completed case near the top of a function. Throwing an Error communicates that a function cannot satisfy its contract. Callers may catch expected failures and show an appropriate message instead of allowing silent bad data.
Before running the example, trace each expression and write down the expected state or result. Then run it, compare the actual result, and change one input or rule to confirm that you understand which part controls the behavior.
Examplefunction divide(a, b) {
if (!Number.isFinite(a) || !Number.isFinite(b)) {
throw new TypeError("Both values must be finite numbers");
}
if (b === 0) throw new RangeError("Cannot divide by zero");
return a / b;
}Resultdivide(10, 2) -> 5
divide(10, 0) -> RangeError
Check your understandingExplain the example in plain language, identify one boundary or failure case, and revise it so the output changes in a predictable way.
02.4
Scope and closures
let and const have block scope. A closure is a function bundled with access to the variables from the scope where it was created. Closures support private state, factories, and event handlers, but captured values should remain purposeful and easy to reason about.
Before running the example, trace each expression and write down the expected state or result. Then run it, compare the actual result, and change one input or rule to confirm that you understand which part controls the behavior.
Examplefunction makeCounter() {
let count = 0;
return () => ++count;
}
const next = makeCounter();
console.log(next(), next(), next());Result1 2 3
Check your understandingExplain the example in plain language, identify one boundary or failure case, and revise it so the output changes in a predictable way.
Chapter assignments
Complete these in order. Later assignments assume that earlier skills are working. Do not only test the sample values; design tests that challenge boundaries, missing values, incorrect types, empty collections, and other likely failures.
- A2.1
Build a score classifier that validates input and returns a structured result with grade and message.
Submit readable source code, a brief design note, and evidence from at least three tests including one boundary or invalid case.
- A2.2
Analyze an array with for...of to produce sum, average, minimum, maximum, and counts by category.
Submit readable source code, a brief design note, and evidence from at least three tests including one boundary or invalid case.
- A2.3
Create safe parse and divide functions that throw specific errors and a caller that catches and displays them.
Submit readable source code, a brief design note, and evidence from at least three tests including one boundary or invalid case.
- A2.4
Build a closure-based tracker that supports increment, decrement, reset, and reading current state.
Submit readable source code, a brief design note, and evidence from at least three tests including one boundary or invalid case.
Chapter project
Quiz Engine
Quiz Engine โ Store questions, validate answers, track score, give immediate feedback, and allow replay. Separate question presentation, answer normalization, scoring, and final reporting into testable functions.
Required process
- Write a short specification listing inputs, outputs, rules, and failure cases.
- Break the work into small functions, queries, modules, or classes appropriate to the language.
- Build the smallest working version before adding optional features.
- Test normal, boundary, empty, and invalid cases and record the results.
- Refactor names and duplication, then write a concise user guide.
Key terms
- truthy
- falsy
- branch
- iteration
- guard clause
- exception
- block scope
- closure
- factory