Scope determines where a name is available. A parameter exists inside its function. Variables declared with let or const inside braces belong to that block. A variable called total inside one function does not overwrite a different total outside it.
Prefer passing needed information as arguments over reading changing values from outside. The dependency becomes visible at the call site, and you can test the function using a tiny input. A function can call another function: this is composition, combining small operations to do a larger job.
Keep a function focused on one meaningful task, not necessarily one line. A function that validates input, calculates totals, prints messages, and modifies a page is harder to reason about than separate functions with clear contracts.
Predict before running
Which total does each console.log read? Try changing the outer total; it should not affect the function’s result.
const total = 100;
function double(value) {
const total = value * 2;
return total;
}
console.log(double(5));
console.log(total);Check your prediction
10
100Trace each statement in order. If your prediction differed, describe the first place your mental model and the result diverged before trying again.
Defaults and a function pipeline
A default parameter supplies a value when an argument is missing or undefined. It does not replace every falsy value: zero is still a supplied number. Put required parameters first and optional ones after them, and document what the default means.
Composition works best when each function has a clear input and output. Here one function calculates minutes, and another uses its numeric result to build a label. The formatting function does not reach into another function’s local variables.
function minutesFor(count, length = 25) {
return count * length;
}
function label(minutes) {
return `${minutes} minutes planned`;
}
console.log(label(minutesFor(2)));
console.log(label(minutesFor(2, 15)));
console.log(minutesFor(2, 0));Check your prediction
50 minutes planned
30 minutes planned
0A missing length uses 25. An explicit 0 stays zero; a default is not validation.
Follow a value through the program
For label(minutesFor(2, 15)), the inner call returns 30 before label receives its argument. Write an intermediate variable if nested calls become hard to read. Readability is part of correctness because you need to inspect the steps when an answer is wrong.
Bug hunt: inside a block, declare const message = "Ready", then try to print message outside the block. Read the ReferenceError. Fix the structure by returning the value from a function or moving the declaration to the scope that needs it. Do not solve every scope problem by making a global variable; that hides dependencies between unrelated parts of the program.
Trace one function call at a time
A parameter is local to one call. A local variable does not overwrite an unrelated outer variable merely because their names match. Naming them differently is often clearer while learning, but tracing where each binding is declared is the underlying skill.
Default parameters handle an omitted argument or undefined, not every falsy value. A deliberate zero is still an argument. Decide whether zero is allowed by the function’s domain rule; do not assume a default parameter validates it.
let outside = 10;
function next(value) {
const local = value + 1;
return local;
}
console.log(next(outside));
console.log(next(outside));
console.log(outside);Check your prediction
11
11
10 Both calls receive 10 because outside never changed. Returning a new result does not reassign the caller variable. To keep the result, the caller must explicitly store it.
function total(minutes, count = 1) {
return minutes * count;
}
console.log(total(20));
console.log(total(20, undefined));
console.log(total(20, 0));Check your prediction
20
20
0 The first two calls use the default count. The final call intentionally requests zero sessions. Replacing count with count||1 would change that behavior; only do that if the contract genuinely excludes zero.
Try the rule on new inputs
Call next with different inputs and store one result. Then add an outer local variable with the same name as the function’s local variable and trace both. Test default arguments with omitted, zero and ordinary positive values.
Your turn
Scope & Parameters: practise
Write sumMinutes(sessions), then report(sessions) that returns Total: 45 minutes for [20, 25]. sumMinutes([]) must return 0. Do not print inside either function.
const sessions = [20, 25];
// Define sumMinutes and report, then print report(sessions).Hint 1: the idea
One function computes a number; the other builds text using that number.
Hint 2: a first step
Initialize a total inside sumMinutes and return it after its loop. Call sumMinutes inside report.
Show one solution
function sumMinutes(sessions) {
let total = 0;
for (const minutes of sessions) {
total = total + minutes;
}
return total;
}
function report(sessions) {
return `Total: ${sumMinutes(sessions)} minutes`;
}
console.log(report([20, 25]));
console.log(sumMinutes([]));Total: 45 minutes
0The report function does not need to know how the sum is calculated. Both functions receive their input explicitly, which makes empty and nonempty examples easy to check.
What you should be able to explain
- How does a default differ from validation?
- Trace the inner and outer calls in label(minutesFor(2, 15)).
- Why is an explicit argument often easier to test than a changing global?
Completion is saved in this browser only. It does not measure mastery.