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
- Ask your code yes-or-no questions with comparison and logical operators
- Make decisions with
if, else if and else, in the right order
- Explain truthy and falsy values, and avoid the empty-field trap
- Use
switch when one value has many exact options
- Write functions that take input and return a result
- Use parameters, arguments and default values correctly
- Explain scope, and why a variable sometimes "disappears"
Yesterday your code ran straight down the page and did the same thing every time. It stored values, calculated with them and printed the result.
That is a calculator. It is not yet an application.
An application looks at what it has been given and decides what to do about it. Did the student pass? Is the password correct? Is there enough money in the wallet? Every one of those is a question with only two possible answers: yes or no.
Today you will teach JavaScript to ask those questions, to act on the answers, and then to package that logic into functions, so you write it once and use it as many times as you like.
1. Conditions: Asking Yes-or-No Questions
Definition
A condition is a question your code asks that can only be answered with true or false.
You met the answer type yesterday: a boolean. A condition is simply any expression that produces a boolean.
Real-Life Example
Picture the admissions officer at a school. For every applicant she asks two questions: "Did this person score 50 or more?" and "Has this person paid the fee?"
She does not answer "maybe". Each answer is yes or no. Then she combines them: only a person with yes AND yes is admitted.
Your code does exactly this. It asks yes-or-no questions using comparison operators, and combines the answers using logical operators.
Types of Operators for Conditions
- Comparison operators: compare two values and give back true or false.
- Logical operators: combine or flip true and false answers.
| Operator | Meaning | Kind |
=== | Equal (value and type) | Comparison |
!== | Not equal | Comparison |
> | Greater than | Comparison |
< | Less than | Comparison |
>= | Greater than or equal to | Comparison |
<= | Less than or equal to | Comparison |
&& | AND: both must be true | Logical |
|| | OR: at least one must be true | Logical |
! | NOT: flips true to false, and false to true | Logical |
Type 1: Comparison Operators
A comparison operator looks at two values and answers one question about them. Stop here for a second. Before you read the output, predict each line.
What this code doesWe want to see how each comparison operator answers one question about a value. So we store a single score and ask five different questions about it.
app.js
1
2
3
4
5
6
7
8
9
// Store the student's score
const score = 72;
// Ask five questions about the score
console.log(score > 50); // is it greater than 50?
console.log(score < 50); // is it less than 50?
console.log(score >= 72); // is it 72 or more?
console.log(score === 72); // is it exactly 72?
console.log(score !== 72); // is it anything other than 72?
OUTPUT TERMINAL
➜ webbo3 node app.js
true
false
true
true
false
Notice score >= 72 is true. "Greater than or equal to" includes the number itself. That small difference between > and >= decides whether a student who scored exactly the pass mark passes, so choose it on purpose.
Type 2: AND (&&)
&& gives back true only when both sides are true. One false answer is enough to make the whole thing false.
What this code doesBefore using && on real data, we want to see every possible combination of true and false, so the rule "both must be true" is clear.
app.js
1
2
3
4
5
// && is true only when BOTH sides are true
console.log(true && true); // both true
console.log(true && false); // right side false
console.log(false && true); // left side false
console.log(false && false); // both false
OUTPUT TERMINAL
➜ webbo3 node app.js
true
false
false
false
Now with real questions. A student needs 50 or more and a paid fee:
What this code doesNow the admission rule for real: a student is admitted only with a passing score AND a paid fee. We check both facts together.
app.js
1
2
3
4
5
6
// The two facts we know about this applicant
const score = 78;
const feePaid = false;
// Admitted only if the score passes AND the fee is paid
console.log(score >= 50 && feePaid);
OUTPUT TERMINAL
➜ webbo3 node app.js
false
The score is fine, but the fee is not paid, so the answer is false.
Type 3: OR (||)
|| gives back true when at least one side is true. It is only false when both sides are false.
What this code doesWe want to see every combination for ||, so the rule "at least one must be true" is clear before we use it.
app.js
1
2
3
4
// || is true when AT LEAST ONE side is true
console.log(true || false); // left side true
console.log(false || true); // right side true
console.log(false || false); // neither is true
OUTPUT TERMINAL
➜ webbo3 node app.js
true
true
false
A bus company gives a discount to students or anyone aged 60 and above:
What this code doesA bus company gives a discount to students OR to anyone aged 60 and above. We check whether this passenger qualifies.
app.js
1
2
3
4
5
6
// The two facts we know about this passenger
const isStudent = false;
const age = 64;
// Discount if they are a student OR aged 60 and above
console.log(isStudent || age >= 60);
OUTPUT TERMINAL
➜ webbo3 node app.js
true
This passenger is not a student, but is over 60, so they still get the discount.
Type 4: NOT (!)
! flips an answer. True becomes false, and false becomes true.
What this code doesWe want to see ! flip an answer: first on a real fact about a user, then on a plain true.
app.js
1
2
3
4
5
6
7
8
// The user has not logged in
const isLoggedIn = false;
// ! flips the answer: read it as "not logged in"
console.log(!isLoggedIn);
// Flipping true gives the opposite
console.log(!true);
OUTPUT TERMINAL
➜ webbo3 node app.js
true
false
Read !isLoggedIn out loud as "not logged in". Because the user is not logged in, "not logged in" is true.
Name Your Conditions
You can write a long condition on one line, but it quickly becomes hard to read. Give each question a name instead.
What this code doesWe want to check eligibility in a way anyone can read. So we give each question a name first, then combine the named answers.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
// The raw facts
const score = 78;
const attendance = 0.6;
// Give each question a name that says what it means
const passedTest = score >= 50;
const attendedEnough = attendance >= 0.75;
// Print each answer, then combine them
console.log('Passed test:', passedTest);
console.log('Attended enough:', attendedEnough);
console.log('Eligible:', passedTest && attendedEnough);
OUTPUT TERMINAL
➜ webbo3 node app.js
Passed test: true
Attended enough: false
Eligible: false
Important Rule
passedTest && attendedEnough says what it means. score >= 50 && attendance >= 0.75 makes the reader work it out. Naming a condition costs one line and saves every future reader, including you next month.
Overall Example
A scholarship goes to students with an average of 70 or more who attended at least 75% of classes, or to anyone who won the national quiz.
What this code doesWe want to decide a scholarship using a rule that has both AND and OR in it, grouping the parts with brackets so the order is clear.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// The facts about this student
const average = 74;
const attendance = 0.8;
const wonNationalQuiz = false;
// Name each part of the scholarship rule
const strongResult = average >= 70;
const goodAttendance = attendance >= 0.75;
// Both good results AND attendance, OR a national quiz win
const getsScholarship = (strongResult && goodAttendance) || wonNationalQuiz;
// Show the final decision
console.log('Scholarship:', getsScholarship);
OUTPUT TERMINAL
➜ webbo3 node app.js
Scholarship: true
Both parts of the first group are true, so the brackets give true. true || false is true, so this student gets the scholarship.
Try This
A cinema lets you in if you are 18 or older and you have a ticket. Write two named conditions and print whether this person can enter.
What this code doesWe want to decide whether a person can enter a cinema: they must be old enough AND have a ticket.
app.js
1
2
3
4
5
6
7
8
9
// The facts about this person
const age = 17;
const hasTicket = true;
// Name the age question
const oldEnough = age >= 18;
// They can enter only if they are old enough AND have a ticket
console.log('Can enter:', oldEnough && hasTicket);
OUTPUT TERMINAL
➜ webbo3 node app.js
Can enter: false
Now change age to 21 and predict the new output before you run it.
Picture This
Picture a checklist on a clipboard. Each line is a question with a tick or a cross. AND needs every line ticked. OR needs just one tick. NOT swaps a tick for a cross. Every condition you write is that clipboard.
2. if, else if and else: Acting on the Answer
Definition
An if statement runs a block of code only when its condition is true. else if and else give your code other paths to take when it is false.
Real-Life Example
At a traffic light, a driver makes the same decision every time: if the light is green, go. Otherwise, if it is amber, get ready to stop. Otherwise, stop.
Only one of those actions happens, and the driver checks them in order. That is exactly how if, else if and else work.
Types of if Statements
if: do something only when the condition is true.
if...else: choose between two paths.
if...else if...else: choose between many paths.
Type 1: if
The shape is always the same: a condition in round brackets, and a block in curly braces that runs only when the condition is true.
What this code doesWe want the purchase message to appear only when the wallet has enough money, and we add a final line to show what always runs.
app.js
1
2
3
4
5
6
7
8
9
10
11
// How much money is in the wallet, and what the item costs
const walletBalance = 5000;
const price = 1500;
// Only runs when there is enough money
if (walletBalance >= price) {
console.log('Purchase successful');
}
// This line always runs, after the if
console.log('Done');
OUTPUT TERMINAL
➜ webbo3 node app.js
Purchase successful
Done
When the condition is false, the block is simply skipped. Change price to 8000 and only Done is printed.
Type 2: if...else
else is the path taken when the condition is false. Exactly one of the two blocks runs.
What this code doesWe want to give one of two responses to a PIN: welcome the user when it is right, and warn them when it is wrong.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
// The PIN the user typed, and the real PIN
const enteredPin = '1234';
const correctPin = '4321';
// Compare them: exactly one of the two blocks will run
if (enteredPin === correctPin) {
// Runs when the PINs match
console.log('Welcome back');
} else {
// Runs when they do not
console.log('Wrong PIN, try again');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Wrong PIN, try again
Type 3: if...else if...else
When there are more than two outcomes, add else if branches. JavaScript checks them from top to bottom and runs the first one that is true. else catches everything that matched nothing.
What this code doesWe want to turn a score into a grade from several possible grades, checking the highest grade first.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// The score to grade
const score = 64;
// Checked from top to bottom; the first true branch wins
if (score >= 70) {
console.log('A');
} else if (score >= 60) {
console.log('B');
} else if (score >= 50) {
console.log('C');
} else {
// Runs only when nothing above matched
console.log('Fail');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
B
64 is not 70 or more, so the first branch is skipped. It is 60 or more, so the second branch runs, and JavaScript stops looking.
Common Mistake: The Order of Your Branches
Because the first true branch wins, the order is not cosmetic. Predict what this prints for a score of 95:
What this code doesWe want to see what goes wrong when the conditions are in the wrong order, with the broadest one checked first.
app.js
1
2
3
4
5
6
7
8
9
10
11
// A very high score
const score = 95;
// The broadest condition is checked FIRST here
if (score >= 50) {
console.log('C');
} else if (score >= 60) {
console.log('B');
} else if (score >= 70) {
console.log('A');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
C
Why This Happens
95 is greater than 50, and that branch is checked first, so it wins. The other branches are never looked at. When every grade comes out the same, check the order before anything else: the strictest condition goes first, the broadest one last.
Overall Example
A phone shows a different message depending on how much data you have left. This uses a condition from section 1 inside an if chain:
What this code doesWe want a phone to show the right data message for three situations, using a condition from section 1 inside the chain.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// How much data is left, and whether the bundle renews itself
const dataLeftMB = 300;
const autoRenew = false;
// Out of data completely?
if (dataLeftMB === 0) {
console.log('You are out of data');
// Low on data AND not set to renew?
} else if (dataLeftMB < 500 && !autoRenew) {
console.log('Running low: buy a bundle soon');
// Everything else
} else {
console.log('You have enough data');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Running low: buy a bundle soon
300 is not 0, so the first branch is skipped. 300 is less than 500 and auto-renew is off, so the second branch runs.
Try This
Write an if chain that gives weather advice: 30 or more prints "Stay hydrated", 20 or more prints "Nice weather", anything else prints "Take a jacket".
What this code doesWe want to give weather advice for three temperature ranges, checking the hottest range first.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
// Today's temperature in degrees Celsius
const temperature = 24;
// Hottest case first
if (temperature >= 30) {
console.log('Stay hydrated');
// Warm, but not hot
} else if (temperature >= 20) {
console.log('Nice weather');
// Anything cooler
} else {
console.log('Take a jacket');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Nice weather
Test it with 35 and with 12, and predict each output first.
Picture This
Picture a road with several turnings, each with a signpost. You drive past them in order and take the first turning whose sign matches you. Once you have turned, you never see the other signs. else is the road straight ahead for anyone who matched nothing.
3. Truthy and Falsy: What Counts as Yes
Definition
An if expects true or false, but JavaScript lets you put any value there. When it does, JavaScript converts the value to a boolean.
Values that become false are called falsy. Every other value is truthy.
Real-Life Example
A teacher asks: "Did you bring anything to hand in?" An empty hand counts as no. A hand holding anything at all counts as yes, even a sheet of paper that says "nothing".
JavaScript judges values the same way. A few "empty" values count as no. Everything else, including some things that look empty, counts as yes.
Types: Falsy and Truthy Values
To see how JavaScript judges a value, pass it to Boolean(). It shows you exactly what an if would decide.
| Falsy value | What it is |
false | The boolean false itself |
0 | The number zero |
'' | An empty string (text with nothing in it) |
null | Deliberately empty |
undefined | No value assigned yet |
NaN | "Not a Number", the result of a failed number conversion |
These six are the falsy values you will actually meet. Everything else is truthy.
Type 1: Falsy Values
What this code doesWe want to see how JavaScript judges each of the six falsy values, so we pass each one to Boolean().
app.js
1
2
3
4
5
6
7
// Boolean() shows how an if would judge each value
console.log(Boolean(false)); // the boolean false
console.log(Boolean(0)); // the number zero
console.log(Boolean('')); // an empty string
console.log(Boolean(null)); // deliberately empty
console.log(Boolean(undefined)); // never given a value
console.log(Boolean(NaN)); // a failed number conversion
OUTPUT TERMINAL
➜ webbo3 node app.js
false
false
false
false
false
false
Type 2: Truthy Values
Predict these before reading the output. Some of them surprise everyone.
What this code doesWe want to check values that look empty or look false, to see how JavaScript really judges them.
app.js
1
2
3
4
5
6
// Values that are not on the falsy list
console.log(Boolean('hello')); // text with letters
console.log(Boolean(42)); // a number that is not zero
console.log(Boolean('0')); // the TEXT "0", not the number
console.log(Boolean('false')); // the TEXT "false"
console.log(Boolean([])); // an empty array
OUTPUT TERMINAL
➜ webbo3 node app.js
true
true
true
true
true
'0' and 'false' are strings with characters in them, so they are not empty. [] is an empty array, but the array itself still exists, so it is truthy.
Using It in an if
What this code doesWe want to greet a user by name, and fall back to a general greeting when they typed no name at all.
app.js
1
2
3
4
5
6
7
8
9
10
// The user left the name box empty
const username = '';
// An empty string counts as false in an if
if (username) {
console.log(`Hello, ${username}`);
} else {
// Fallback when no name was typed
console.log('Hello, guest');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Hello, guest
The empty string is falsy, so the else branch runs. This is a very common way to check "did the user type anything?"
Common Mistake: The Empty-Field Trap
Remember from Day 1: values from a form arrive as strings, and you convert them with Number(). Now watch what happens when a user genuinely types 0:
What this code doesWe want to reject an order when the quantity box is empty. Watch what this does when the user genuinely types 0.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
// What the user typed into the quantity box
const typedQuantity = '0';
// Convert the text into a number
const quantity = Number(typedQuantity);
// Reject the order when quantity is falsy
if (!quantity) {
console.log('Please enter a quantity');
} else {
console.log(`Ordering ${quantity}`);
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Please enter a quantity
Why This Is a Bug
The user did type something. But 0 is falsy, so !quantity is true and their valid answer is rejected as if the box were empty.
When "empty" and "zero" must mean different things, test for exactly what you mean:
What this code doesWe want to fix the empty-field trap: test for an empty box directly, so a typed 0 is accepted.
app.js
1
2
3
4
5
6
7
8
9
10
// What the user typed into the quantity box
const typedQuantity = '0';
// Test for exactly what "empty" means: no text at all
if (typedQuantity === '') {
console.log('Please enter a quantity');
} else {
// Something was typed, so convert and use it
console.log(`Ordering ${Number(typedQuantity)}`);
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Ordering 0
Overall Example
Show a display name, falling back to a default when the profile has none:
What this code doesWe want to show the best available name: the nickname if there is one, the first name if not, and a general word otherwise.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// This profile has no nickname, but it has a first name
const nickname = null;
const firstName = 'Aisha';
// Use the nickname if there is one
if (nickname) {
console.log(`Welcome, ${nickname}`);
// Otherwise use the first name
} else if (firstName) {
console.log(`Welcome, ${firstName}`);
// Otherwise use a general greeting
} else {
console.log('Welcome, student');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Welcome, Aisha
Try This
Predict whether each value is truthy or falsy, then check with Boolean():
What this code doesPractise predicting: three more values to judge as truthy or falsy.
app.js
1
2
3
4
// Predict each one before you run it
console.log(Boolean(' ')); // a single space
console.log(Boolean(-5)); // a negative number
console.log(Boolean(Number('abc'))); // converting letters to a number
OUTPUT TERMINAL
➜ webbo3 node app.js
true
true
false
A single space is a character, so the string is not empty. Number('abc') fails and gives NaN, which is falsy.
Picture This
Picture a bouncer who only turns away six specific people: false, zero, the empty string, null, undefined and NaN. Everyone else walks in, even someone wearing a T-shirt that says "false".
4. switch: One Value, Many Exact Options
Definition
switch compares one value against a list of exact options and runs the code for the option that matches.
It is not a new kind of logic. It is a tidier shape for one situation that an if chain can also handle.
Real-Life Example
Dial a USSD code on your phone and you get a menu: press 1 to check your balance, 2 to buy airtime, 3 to buy data. The phone takes the one number you pressed and jumps to the matching option. Anything else gets "Invalid option".
How It Works
| Part | What it does |
switch (value) | The one value being checked |
case ...: | One exact option to compare against |
break | Stops here, so the next case does not run |
default: | Runs when no case matched, like else |
Example 1: A Basic switch
What this code doesWe want to run the menu action that matches the option the user pressed, like a USSD menu.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// The menu option the user pressed
const option = 2;
// Compare option against each case, one by one
switch (option) {
case 1:
console.log('Your balance is N2,500');
break; // stop here
case 2:
console.log('Buy airtime');
break; // stop here
case 3:
console.log('Buy data');
break; // stop here
default:
// Runs when no case matched
console.log('Invalid option');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Buy airtime
Example 2: Two Options, One Outcome
Put cases directly on top of each other when they share a result. Here, Saturday and Sunday both get the weekend rate:
What this code doesWe want Saturday and Sunday to share one price, without writing the same line twice.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// The day being priced
const day = 'sunday';
switch (day) {
// Two cases stacked together share one result
case 'saturday':
case 'sunday':
console.log('Weekend rate');
break;
case 'friday':
console.log('Friday rate');
break;
default:
// Every other day
console.log('Weekday rate');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Weekend rate
Common Mistake: Forgetting break
Without break, JavaScript does not stop at the matching case. It carries on and runs every case below it too. Predict this output:
What this code doesWe want to see what happens when break is left out of a switch.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
// The menu option the user pressed
const option = 1;
// Notice: there is no break in any case below
switch (option) {
case 1:
console.log('Check balance');
case 2:
console.log('Buy airtime');
case 3:
console.log('Buy data');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Check balance
Buy airtime
Buy data
Why This Happens
Case 1 matched, and with no break the code "falls through" into cases 2 and 3. In Example 2 the fall-through was deliberate, with nothing between the two cases. Everywhere else, a missing break is a bug.
Watch Out: switch Uses ===
switch compares with strict equality, so the type must match too. A form gives you the string '1', not the number 1:
What this code doesWe want to see why text from a form does not match a number case in a switch.
app.js
1
2
3
4
5
6
7
8
9
10
11
// A form gives you text, not a number
const typed = '1';
// switch compares with ===, so the type must match too
switch (typed) {
case 1: // this case is the NUMBER 1
console.log('Check balance');
break;
default:
console.log('Invalid option');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Invalid option
'1' === 1 is false, so nothing matches. Convert first with Number(typed), or write the cases as strings.
switch or if?
| Use | When |
switch | One value compared against several exact options |
if / else if | Ranges (score >= 70), or conditions that check different things |
Overall Example
The full USSD menu, with the typed option converted to a number first:
What this code doesWe want a complete USSD menu that works with what the phone actually sends, which is text, so we convert it first.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// The option arrives from the phone as text
const typedOption = '3';
// Convert it to a number so it can match the cases
const option = Number(typedOption);
// Jump to the matching menu item
switch (option) {
case 1:
console.log('Your balance is N2,500');
break;
case 2:
console.log('Enter airtime amount');
break;
case 3:
console.log('Choose a data bundle');
break;
default:
// Any number that is not on the menu
console.log('Invalid option, try again');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Choose a data bundle
Try This
Write a switch that turns a grade letter into a message: A prints "Excellent", B prints "Very good", C prints "Good", and anything else prints "Keep going".
What this code doesWe want to turn a grade letter into an encouraging message.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// The student's grade letter
const grade = 'B';
// One message per grade
switch (grade) {
case 'A':
console.log('Excellent');
break;
case 'B':
console.log('Very good');
break;
case 'C':
console.log('Good');
break;
default:
// Any other grade
console.log('Keep going');
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Very good
Picture This
Picture a lift. You press one button, and the lift goes to that exact floor. break is the lift stopping. Forget it and the lift keeps going past every floor below. default is the lift telling you "that floor does not exist".
5. Functions: Write It Once, Use It Everywhere
Definition
A function is a named, reusable block of code that does one job. You define it once, and you call it whenever you need that job done.
Real-Life Example
Think of a blender. You do not rebuild the blender every time you want a smoothie. You put ingredients in, press the button, and get a smoothie back.
A function is the blender. What you put in are its inputs. What comes out is its return value. And you can use it again tomorrow with different fruit.
Why You Need Functions
Without functions, the grading logic from section 2 would be copied for every single student. Change the pass mark and you would have to find and fix every copy. With a function, you fix it in one place.
The Parts of a Function
| Part | Example | Meaning |
| Keyword | function | Tells JavaScript you are defining a function |
| Name | gradeFor | What you will call it by |
| Parameters | (score) | The inputs it expects |
| Body | { ... } | The code that does the job |
| Return | return | The value it hands back |
| Call | gradeFor(64) | Actually running it |
Types of Function Syntax
- Function declaration: the classic form.
- Function expression: a function stored in a variable.
- Arrow function: a shorter way to write a function expression.
All three make a function. You will see all three in real code, so you need to read all three.
Type 1: Function Declaration
What this code doesWe want to write a welcome message once and be able to show it as many times as we like.
app.js
1
2
3
4
5
6
7
8
// Define the function: this prints nothing yet
function welcome() {
console.log('Welcome to Webbo3');
}
// Call it twice: each call runs the body once
welcome();
welcome();
OUTPUT TERMINAL
➜ webbo3 node app.js
Welcome to Webbo3
Welcome to Webbo3
Defining the function prints nothing. Each welcome() call runs the body once, which is why the line appears twice.
Most functions take input and give a value back with return:
What this code doesWe want one function that adds any two numbers and hands the answer back to us.
app.js
1
2
3
4
5
6
7
8
9
// Define a function that takes two numbers
function add(a, b) {
// Hand the sum back to whoever called it
return a + b;
}
// Call it with different arguments and print the results
console.log(add(2, 3));
console.log(add(10, 20));
OUTPUT TERMINAL
➜ webbo3 node app.js
5
30
return Is Not the Same as console.log
console.log only shows a value on screen. return hands the value back to the code that called the function, so that code can keep using it.
What this code doesWe want to prove that a returned value can be stored and used again, which console.log alone can never do.
app.js
1
2
3
4
5
6
7
8
9
10
// A function that returns twice the number it is given
function double(n) {
return n * 2;
}
// The returned value is stored in a variable
const result = double(5);
// ...and can be used in more calculations
console.log(result + 1);
OUTPUT TERMINAL
➜ webbo3 node app.js
11
double(5) became the value 10, which was stored and then used in a calculation. A console.log inside the function could never have done that.
Type 2: Function Expression
Here the function has no name of its own. It is stored in a variable, and you call it through the variable name.
What this code doesWe want to write the same kind of function a second way: stored inside a variable.
app.js
1
2
3
4
5
6
7
// Store a function in a variable
const triple = function (n) {
return n * 3;
};
// Call it through the variable's name
console.log(triple(4));
OUTPUT TERMINAL
➜ webbo3 node app.js
12
Type 3: Arrow Function
An arrow function drops the word function and uses =>. When the body is a single expression with no braces, the value is returned automatically.
What this code doesWe want to write short functions with the arrow syntax, where a single expression is returned automatically.
app.js
1
2
3
4
5
6
7
// Arrow functions: no braces means the value is returned automatically
const square = (n) => n * n;
const area = (width, height) => width * height;
// Call them like any other function
console.log(square(6));
console.log(area(3, 5));
OUTPUT TERMINAL
➜ webbo3 node app.js
36
15
Common Mistake: Forgetting return
A function with no return still runs, but it hands back undefined.
What this code doesWe want to see what a function gives back when we forget to write return.
app.js
1
2
3
4
5
6
7
8
// This function calculates a total...
function addVat(amount) {
const total = amount * 1.075;
// ...but never returns it
}
// So the call hands back nothing
console.log(addVat(1000));
OUTPUT TERMINAL
➜ webbo3 node app.js
undefined
The same trap hides inside arrow functions. With braces, the automatic return is gone:
What this code doesWe want to compare two arrow functions that look almost the same, to see why the braces matter.
app.js
1
2
3
4
5
6
7
8
9
// With braces: the arrow needs an explicit return, and has none
const withBraces = (n) => { n * 2; };
// Without braces: the value is returned automatically
const withoutBraces = (n) => n * 2;
// Compare what each one gives back
console.log(withBraces(4));
console.log(withoutBraces(4));
OUTPUT TERMINAL
➜ webbo3 node app.js
undefined
8
Why This Matters
undefined does not cause an error where the mistake is. It travels quietly through your program and breaks something far away. When a function gives you undefined, look for the missing return first.
Early Return
return leaves the function immediately. That lets you drop the else branches entirely, because once a line returns, nothing below it runs:
What this code doesWe want to grade scores with a function that leaves as soon as it knows the answer, so no else branches are needed.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
// Each return leaves the function immediately
function gradeFor(score) {
if (score >= 70) return 'A'; // stop here for 70 and above
if (score >= 60) return 'B'; // only reached below 70
if (score >= 50) return 'C'; // only reached below 60
return 'F'; // everything else
}
// Grade three different scores
console.log(gradeFor(95));
console.log(gradeFor(64));
console.log(gradeFor(31));
OUTPUT TERMINAL
➜ webbo3 node app.js
A
B
F
This pattern is called an early return. It keeps decision logic flat and easy to read.
Overall Example
Two small functions working together: one calculates, the other formats.
What this code doesWe want two small functions to work together: one does the maths, the other formats a receipt line.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Job 1: calculate the cost of one line
function lineTotal(price, quantity) {
return price * quantity;
}
// Job 2: format that line for a receipt
function receiptLine(item, price, quantity) {
// Reuse the first function to do the maths
const total = lineTotal(price, quantity);
return `${item} x${quantity}: N${total}`;
}
// Print two receipt lines
console.log(receiptLine('Data bundle', 1500, 3));
console.log(receiptLine('Airtime', 500, 2));
OUTPUT TERMINAL
➜ webbo3 node app.js
Data bundle x3: N4500
Airtime x2: N1000
receiptLine calls lineTotal to do the maths. Each function does one job, and together they do something useful.
Try This
Write a function fullName that takes a first name and a last name and returns them joined with a space. Then print it for two people.
What this code doesPractise: a function that builds a full name from a first name and a last name.
app.js
1
2
3
4
5
6
7
8
// Join a first name and a last name with a space
function fullName(first, last) {
return `${first} ${last}`;
}
// Try it for two people
console.log(fullName('Aisha', 'Bello'));
console.log(fullName('Tunde', 'Okafor'));
OUTPUT TERMINAL
➜ webbo3 node app.js
Aisha Bello
Tunde Okafor
Now rewrite it as an arrow function and check that the output is identical.
Picture This
Picture a machine on a table with a slot on top and a tray at the bottom. You drop things in the slot (the arguments), the machine does its job (the body), and the result lands in the tray (the return value). No tray means you get nothing back: undefined.
6. Parameters, Arguments and Default Values
Definition
Parameters are the names listed in a function's definition. They are placeholders. Arguments are the actual values you pass in when you call the function.
Real-Life Example
A registration form says "Name: ______" and "Course: ______". The blank lines are the parameters. What you write on them, "Aisha" and "JavaScript", are the arguments.
Some forms even pre-fill a blank for you, like "Country: Nigeria", which you can change. That is a default value.
Types
- Parameters: the placeholders in the definition.
- Arguments: the real values passed in the call.
- Default parameters: a value used when no argument is passed.
Type 1 and 2: Parameters and Arguments
What this code doesWe want to see how the values in a call fill the placeholders in the function, in order.
app.js
1
2
3
4
5
6
7
8
// name and course are the PARAMETERS (the placeholders)
function enrol(name, course) {
return `${name} is enrolled in ${course}`;
}
// The values in the calls are the ARGUMENTS, filled in order
console.log(enrol('Aisha', 'JavaScript'));
console.log(enrol('Musa', 'Data Analysis'));
OUTPUT TERMINAL
➜ webbo3 node app.js
Aisha is enrolled in JavaScript
Musa is enrolled in Data Analysis
name and course are the parameters. 'Aisha' and 'JavaScript' are the arguments. Arguments fill parameters in order: the first argument goes into the first parameter.
What If You Pass Too Few Arguments?
A parameter with no argument becomes undefined. Predict this:
What this code doesWe want to see what happens when a function is called with fewer arguments than it expects.
app.js
1
2
3
4
5
6
7
// A function that expects two numbers
function add(a, b) {
return a + b;
}
// Called with only ONE argument, so b gets nothing
console.log(add(5));
OUTPUT TERMINAL
➜ webbo3 node app.js
NaN
b was undefined, and 5 + undefined is NaN. That is exactly the problem default values solve.
Type 3: Default Parameters
Write the default straight into the parameter list. It is used only when the caller does not pass that argument.
What this code doesWe want VAT to be 7.5% unless the caller says otherwise, including when the caller asks for 0%.
app.js
1
2
3
4
5
6
7
8
9
// vat falls back to 7.5 when the caller does not pass it
function totalWithVat(amount, vat = 7.5) {
// Add the VAT percentage on top of the amount
return amount + (amount * vat / 100);
}
console.log(totalWithVat(1000)); // no vat passed: the default is used
console.log(totalWithVat(1000, 10)); // vat of 10%
console.log(totalWithVat(1000, 0)); // vat of 0%, meaning no VAT
OUTPUT TERMINAL
➜ webbo3 node app.js
1075
1100
1000
The last call matters. The caller passed 0, meaning "no VAT", and got exactly that.
Common Mistake: Using || for Defaults
You will see older code write defaults with ||. Look what it does to that same 0:
What this code doesWe want to see why the older || way of writing a default breaks when the caller passes 0.
app.js
1
2
3
4
5
6
7
8
9
// The older way of writing a default
function totalWithVat(amount, vat) {
// Use 7.5 whenever vat is falsy
vat = vat || 7.5;
return amount + (amount * vat / 100);
}
// The caller asks for 0% VAT
console.log(totalWithVat(1000, 0));
OUTPUT TERMINAL
➜ webbo3 node app.js
1075
Why This Happens
0 is falsy (section 3), so vat || 7.5 throws the 0 away and uses 7.5. A default parameter only fills in when the value is undefined, so a real 0 is kept. Use default parameters.
Overall Example
What this code doesWe want one function that covers the common case with defaults, and still lets the caller change any value.
app.js
1
2
3
4
5
6
7
8
9
10
// bundles defaults to 1, and discount defaults to 0
function bundleCost(pricePerBundle, bundles = 1, discount = 0) {
// Multiply first, then take the discount off
const total = pricePerBundle * bundles;
return total - discount;
}
console.log(bundleCost(1500)); // both defaults used
console.log(bundleCost(1500, 3)); // three bundles, no discount
console.log(bundleCost(1500, 3, 500)); // three bundles, N500 off
OUTPUT TERMINAL
➜ webbo3 node app.js
1500
4500
4000
One function, three ways to call it. The defaults cover the common case, and the caller can override any of them.
Try This
Write greet(name, greeting) where greeting defaults to "Hello". Call it once with one argument and once with two.
What this code doesPractise: a greeting that says "Hello" unless the caller chooses different words.
app.js
1
2
3
4
5
6
7
// greeting is pre-filled with 'Hello'
function greet(name, greeting = 'Hello') {
return `${greeting}, ${name}!`;
}
console.log(greet('Chidi')); // uses the default
console.log(greet('Chidi', 'Good morning')); // overrides it
OUTPUT TERMINAL
➜ webbo3 node app.js
Hello, Chidi!
Good morning, Chidi!
Picture This
Picture a form with labelled blanks. The labels are the parameters. What you write in the blanks are the arguments. A blank that comes pre-filled is a default value: it stays unless you write something else over it.
7. Scope: Why Your Variable "Disappeared"
Definition
Scope is the part of your code where a variable can be seen and used. Outside its scope, the variable simply does not exist.
Real-Life Example
Think about notices at a school. A notice on the main gate is seen by the whole school. A notice on a classroom board is seen only by that class. A note passed inside one group is seen only by that group.
Variables work the same way. Where you declare a variable decides who can see it.
Types of Scope
- Global scope: declared outside every function and block. Visible everywhere.
- Function scope: declared inside a function. Visible only inside that function.
- Block scope: declared with
let or const inside curly braces, such as an if. Visible only inside those braces.
Type 1: Global Scope
What this code doesWe want to see that a variable declared outside everything can be used both inside a function and outside it.
app.js
1
2
3
4
5
6
7
8
9
10
11
// Declared outside everything: GLOBAL scope
const schoolName = 'Webbo3 Academy';
// A function can read a global variable
function showSchool() {
console.log(schoolName);
}
// Use it from inside the function, then from outside
showSchool();
console.log(schoolName);
OUTPUT TERMINAL
➜ webbo3 node app.js
Webbo3 Academy
Webbo3 Academy
The variable lives outside the function, so both the function and the rest of the code can read it.
Type 2: Function Scope
What this code doesWe want to see where a variable declared inside a function can be used, and where it cannot.
app.js
1
2
3
4
5
6
7
8
9
10
11
function makeGreeting() {
// Declared inside the function: FUNCTION scope
const greeting = 'Hello from inside';
console.log(greeting);
}
// Works: the function uses its own variable
makeGreeting();
// Outside the function, greeting does not exist
console.log(greeting);
OUTPUT TERMINAL
➜ webbo3 node app.js
Hello from inside
ReferenceError: greeting is not defined
Inside the function, greeting works. Outside it, JavaScript has never heard of it, so the program stops with an error.
Type 3: Block Scope
What this code doesWe want to see that a variable declared inside an if block exists only within its braces.
app.js
1
2
3
4
5
6
7
8
if (true) {
// Declared inside the braces: BLOCK scope
const inside = 'only here';
console.log(inside);
}
// Outside the braces, inside does not exist
console.log(inside);
OUTPUT TERMINAL
➜ webbo3 node app.js
only here
ReferenceError: inside is not defined
The curly braces of the if are a boundary. inside exists within them and nowhere else.
Common Mistake: Declaring Inside, Using Outside
This is the most common reason a variable "disappears". The fix is to declare it one level up, before the block, with let, and only assign it inside:
What this code doesWe want to keep a result that is set inside an if, by declaring it before the block.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// The score to check
const score = 64;
// Declare result OUTSIDE the if, with no value yet
let result;
// Only assign it inside the branches
if (score >= 50) {
result = 'Pass';
} else {
result = 'Fail';
}
// It still exists here, after the if
console.log(result);
OUTPUT TERMINAL
➜ webbo3 node app.js
Pass
When You See This Error
ReferenceError: ... is not defined almost always means the same thing: you declared the variable inside a block or function and tried to use it outside. Move the declaration up one level.
Overall Example
What this code doesWe want to see all three scopes working together in one realistic function.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// Global: every function can read the pass mark
const passMark = 50;
function checkResult(name, score) {
// Function scope: exists anywhere inside checkResult
const message = `${name} scored ${score}`;
if (score >= passMark) {
// Block scope: exists only inside this if
const note = 'well done';
return `${message}: Pass, ${note}`;
}
// note is not available here, but message is
return `${message}: Fail`;
}
// Check two students
console.log(checkResult('Amaka', 72));
console.log(checkResult('Femi', 41));
OUTPUT TERMINAL
➜ webbo3 node app.js
Amaka scored 72: Pass, well done
Femi scored 41: Fail
passMark is global, so the function can read it. message belongs to the function. note belongs only to the if block, and is used only inside it.
Try This
This code breaks. Predict the error, then fix it by moving one declaration.
What this code doesPractise: this code breaks because of scope. Find out why before you fix it.
app.js
1
2
3
4
5
6
7
8
9
10
// It is nine in the morning
const hour = 9;
if (hour < 12) {
// greeting is declared inside the block
const greeting = 'Good morning';
}
// Trying to use it outside the block
console.log(greeting);
OUTPUT TERMINAL
➜ webbo3 node app.js
ReferenceError: greeting is not defined
Fixed version:
What this code doesThe fixed version: declare greeting before the block, so it can be used after it.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// It is nine in the morning
const hour = 9;
// Declare greeting before the if, so it lives outside the block
let greeting;
// Assign it inside the branches
if (hour < 12) {
greeting = 'Good morning';
} else {
greeting = 'Good afternoon';
}
// Now it can be used here
console.log(greeting);
OUTPUT TERMINAL
➜ webbo3 node app.js
Good morning
Picture This
Picture a house. What you leave in the living room (global) anyone in the house can use. What you keep in your bedroom (a function) only people in that room can see. What you put in a drawer inside the bedroom (a block) is visible only when that drawer is open.
Do This Now
Now bring everything together. Do not just read these. Write each one yourself, predict the output, and then run it.
1. Adult or Not (Very Easy)
Write isAdult(age) that returns true for 18 and above, and false otherwise.
What this code doesExercise 1: answer one yes-or-no question about an age, from inside a function.
app.js
1
2
3
4
5
6
7
8
9
// true for 18 and above, false otherwise
function isAdult(age) {
// The comparison itself is the answer, so return it directly
return age >= 18;
}
// Test one adult and one child
console.log(isAdult(21));
console.log(isAdult(15));
OUTPUT TERMINAL
➜ webbo3 node app.js
true
false
2. Delivery Fee (Simple Application)
A shop delivers free for orders of N20,000 or more. Smaller orders pay N1,500.
What this code doesExercise 2: charge a delivery fee only on orders below N20,000.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
// Work out the delivery fee for an order total
function deliveryFee(orderTotal) {
// Big orders deliver free
if (orderTotal >= 20000) {
return 0;
}
// Everything else pays the standard fee
return 1500;
}
// Test a big order and a small one
console.log(deliveryFee(25000));
console.log(deliveryFee(8000));
OUTPUT TERMINAL
➜ webbo3 node app.js
0
1500
3. Ticket Price (Combination)
Weekend tickets cost N3,000 and weekday tickets N2,000. Students get N500 off. Use a switch, a condition and a default parameter together.
What this code doesExercise 3: combine a switch, a condition and a default parameter to price a ticket.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// isStudent is false unless the caller says otherwise
function ticketPrice(day, isStudent = false) {
// The price is decided below, so start with no value
let price;
// Step 1: the base price depends on the day
switch (day) {
case 'saturday':
case 'sunday':
price = 3000;
break;
default:
price = 2000;
}
// Step 2: students get N500 off
if (isStudent) {
price = price - 500;
}
// Step 3: hand the final price back
return price;
}
console.log(ticketPrice('sunday')); // weekend, not a student
console.log(ticketPrice('sunday', true)); // weekend, student
console.log(ticketPrice('tuesday', true)); // weekday, student
OUTPUT TERMINAL
➜ webbo3 node app.js
3000
2500
1500
4. The Student Result Analyzer (Small Real-World Problem)
This is your first piece of real business logic: rules a school actually has, expressed as functions. Build it as four small functions, not one long script.
average(a, b, c) returns the average of three scores.
gradeFor(avg) returns A, B, C or F: 70 and above is A, 60 is B, 50 is C.
verdict(avg, attendance) 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.
report(name, a, b, c, attendance) puts it all together in one line.
What this code doesExercise 4: turn a school's result rules into four small functions that work together.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 1. Add the three scores and divide by three
function average(a, b, c) {
return (a + b + c) / 3;
}
// 2. Turn an average into a grade letter, strictest first
function gradeFor(avg) {
if (avg >= 70) return 'A';
if (avg >= 60) return 'B';
if (avg >= 50) return 'C';
return 'F';
}
// 3. Apply the school's pass rule
function verdict(avg, attendance) {
// Name the two conditions
const passed = avg >= 50;
const attended = attendance >= 0.75;
if (passed && attended) return 'Pass';
if (passed && !attended) return 'Withheld: attendance below 75%';
return 'Fail';
}
// 4. Put everything together into one line
function report(name, a, b, c, attendance) {
const avg = average(a, b, c);
return `${name} | average ${avg} | grade ${gradeFor(avg)} | ${verdict(avg, attendance)}`;
}
// Run the analyser for three students
console.log(report('Chidinma', 72, 66, 90, 0.9));
console.log(report('Musa', 55, 61, 58, 0.5));
console.log(report('Tunde', 30, 44, 40, 0.95));
OUTPUT TERMINAL
➜ webbo3 node app.js
Chidinma | average 76 | grade A | Pass
Musa | average 58 | grade C | Withheld: attendance below 75%
Tunde | average 38 | grade F | Fail
Extend It Tonight
Add a rule of your own: for example, a student whose average is 85 or more gets "Pass with distinction". Make it fit the existing functions without rewriting them. If one new rule forces you to change three functions, each function is doing too much. That feeling is the lesson.
Checkpoint
Q1. Why must a grade chain start at 70 rather than at 50?
Because the first true branch wins. Starting at 50 catches every passing score in the first branch, so 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 even have printed something, but it handed nothing back.
What this code doesCheck your answer: call a function that has no return.
app.js
1
2
3
4
5
6
7
// A function that does some work but has no return
function noReturn() {
const x = 5;
}
// Print what the call hands back
console.log(noReturn());
OUTPUT TERMINAL
➜ webbo3 node app.js
undefined
Q3. Why is vat = 7.5 as a default parameter safer than vat || 7.5?
A default parameter only fills in for undefined. || replaces every falsy value, so a genuine 0 would be thrown away and replaced by 7.5.
Q4. Name a value that is truthy but that beginners expect to be falsy.
The string '0', the string 'false', or an empty array [].
What this code doesCheck your answer: judge three values that beginners often get wrong.
app.js
1
2
3
4
// Three values beginners often expect to be falsy
console.log(Boolean('0')); // the text "0"
console.log(Boolean('false')); // the text "false"
console.log(Boolean([])); // an empty array
OUTPUT TERMINAL
➜ webbo3 node app.js
true
true
true
Q5. What does this switch print, and why?
Both lines, because case 2 has no break, so it falls through into case 3.
What this code doesCheck your answer: a switch where one case has no break.
app.js
1
2
3
4
5
6
7
8
9
10
11
// The menu option pressed
const option = 2;
switch (option) {
case 2:
console.log('Buy airtime');
// no break here
case 3:
console.log('Buy data');
break;
}
OUTPUT TERMINAL
➜ webbo3 node app.js
Buy airtime
Buy data
Q6. Why does this code throw an error?
total was declared with const inside the if block, so it only exists inside those braces. Declare it before the block to use it afterwards.
What this code doesCheck your answer: use a variable outside the block it was declared in.
app.js
1
2
3
4
5
6
7
if (true) {
// total only exists inside these braces
const total = 100;
}
// Trying to use it outside them
console.log(total);
OUTPUT TERMINAL
➜ webbo3 node app.js
ReferenceError: total is not defined
Tick Before You Move On
-
☐
I can write a condition with comparison operators
-
☐
I can combine conditions with &&, || and !
-
☐
I name my conditions instead of writing one long line
-
☐
I understand why the order of an if chain matters
-
☐
I know the six falsy values and the empty-field trap
-
☐
I can use switch, and I never forget break by accident
-
☐
I know the difference between return and console.log
-
☐
I can write a function in all three styles
-
☐
I use default parameters instead of ||
-
☐
I understand global, function and block scope
-
☐
I built the Student Result Analyzer as four functions
Quick Recap
A condition is a yes-or-no question, built with comparison operators and combined with &&, || and !.
An if chain runs the first branch that is true, so the strictest condition goes first.
Six values are falsy and everything else is truthy, which is why a typed 0 can be rejected by mistake.
switch compares one value against exact options, and needs break to stop.
A function is written once and called many times, and return hands its result back.
Default parameters fill in only for undefined, so they keep a real 0.
Scope decides where a variable exists: declare it at the level where you need to use it.
Final Scenario
Imagine the school asks you to automate its admission list tomorrow.
For every applicant, the program has to check a score and a payment, decide admitted or not, and print one clear line. The same rules must apply to every applicant, and when the school changes the pass mark, you should only have to change it once.
You now have every piece:
- Conditions to ask the questions.
- if and else to act on the answers.
- A function so the rules are written once.
- A default parameter for the usual case.
- A global pass mark that every call can read.
What this code doesWe want to apply one admission rule to every applicant, with the pass mark written in a single place.
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// One pass mark, read by every call
const passMark = 50;
// feePaid defaults to false when it is not passed
function admission(name, score, feePaid = false) {
// Name the score question
const passed = score >= passMark;
// Passed and paid: fully admitted
if (passed && feePaid) {
return `${name}: Admitted`;
// Passed but not yet paid
} else if (passed) {
return `${name}: Passed, awaiting fee`;
}
// Did not pass
return `${name}: Not admitted`;
}
// Three applicants
console.log(admission('Aisha', 68, true));
console.log(admission('Bayo', 74));
console.log(admission('Ngozi', 42, true));
OUTPUT TERMINAL
➜ webbo3 node app.js
Aisha: Admitted
Bayo: Passed, awaiting fee
Ngozi: Not admitted
That is the mental model to carry forward. Your code no longer just calculates. It asks questions, takes different paths depending on the answers, and keeps each rule in one named place you can call again and again.
Tomorrow, Day 3: Arrays, objects and reshaping data. Today you analysed three students by passing their scores in one at a time. Tomorrow you will hold a whole class of students in one value, and run these same functions over all of them at once.