Skip to content
EzzyWeb

Level 3, Interactive Building, Chapter 9 of 24

JavaScript Functions & Scope

Why this matters

You've already written conditionals and loops that make decisions and repeat work, but so far every piece of logic has lived inline, written out fresh each time you needed it. As programs grow, that gets repetitive fast, and worse, it gets error-prone: if you need to fix a calculation, you'd have to find and update every copy of it scattered through your code.

Functions solve this by letting you name a piece of logic once and reuse it anywhere, with different inputs each time. Understanding scope, the rules about where a variable is visible, is what lets you use functions confidently without variables from one part of your program accidentally leaking into or clashing with another.

The lesson

Defining and calling functions

A function is a reusable block of code that can accept inputs, called parameters, and optionally return a value. You define one with the function keyword, a name, a parenthesized list of parameters, and a body in curly braces. Defining a function doesn't run it, calling it does, by writing its name followed by parentheses containing the actual values, called arguments, you want to pass in. This separation is powerful: you can define a calculation once, like converting a price and quantity into a total, and then call it with completely different numbers every time you need that calculation done.

A simple function with a return value
JavaScript
1function calculateTotal(price, quantity) {2  return price * quantity;3}45const total = calculateTotal(19.99, 3);6console.log(total); // 59.97
calculateTotal accepts two parameters and returns their product; the return statement sends a value back to wherever the function was called.

Function expressions and arrow functions

Functions can also be stored in variables. A function expression, const greet = function(name) { ... }, and an arrow function, const greet = (name) => { ... }, both create a function value and assign it to a variable, rather than declaring a named function directly. Arrow functions offer a shorter syntax, and when the body is a single expression, you can even skip the curly braces and return keyword entirely, since the expression's value is returned automatically. You'll see arrow functions everywhere in modern JavaScript, especially for short callbacks, so it's worth getting comfortable reading both styles even before you use them everywhere yourself.

Arrow function shorthand
JavaScript
1const square = (n) => n * n;2const greet = (name) => `Hello, ${name}!`;34console.log(square(5));   // 255console.log(greet("Ada")); // Hello, Ada!
When an arrow function's body is one expression, the braces and return keyword can be omitted; the expression's result is returned implicitly.

Parameters, defaults, and multiple returns paths

Parameters can have default values, used only when a caller doesn't provide an argument for that position, written as function greet(name = "friend") { ... }. Functions can also contain multiple return statements guarded by conditionals, letting a function take different paths depending on its inputs, similar to how you already write branching logic with if and else, except now that logic lives inside a reusable, named unit. As soon as a return statement runs, the function stops immediately and hands that value back, so return also doubles as an early exit from a function when you want to skip the rest of its body under certain conditions.

A function with a default parameter and branching
JavaScript
1function describeAge(age, unit = "years") {2  if (age < 0) {3    return "Invalid age";4  }5  return `${age} ${unit} old`;6}78console.log(describeAge(10));        // 10 years old9console.log(describeAge(5, "days")); // 5 days old
unit defaults to "years" when omitted, and an early return handles the invalid case before the normal path runs.

Scope: where variables live

Scope determines which parts of your code can see and use a given variable. Variables declared with let or const inside a function are local to that function, they exist only while the function is running and are invisible from outside it. This is a feature, not a limitation: it means two different functions can each use a variable named total without any conflict, because each total lives in its own private scope. Blocks like if statements and loops also create their own scope for let and const, so a variable declared inside a for loop's braces doesn't exist once the loop finishes. Variables declared outside any function, at the top level of your script, are in the global scope and are visible everywhere, which is convenient but risky, since too many global variables makes it easy for one part of a program to accidentally overwrite another's data.

  • Function scope: variables declared inside a function only exist while that function runs and only inside it.
  • Block scope: let and const declared inside { } (including if and for blocks) are confined to that block.
  • Global scope: variables declared outside any function or block are visible everywhere, so use them sparingly.
  • Inner scopes can read outer variables, but outer scopes can never read variables declared inside an inner scope.
Local scope keeps variables separate
JavaScript
1function makeMessage() {2  const greeting = "Hi there";3  return greeting;4}56console.log(makeMessage());7console.log(typeof greeting); // "undefined" - greeting doesn't exist out here
greeting is created and destroyed inside makeMessage; trying to read it outside the function finds nothing.

Why functions and scope matter together

Combining functions with careful scope is what lets programs grow without becoming tangled messes. Each function becomes a small, self-contained unit you can reason about on its own, understand its inputs, trust its output, and reuse it elsewhere without worrying that it secretly depends on or interferes with unrelated variables somewhere else in your program. As you move toward working with the DOM and handling events in the next chapters, you'll write many small functions, each responsible for one clear job, and scope is what keeps all of those functions from stepping on each other's toes.

Try it yourself

Write a function called calculateDiscount that takes a price and a percentage and returns the discounted price. Then write a second function that uses the first one to print a formatted receipt line for several items.

Live preview

Chapter boss project

Boss project: A Reusable Grading Toolkit

Write a small set of functions that work together to grade a list of test scores. One function should convert a numeric score into a letter grade, another should calculate the average of an array of scores, and a third should combine them to print a full report.

Each function should do exactly one job and return a value rather than only logging to the console, so the functions can call each other cleanly. Practice giving at least one function a default parameter, such as a passing threshold that defaults to 60.

Once your functions work, try calling them with a few different arrays of scores to prove they're genuinely reusable rather than hardcoded for one specific list.

  • Write scoreToLetter(score) first and test it alone with a few sample scores before building anything on top of it.
  • Use a loop (from Level 2) inside your average function to add up scores, then divide by the array's length.
  • Remember that a function can call another function; your report function can call both scoreToLetter and your average function.
  • Keep an eye on scope: name your loop counters and temporary totals inside the function so they don't leak into the global scope.

Level-up checklist

Tick these off once each one is true for you.