Webbo3 Level 2 · JavaScript Development · Day 2 of 20
Decisions and Functions
Teaching your code to choose, and packaging that choice into something you can use again without copying it.
By the end of today you can
- Branch with
if,else ifandelse, and know whenswitchreads better - Combine conditions with
&&,||and!without producing nonsense - Write a function that takes input and returns a result
- Explain what scope is and why a variable "disappeared"
- Build a small result analyser that applies real business rules
Yesterday your code ran straight down the page, doing the same thing every time. That is a calculator, not an application. An application looks at what it has been given and decides what to do about it, and it does that by asking questions whose answer is only ever true or false.
1. if, else if, else
The shape is always the same: a question in brackets, and a block that runs only when the answer is true.
const score = 72;
if (score >= 70) {
console.log('A');
} else if (score >= 60) {
console.log('B');
} else if (score >= 50) {
console.log('C');
} else {
console.log('Fail');
}Order is not cosmetic here
The engine stops at the first branch that is true. Put score >= 50 at the top of that chain and every score above 50 prints "C", including a 95. When your grades all come out the same, this is why: check the order before you check anything else.
Predict: what does this print, and why is it wrong?
const score = 95;
if (score >= 50) { console.log('C'); }
else if (score >= 70) { console.log('B'); }
else if (score >= 90) { console.log('A'); }It prints C. 95 is indeed greater than 50, that branch is checked first, it wins, and the rest is never looked at. Broadest condition last, narrowest first.
2. Comparison and logic
| Operator | Means | Example |
|---|---|---|
=== | Equal, type included | 5 === 5 is true |
!== | Not equal | 'a' !== 'b' is true |
> < | Greater, less | 70 > 50 is true |
>= <= | Greater or equal | 50 >= 50 is true |
&& | AND: both must be true | a && b |
\|\| | OR: either may be true | a || b |
! | NOT: flips it | !true is false |
const score = 78;
const attendance = 0.6;
const passedTest = score >= 50;
const attendedEnough = attendance >= 0.75;
console.log('passed test:', passedTest);
console.log('attended enough:', attendedEnough);
console.log('eligible:', passedTest && attendedEnough);
console.log('needs review:', passedTest && !attendedEnough);Notice what that code does with names. passedTest and attendedEnough are not necessary: the whole thing would fit on one line. But the one-line version says if (score >= 50 && attendance >= 0.75), and the named version says what those numbers mean. Naming a condition is the cheapest readability win in programming, and it is the habit that separates code a team can maintain from code only its author can read.
3. Truthy, falsy, and the empty-field trap
JavaScript will accept any value where a condition is expected, and quietly decide whether it counts as true. A short list of values are falsy. Everything else in the language is truthy, including some things that look empty.
// the falsy values you will actually meet
const falsy = [false, 0, '', null, undefined, NaN];
for (const value of falsy) {
console.log(String(value).padEnd(10), '->', Boolean(value));
}
console.log('--- but these are TRUE ---');
console.log('"0" ->', Boolean('0')); // a string, not the number zero
console.log('"false" ->', Boolean('false'));
console.log('[] ->', Boolean([])); // an empty array still existsThe bug this creates
A user leaves a quantity box empty and you check if (!quantity). Empty string is falsy, so you correctly reject it. Then a user types 0 and you reject that too, because 0 is also falsy, even though zero was a perfectly valid thing to type. When "empty" and "zero" must mean different things, test for what you actually mean: quantity === ''.
4. switch, when there are many exact matches
switch is not a different kind of logic, it is a tidier shape for one specific case: one value compared against several exact possibilities.
const day = 'saturday';
switch (day) {
case 'saturday':
case 'sunday':
console.log('Weekend rate');
break;
case 'friday':
console.log('Friday rate');
break;
default:
console.log('Weekday rate');
}Forgetting break is the classic switch bug
Without break, execution falls straight through into the next case and runs that too. Above, the missing break after case 'saturday' is deliberate: it is how two values share one outcome. Everywhere else, a missing break is an accident.
5. Functions: the real unit of work
A function is a named, reusable block that takes values in and hands a value back. Once you can write one, you stop copying code and start calling it.
function gradeFor(score) {
if (score >= 70) return 'A';
if (score >= 60) return 'B';
if (score >= 50) return 'C';
return 'F';
}
console.log(gradeFor(95), gradeFor(64), gradeFor(31));Look at what return did to the shape. Because return exits the function on the spot, the else branches disappeared entirely. That pattern, called an early return, flattens nested code and is worth reaching for deliberately.
Arrow functions
The same thing, written shorter. You will see both everywhere, so you need to read both.
const double = function (n) { return n * 2; }; // function expression
const triple = (n) => n * 3; // arrow, implicit return
const areaOf = (w, h) => w * h;
console.log(double(4), triple(4), areaOf(3, 5));An arrow with no braces returns its single expression automatically. Add braces and you must write return yourself. That is the trap:
const withBraces = (n) => { n * 2; }; // no return written
console.log(withBraces(4));It prints undefined, because the function ran and returned nothing. A function with no return returns undefined, which then travels quietly through the rest of your program until it breaks something far away from the real cause.
6. Parameters, arguments and defaults
// vat defaults to 7.5 when the caller does not supply it
function totalWithVat(amount, vat = 7.5) {
return amount + (amount * vat / 100);
}
console.log(totalWithVat(1000));
console.log(totalWithVat(1000, 0));That second call matters. Because the default only fills in for undefined, passing 0 genuinely means zero rather than falling back to 7.5. If you had written the fallback as vat = vat || 7.5 instead, passing 0 would have given you 7.5, because 0 is falsy. Default parameters are the safe way to do this.
7. Scope: why your variable disappeared
A variable declared with let or const inside { } exists only inside those braces. Outside them it does not exist at all.
function checkIn(name) {
const greeting = `Welcome, ${name}`;
if (name.length > 3) {
const note = 'long name';
console.log(greeting, '-', note);
}
// `note` does not exist out here
return greeting;
}
console.log(checkIn('Adewale'));if (true) {
const inside = 'only here';
}
console.log(inside);That error message is one you will meet often, and it almost always means the same thing: you declared the variable inside a block and tried to use it outside. Declare it one level up, before the block, and assign inside.
Do this now: the Student Result Analyzer
This is your first piece of real business logic: rules a school actually has, expressed in code. Build it as functions, not as one long script.
- A function
average(scores)that takes an array of three scores and returns the average. - A function
gradeFor(average)that returns A, B, C or F on the boundaries above. - A function
verdict(average, attendance)that returns Pass only when the average is 50 or more and attendance is 0.75 or more, Withheld when the average passes but attendance does not, and Fail otherwise. - A function
report(student)that puts it together and returns a formatted multi-line string using a template literal.
const average = (scores) => scores.reduce((sum, n) => sum + n, 0) / scores.length;
function gradeFor(avg) {
if (avg >= 70) return 'A';
if (avg >= 60) return 'B';
if (avg >= 50) return 'C';
return 'F';
}
function verdict(avg, attendance) {
const passed = avg >= 50;
const attended = attendance >= 0.75;
if (passed && attended) return 'Pass';
if (passed && !attended) return 'Withheld: attendance below 75%';
return 'Fail';
}
function report(student) {
const avg = average(student.scores);
return `${student.name} | avg ${avg.toFixed(1)} | ${gradeFor(avg)} | ${verdict(avg, student.attendance)}`;
}
console.log(report({ name: 'Chidinma', scores: [72, 65, 88], attendance: 0.9 }));
console.log(report({ name: 'Musa', scores: [55, 61, 58], attendance: 0.5 }));
console.log(report({ name: 'Tunde', scores: [30, 44, 39], attendance: 0.95 }));Extend it tonight
Add a fourth rule of your own and make it fit the existing functions without rewriting them. If adding a rule forces you to change three functions, your functions are doing too much each: that feeling is the lesson.
Checkpoint
Q1. Why did the grade chain have to start at 70 rather than 50?
Because the first true branch wins. Starting at 50 catches every passing score in the first branch and the higher grades are never reached.
Q2. A function has no `return`. What do you get when you call it?
undefined. It ran, it may have printed things, but it handed nothing back. An arrow function with braces and no return is the usual way this happens by accident.
Q3. Why is `vat = 7.5` as a default parameter safer than `vat || 7.5`?
A default parameter only fills in for undefined. || fills in for every falsy value, so a genuine 0 would be replaced by 7.5.
Q4. Name a value that is truthy but that a beginner expects to be falsy.
The string "0", the string "false", and an empty array []. All three are truthy, because they are non-empty strings and an existing object.
Tick before you move on
- ☐ I wrote the analyser as four separate functions, not one script
- ☐ I hit the ReferenceError from block scope and understood it
- ☐ I used early returns instead of nested else branches
- ☐ All three of my test students produced the right verdict
Quick recap
First true branch wins, so order the chain narrowest first · Name your conditions: passedTest reads better than score >= 50 · Eight falsy values, and 0 being one of them is the empty-field trap · A function with no return gives back undefined · let and const live only inside the braces they were declared in
Tomorrow, Day 3: arrays and objects. Real applications are mostly data being reshaped, and tomorrow is the day you learn to reshape it.