Computer Science Fundamentals 5: Functions

Introduction

The Problem: Code Repetition

You've written enough code to notice a pattern: sometimes you write the same block of code over and over in different places.

// Calculate area of a rectangle (first room)
const width1 = 10;
const length1 = 15;
const area1 = width1 * length1;
console.log(area1);

// Calculate area of another rectangle (second room)
const width2 = 8;
const length2 = 12;
const area2 = width2 * length2;
console.log(area2);

// Calculate area of yet another rectangle (third room)
const width3 = 20;
const length3 = 5;
const area3 = width3 * length3;
console.log(area3);

There's a better way. Instead of repeating the calculation, you can wrap code into a reusable block and give it a name.

The Solution: Functions

A function is a named block of code that you can run (invoke) as many times as you want, wherever you want.

function calculateArea(width, length) {
  return width * length;
}

// Now, one line does the work of three:
const area1 = calculateArea(10, 15); // 150
const area2 = calculateArea(8, 12);  // 96
const area3 = calculateArea(20, 5);  // 100

Benefits:

  • Less code duplication
  • Easier to maintain (change the logic in one place)
  • More flexibility (different inputs, different outputs)

What You'll Learn Today

  • Function syntax: How to declare and invoke functions
  • Parameters & arguments: How to pass data into functions
  • Return values: How to get results back
  • Function patterns: Different combinations of input and output
  • Early returns: A clean pattern to avoid else

Before You Begin

You know arrays, loops, and objects. Functions are the next building block that tie everything together.


Functions: The Machine Analogy

Think of a function like everyday machines: a washing machine, a toaster, or an oven.

The Pattern: Input → Process → Output

Think of a function like everyday machines: a washing machine, a toaster, or an oven. Each one follows the same pattern:

  • Input (arguments): What you put in
  • Process (function body): The work that happens
  • Output (return value): What comes out

Washing Machine analogy:

  • Power button: () — you invoke it by calling the function
  • Load the clothes: Your arguments — what goes in
  • Machine runs: The function body — the work happens inside (you don't see it)
  • Clean clothes come out: The return value — the result
// The washing machine
function washClothes(dirtyClothes, detergent) {
  // The process happens inside (we don't write it out)
  // Assume the machine does its job...
  const cleanClothes = "fresh " + dirtyClothes;
  return cleanClothes;
}

// Press the power button with your inputs
const result = washClothes("jeans", "soap");
console.log(result); // "fresh jeans"

Toaster analogy:

  • Bread goes in (argument)
  • Toaster does its thing (function body)
  • Toast comes out (return value)

Oven analogy:

  • Raw ingredients go in (arguments)
  • Oven heats and cooks (function body)
  • Baked goods come out (return value)

Function Syntax & Terminology

Basic Declaration

function greet(name) {
  return "Hello, " + name + "!";
}

The parts:

  • function — the keyword that says "I'm declaring a function"
  • greet — the function name (use a verb: save, calculate, check, validate, etc.)
  • (name)parameters (placeholders for data that will come in)
  • { ... } — the function body (the work that happens inside)
  • return — sends a value back to the caller

Parameters vs Arguments

  • Parameters: Named placeholders in the function definition

    function add(x, y) { // x and y are parameters
      return x + y;
    }
    
  • Arguments: The actual values you pass when you call the function

    const sum = add(5, 3); // 5 and 3 are arguments
    

Key insight: Parameters are like labels on boxes. When you call the function, you fill those boxes with arguments.

Invoking a Function

The () is the power button. Without it, the function doesn't run:

function greet() {
  return "Hello!";
}

greet;     // ❌ Just the name, nothing happens
greet();   // ✅ The power button () invokes it, returns "Hello!"

Return Values

The return keyword sends a value back to whoever called the function:

function multiply(a, b) {
  const result = a * b;
  return result; // Send this back
}

const product = multiply(4, 5);
console.log(product); // 20

Important: Once return runs, the function stops immediately:

function checkAge(age) {
  if (age >= 18) {
    return "Adult";
  }
  console.log("This won't print if age >= 18");
  return "Minor";
}

console.log(checkAge(25)); // "Adult" — stops at first return

If a function has no explicit return, it returns undefined:

function add(a, b) {
  const result = a + b;
  // No return here
}

const sum = add(5, 3);
console.log(sum); // undefined

Generally, you want to return a value so the caller can use it. If you just want to do something (like log), you can, but it's often better to return and let the caller decide what to do. In the latter case, this is called a "side effect" — we'll talk about that later.


Function Patterns: Pure Functions

Core Principle: Functions should be pure—they do one job and return a result. The caller decides what to do with that result (log it, render it, save it, etc.).

This keeps functions testable, reusable, and simple.

The General Rule: Takes Input, Returns Output

// Pure function: does one job, returns a result
function double(num) {
  return num * 2;
}

// The CALLER decides what to do with the result
const result = double(5);
console.log(result);        // Caller logs it
alert(result);              // Caller alerts it
return result;              // Caller returns it
storeInDatabase(result);    // Caller saves it

Why this matters:

  • The function is reusable in any context
  • It's easy to test (no hidden side effects)
  • The function doesn't decide what happens to the result—the caller does

An Exception: No Arguments, Generates a Value

Sometimes a function generates data that doesn't depend on arguments:

function getRandomNumber() {
  return Math.floor(Math.random() * 100);
}

// Caller uses the result however they want
const num = getRandomNumber();
console.log(num);

Note: Math is a built-in object in JavaScript. Math.random() generates a decimal between 0 and 1. By multiplying by 100 and using Math.floor() (round down), we get an integer from 0 to 99.


Early Return: Avoiding Nested else

When you have a condition that should stop the function early, use a direct return:

// Without early return (nested)
function validate(age) {
  if (age >= 18) {
    return "Valid";
  } else {
    return "Too young";
  }
}

// With early return (cleaner)
function validate(age) {
  if (age < 18) {
    return "Too young";  // Exit early if condition is true
  }
  return "Valid";        // Only reached if above return didn't run
}

The early return pattern is cleaner because:

  • No nested if/else
  • Clear: "If this condition, stop and return"
  • Easier to read
function processOrder(items) {
  // Check invalid conditions early
  if (!items || items.length === 0) {
    return "No items to process";
  }

  if (items.length > 100) {
    return "Too many items";
  }

  // If we get here, all checks passed
  return "Order processed: " + items.length + " items";
}

Side Effects: A Peek at the Real World

What is a side effect? When a function does something outside of returning a value—like updating the DOM, changing a global variable, or writing to a database.

Pure Function (No Side Effects)

// Pure: takes input, returns output, nothing else
function calculateTax(price) {
  return price * 0.08;
}

const tax = calculateTax(100); // 8

This function is clean and testable. It does one job.

Function with Side Effects (Impure)

// Impure: returns a value AND manipulates the DOM
function calculateAndDisplay(price) {
  const tax = price * 0.08;
  document.getElementById("tax").innerText = tax; // Side effect!
  return tax;
}

calculateAndDisplay(100);

This function does two jobs: calculate AND update the page. This makes it harder to test and less reusable.

Note: document.getElementById("tax").innerText will not work in our current environment since we are not running in a browser. This is just an example to illustrate the concept of side effects.

The Better Pattern

When you need to update the DOM (or perform any side effect):

// Pure function: just calculate
function calculateTax(price) {
  return price * 0.08;
}

// The caller handles the side effect
const tax = calculateTax(100);
document.getElementById("tax").innerText = tax; // Caller updates the DOM

Why this is better:

  • The function is reusable (works in any context: tests, different pages, etc.)
  • The caller decides what happens with the result
  • Easier to test: you can verify the calculation without needing a DOM
  • Clear separation of concerns: calculation vs. DOM updates are separate

You'll learn more about DOM manipulation in upcoming lessons. For now, just remember: write pure functions that return results, and let the caller decide what to do with them.


Checkpoint: Functions in Practice

Write your answers in handwritten notes ✍️.

  1. Write a function called greetPerson that takes a name as a parameter and returns a greeting string.

  2. Write a function called calculateDistance that takes two arguments (start, end) and returns the distance between them.

  3. Write a function called repeatMessage that takes a message and a count, then returns a string with that message repeated. Example: repeatMessage("Hi ", 3) returns "Hi Hi Hi ". Why is returning a value here better than just logging?

  4. Explain: Why is this function problematic?

    function getAge(name) {
      const age = 20;
    }
    
  5. Rewrite this function using early return instead of else:

    function isEven(num) {
      if (num % 2 === 0) {
        return true;
      } else {
        return false;
      }
    }
    

Common Gotchas

Missing the Power Button ()

function sayHi() {
  console.log("Hi!");
}

sayHi;    // Just references the function, doesn't run it
sayHi();  // Actually runs it

Forgetting to Return

function add(a, b) {
  const result = a + b;
  // Forgot to return! Don't do this.
}

const sum = add(5, 3);
console.log(sum); // undefined (nothing was returned)

The fix: Always return the result. Let the caller decide what to do.

Type Mismatches

Functions expect certain types. If you pass the wrong type, you get unexpected results:

function add(a, b) {
  return a + b;
}

add(5, 3);        // 8 ✅
add("5", "3");    // "53" (concatenation, not addition)
add(5, "3");      // "53" (JavaScript converts the number to a string)
add("hello", 3);  // "hello3" 🤔

If you try to do math on non-numbers, you get NaN (Not a Number):

function multiply(a, b) {
  return a * b;
}

multiply(5, 3);      // 15
multiply("5", "3");  // 15 (JavaScript is forgiving here)
multiply("hello", 3); // NaN (can't multiply text by a number)

The lesson: Type consistency matters. As you write more functions, you'll learn to validate inputs.


Wrap-Up

Key Takeaways

Functions are pure blocks of code:

  • Do one job
  • Take input (arguments)
  • Return a result
  • Let the caller decide what happens next (log it, render it, save it, etc.)

The machine analogy:

  • Arguments go in
  • Process happens inside (you don't see it)
  • Return value comes out
  • The user (caller) decides what to do with the output

Syntax matters:

  • Function declaration with parameters
  • Invoke with () (the power button)
  • Return to send a value back

Pure functions are testable:

  • No console.log inside functions
  • No hidden side effects
  • Easy to verify: given input X, does it return Y?

Early return keeps code clean and avoids nested else.

What's Next

You can now create reusable blocks of code. In the next lesson, you'll learn even more powerful function patterns — including how to pass functions into other functions, and how functions can remember data from their surroundings.

Functions are the building blocks of everything that comes next.