In this chapter
We'll learn to package code into functions — with parameters, arguments and return values — understand scope, see how programs take input and give output, and handle errors so bad input can't produce a wrong price.
The Problem in Real Life
The checkout page, the mobile app and the refund page all need the same pricing rules. Right now the rules live only inside the checkout page's code.
John draws a box on a sticky note and writes "price" on it. "Put the rules in here, once. Everyone who needs a price gives this box the cart, and gets a number back. Change the rules once, and every page changes with them."
Write it once, give it a good name, and use it everywhere.
John
Code Copied Into Every Page vs. One Reusable Function
Same rules, three places
Three copies of the pricing rules will slowly drift apart — and customers will see three different prices.
In and out
Pricing needs inputs (the cart, the buyer) and gives one output (the total).
Bad input must not slip through
A negative ticket count or a missing price should stop the code, not produce a strange total.
Functions, Scope and Handling Errors
A function is a named block of code that does one job. You write it once and call (run) it whenever you need that job done. You've already used functions: console.log(...) and Math.round(...) are functions someone else wrote.
- Defining a function:
function calculateTotal(cart, isStudent) { ... }creates a function calledcalculateTotal. The code inside the curly brackets is its body. - Parameters: the names inside the brackets in the definition —
cartandisStudent. They're placeholders for the inputs the function needs. - Arguments: the actual values you pass in when you call it —
calculateTotal(myCart, true). HeremyCartandtrueare the arguments; inside the function they're known ascartandisStudent. - Return value: the result the function sends back, with the
returnkeyword.return totalCents;ends the function and hands that number to whoever called it. A function withoutreturngives back nothing (undefinedin JavaScript). - Scope: where a variable can be used. A variable created inside a function (a local variable) exists only inside that function, and disappears when it ends. A variable created outside all functions (a global variable) can be used everywhere — which is convenient but risky, because any code can change it. In JavaScript,
letandconstare also limited to the curly-bracket block they're created in. Keep variables in the smallest scope that works.
| Term | Where it appears | Example |
|---|---|---|
| Parameter | In the function definition | function calculateTotal(cart, isStudent) |
| Argument | In the function call | calculateTotal(myCart, true) |
| Return value | What the call gives back | 13497 |
| Feature | Local | Global |
|---|---|---|
| Created | Inside a function or block | Outside all functions |
| Usable | Only inside that function or block | Everywhere in the program |
| Lives until | The function or block ends | The program ends |
| Risk | Low | Any code can change it by mistake |
A function: inputs in, one result out
Arguments
myCart, true
calculateTotal(cart, isStudent)
check input → add up → apply discount
Return value
13497 (cents)
Or: an error
"Ticket has no price"
const GROUP_SIZE = 5;function calculateTotal(cart, isStudent) {if (!Array.isArray(cart)) {throw new Error("Cart must be a list of tickets");}let totalCents = 0; // local: only exists inside this functionfor (const ticket of cart) {if (typeof ticket.priceCents !== "number" || ticket.priceCents < 0) {throw new Error("Ticket has no valid price");}totalCents += ticket.priceCents;}if (isStudent) {return Math.round(totalCents * 0.8);} else if (cart.length >= GROUP_SIZE) {return Math.round(totalCents * 0.9);}return totalCents;}// Using it, with error handlingtry {const total = calculateTotal(myCart, false);console.log("Total in cents:", total);} catch (error) {console.log("Could not price this order:", error.message);}
Input and output: every program takes input and produces output, just like the computers in Act 02. For a function, input is its arguments and output is its return value. For a whole program, input can come from a keyboard, a web form, a file or a network request; output can be text on a screen, a file, a web page or a response to another program. console.log is the simplest output — it prints text so you can see what's going on.
Handling errors: what if someone calls calculateTotal with a negative number of tickets, or a ticket with no price? The function shouldn't quietly return a nonsense number. Instead, it can throw an error — stop immediately and report what went wrong: throw new Error("Ticket has no price"). The code that called it can catch the error with try { ... } catch (error) { ... } and show the customer a helpful message instead of crashing. Validating input — checking it's sensible before using it — is one of the best ways to prevent bugs.
Anna moves all the pricing rules into one function, calculateTotal, with a check at the top that throws an error for bad input. The checkout page, the mobile app and the refund page now all call the same function. John reviews it and gives a thumbs-up: one place to change, one place to test.
Key Takeaway
A function packages one job under a name: parameters name its inputs, arguments are the values passed in, and return sends back the result. Variables live in a scope — keep them local. Validate inputs and throw errors for bad data, so a function never quietly returns a wrong answer.
Why This Matters
Functions are how all real software is organised — every feature in BlueTicket is built from hundreds of small functions calling each other. Good functions are easy to test (Act 17), easy to reuse, and safe because they check their input. And error handling is the difference between a customer seeing "please check your order" and a blank, broken page.
The function works. Anna opens her first pull request, and John reviews it. His comments aren't about whether it works — they're about whether the next person can understand it. That's the last skill in this Act.
