Functions, Parameters and Closures
Why functions exist
Name a behaviour once, use it everywhere
// The behaviour: turn a raw name into a display name.
function displayName(first, last) {
const clean = first.trim() + " " + last.trim();
return clean.toUpperCase();
}
console.log(displayName(" ada ", "lovelace ")); // ADA LOVELACE
console.log(displayName("grace", "hopper")); // GRACE HOPPER
The anatomy: function starts the declaration; displayName is the name;
(first, last) are parameters — named placeholders for values that
arrive later; the body runs on each call; return hands the result back and
stops the function immediately.
The words parameter and argument
The parameter is the placeholder in the definition (first); the
argument is the actual value in the call (" ada "). Mixing the
words is harmless; mixing the order is not — arguments arrive in order.
Three ways to write one
Declaration, expression, arrow
// 1. Declaration — hoisted: callable even from lines above it.
function add(a, b) { return a + b; }
// 2. Function expression — a function stored like any value (NOT hoisted).
const multiply = function (a, b) { return a * b; };
// 3. Arrow — short; no braces means "return this automatically".
const square = n => n * n;
console.log(add(2, 3), multiply(2, 3), square(4)); // 5 6 16
Style: declarations for named top-level operations; arrows for the small functions you hand to
addEventListener, map and friends. One arrow difference matters later:
an arrow's this is inherited, not set (lesson 12).
Defaults and rest
function greet(name, greeting = "Hello") {
return `${greeting}, ${name}!`;
}
console.log(greet("Ada")); // Hello, Ada! — the default kicked in
console.log(greet("Ada", "Hi")); // Hi, Ada!
function total(first, ...others) { // ...others gathers the rest into an array
return first + others.reduce((sum, n) => sum + n, 0);
}
console.log(total(1, 2, 3, 4)); // 10
Return semantics
return stops the function — and undefined is the default
function firstEven(numbers) {
for (const n of numbers) {
if (n % 2 === 0) return n; // exits HERE, with the value
}
return "none"; // only reached when nothing matched
}
function noReturn() {
const x = 1 + 1; // computed, then dropped
}
console.log(noReturn()); // undefined — the lesson 01 mystery, solved
Every function returns something: the value of its return, or
undefined. That is why the console echoed undefined after your first
console.log line — console.log itself returns nothing.
Scope inside a function
Locals die with the call
const rate = 1.2; // outer
function convert(euro) {
const result = euro * rate; // result is LOCAL: invisible outside
return result;
}
console.log(result); // ReferenceError — locals vanish when the call ends
Each call creates a fresh scope: locals are born on entry and gone on return — which is why two calls never interfere, and the exact machinery that makes closures possible.
Closures
A function remembers its birthplace
The returned function keeps its birth scope alive: count outlives the call that created it.
function makeCounter() {
let count = 0; // private: no code outside can reach it
return function () {
count += 1; // the inner function REMEMBERS count
return count;
};
}
const next = makeCounter();
console.log(next(), next(), next()); // 1 2 3 — count survived between calls
console.log(count); // ReferenceError — it stays private
A function plus its captured scope is a closure. Each makeCounter()
call creates a fresh, independent count — the pattern behind private state
everywhere. Demo foundations/14_closures.html puts two independent counters on one
page.
Pure functions and side effects
The testability line
// PURE: same input → same output; touches nothing outside itself.
function fullName(first, last) {
return `${first} ${last}`;
}
// IMPURE: it changes the page — a side effect.
function showScore(score) {
document.querySelector("#score").textContent = score;
}
Pure functions are trivial to test and reason about; side effects (DOM writes, network) are unavoidable but belong at the edges of a program. The professional shape: pure logic core, thin effect layer.
Callbacks: functions as values
"Run this later"
const button = document.querySelector("#save");
button.addEventListener("click", () => {
console.log("clicked"); // stored by the browser, run on EVERY click
});
setTimeout(() => console.log("3 seconds passed"), 3000);
A function passed to another function, to be run later, is a callback. You have used them since lesson 02 without the name — and they are the doorway to asynchronous JavaScript (Phase 4) and the reason functions-as-values matter so much.
Practice: small helpers
The task
Run foundations/13_functions_lab.js with node, predicting each line.
Then write formatPrice(cents) returning "€12.50" from
1250 (pure!), and preview foundations/14_closures.html — explain in
one sentence why the two counters never interfere.
The checklist
- You can name the three function forms and when each is the natural choice.
- You can predict whether a call returns a value or
undefined. - You can explain a closure as "a function plus its captured scope".
- You can sort a function into pure vs impure — and say where the impure ones belong.