Skip to main content

🧩 Lesson 7: Functions

Package your logic into reusable building blocks — write once, call anywhere, and keep your code organized.

🎯 Learning Objectives

By the end of this lesson, you will be able to:

  • Write function declarations and understand hoisting
  • Create function expressions and arrow functions
  • Use parameters, arguments, and default values
  • Return values from functions and use them in expressions
  • Understand the difference between parameters and arguments
  • Write clean functions that do one thing well

Estimated Time: 55 minutes

Project: Build a mini utility library of reusable helper functions

📑 In This Lesson

Introduction

So far you've been writing code that runs straight from top to bottom. As your programs grow, you'll notice yourself copying and pasting the same logic in multiple places. Functions solve this — they let you wrap a block of code into a named, reusable unit that you can call whenever you need it.

Think of a function like a recipe. You define it once (the recipe card), and you can follow it (call it) whenever you want that dish. You can even tweak the ingredients (parameters) each time.

graph LR A["Define function"] --> B["Call function"] B --> C["Code runs"] C --> D["Returns result"] D --> E["Use the result"] B --> F["Call again with different inputs"] F --> C style A fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style D fill:#ecfdf5,stroke:#22c55e,stroke-width:2px,color:#1e293b

Function Declarations

A function declaration is the most traditional way to create a function. It uses the function keyword followed by a name.


// Defining a function
function greet() {
    console.log("Hello, world!");
}

// Calling (invoking) the function
greet();  // "Hello, world!"
greet();  // "Hello, world!" — call it as many times as you want
                

The parentheses () after the function name are what call (invoke) it. Without them, you're just referencing the function itself, not running it.


console.log(greet);   // [Function: greet] — the function object
console.log(greet()); // "Hello, world!" — runs the function
                

Hoisting

Function declarations are hoisted — JavaScript moves them to the top of their scope before execution. This means you can call a function before it's defined in your code.


// This works! Declaration is hoisted.
sayHi();  // "Hi there!"

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

💡 Should You Rely on Hoisting?

Hoisting is useful to know about, but many developers prefer to define functions before calling them for readability. You'll see both styles in real codebases. The important thing is to be consistent within your projects.

Parameters & Arguments

Parameters are placeholders defined in the function signature. Arguments are the actual values you pass when calling the function.


//              parameter
function greet(name) {
    console.log(`Hello, ${name}!`);
}

//       argument
greet("Ray");    // "Hello, Ray!"
greet("Maria");  // "Hello, Maria!"
                

Multiple Parameters


function introduce(name, role, city) {
    console.log(`I'm ${name}, a ${role} from ${city}.`);
}

introduce("Ray", "developer", "Henderson");
// "I'm Ray, a developer from Henderson."
                

Default Parameters

You can give parameters default values that kick in when no argument is provided.


function greet(name = "stranger") {
    console.log(`Hello, ${name}!`);
}

greet("Ray");   // "Hello, Ray!"
greet();        // "Hello, stranger!"

// Practical example
function createUser(name, role = "viewer", active = true) {
    console.log(`${name} — ${role} — ${active ? "Active" : "Inactive"}`);
}

createUser("Alex");                 // "Alex — viewer — Active"
createUser("Jordan", "admin");       // "Jordan — admin — Active"
createUser("Sam", "editor", false);  // "Sam — editor — Inactive"
                

What Happens with Missing Arguments?


function showInfo(a, b, c) {
    console.log(a, b, c);
}

showInfo(1, 2, 3);  // 1 2 3
showInfo(1, 2);     // 1 2 undefined  ← missing args become undefined
showInfo(1);        // 1 undefined undefined
                

⚠️ Parameters vs. Arguments

People often use these terms interchangeably, but technically: parameters are the names in the function definition, and arguments are the values passed when calling. Think: parameters are the parking spots, arguments are the cars that park in them.

Return Values

Functions can return a value back to the code that called them. This is what makes functions truly powerful — they can compute something and hand the result back.


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

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

// Use returned values directly in expressions
console.log(add(10, 20) * 2);  // 60
                

Key Rules About return


// 1. return immediately exits the function
function checkAge(age) {
    if (age < 0) {
        return "Invalid age";  // Function exits here
    }
    return age >= 18 ? "Adult" : "Minor";
}

console.log(checkAge(-5));  // "Invalid age"
console.log(checkAge(25));  // "Adult"

// 2. A function without return gives undefined
function sayHello(name) {
    console.log(`Hello, ${name}!`);
    // No return statement
}

const result = sayHello("Ray");
console.log(result);  // undefined
                

Returning Objects and Arrays


function getUser(name, age) {
    return {
        name: name,
        age: age,
        isAdult: age >= 18
    };
}

const user = getUser("Ray", 30);
console.log(user.name);    // "Ray"
console.log(user.isAdult); // true

// Returning an array
function getMinMax(numbers) {
    return [Math.min(...numbers), Math.max(...numbers)];
}

const [min, max] = getMinMax([4, 2, 9, 1, 7]);
console.log(`Min: ${min}, Max: ${max}`);  // "Min: 1, Max: 9"
                
graph TD A["Call add(3, 5)"] --> B["a = 3, b = 5"] B --> C["Calculate a + b = 8"] C --> D["return 8"] D --> E["const sum = 8"] style A fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style D fill:#ecfdf5,stroke:#22c55e,stroke-width:2px,color:#1e293b style E fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b

Function Expressions

A function expression stores a function in a variable. The function itself can be named or anonymous (unnamed).


// Anonymous function expression
const multiply = function(a, b) {
    return a * b;
};

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

// Named function expression (name useful for debugging)
const divide = function divideNumbers(a, b) {
    if (b === 0) return "Cannot divide by zero";
    return a / b;
};

console.log(divide(10, 2));  // 5
                

Declarations vs. Expressions

Feature Declaration Expression
Syntaxfunction name() {}const name = function() {}
Hoisted?Yes — can call before definitionNo — must define before calling
Named?Always namedCan be anonymous
Use caseGeneral-purpose functionsCallbacks, conditional definitions

// ❌ Function expressions are NOT hoisted
// sayBye();  // Error: Cannot access 'sayBye' before initialization

const sayBye = function() {
    console.log("Goodbye!");
};

sayBye();  // "Goodbye!" — works after definition
                

Arrow Functions

Arrow functions (introduced in ES6) provide a shorter syntax for writing function expressions. They use the => "fat arrow" notation.

Basic Syntax


// Traditional function expression
const add = function(a, b) {
    return a + b;
};

// Arrow function — same thing, shorter
const addArrow = (a, b) => {
    return a + b;
};

// Even shorter — implicit return (single expression)
const addShort = (a, b) => a + b;

console.log(addShort(3, 7));  // 10
                

Arrow Function Shortcuts


// One parameter — parentheses optional
const double = n => n * 2;
console.log(double(5));  // 10

// No parameters — empty parentheses required
const getRandom = () => Math.floor(Math.random() * 100);
console.log(getRandom());  // random number 0–99

// Multiple parameters — parentheses required
const greet = (first, last) => `Hello, ${first} ${last}!`;
console.log(greet("Ray", "De La Paz"));  // "Hello, Ray De La Paz!"

// Returning an object — wrap in parentheses
const makeUser = (name, age) => ({ name, age, active: true });
console.log(makeUser("Alex", 28));
// { name: "Alex", age: 28, active: true }
                

Arrow Functions in Practice

Arrow functions really shine as callbacks — short functions you pass to other functions.


const numbers = [1, 2, 3, 4, 5];

// Traditional function
const doubled = numbers.map(function(num) {
    return num * 2;
});

// Arrow function — much cleaner
const doubledArrow = numbers.map(num => num * 2);

console.log(doubledArrow);  // [2, 4, 6, 8, 10]

// Filtering with arrow function
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens);  // [2, 4]
                

💡 When to Use Which?

Arrow functions are great for short callbacks, array methods, and simple utilities. Function declarations are better for top-level functions and methods that need their own this context (you'll learn about this later). A good rule of thumb: if the function has a name and stands on its own, use a declaration. If it's short and passed inline, use an arrow.

graph TD A["Function Declaration"] --> B["function name() { }"] C["Function Expression"] --> D["const name = function() { }"] E["Arrow Function"] --> F["const name = () => { }"] B --> G["Hoisted ✅"] D --> H["Not hoisted ❌"] F --> H style A fill:#eff6ff,stroke:#3b82f6,stroke-width:2px,color:#1e293b style C fill:#fef3c7,stroke:#f59e0b,stroke-width:2px,color:#1e293b style E fill:#ecfdf5,stroke:#22c55e,stroke-width:2px,color:#1e293b

Best Practices

1. Do One Thing

Each function should have a single, clear purpose. If you find yourself naming it "doThisAndThat," it should probably be two functions.


// ❌ Does too much
function processUser(name, email, age) {
    // validates, formats, saves, sends welcome email...
}

// ✅ Each function has one job
function validateEmail(email) {
    return email.includes("@");
}

function formatName(name) {
    return name.trim().toLowerCase();
}
                

2. Use Descriptive Names


// ❌ Vague
function process(d) { /* ... */ }
function calc(x, y) { /* ... */ }

// ✅ Clear and descriptive
function calculateTax(subtotal, taxRate) { /* ... */ }
function formatPhoneNumber(rawNumber) { /* ... */ }
function isValidPassword(password) { /* ... */ }
                

3. Keep Functions Short

If a function is longer than about 20 lines, consider breaking it into smaller helper functions. Short functions are easier to test, debug, and reuse.

4. Use Early Returns


// ❌ Nested
function getDiscount(price, isMember) {
    let discount = 0;
    if (price > 0) {
        if (isMember) {
            discount = price * 0.1;
        }
    }
    return discount;
}

// ✅ Guard clauses with early returns
function getDiscount(price, isMember) {
    if (price <= 0) return 0;
    if (!isMember) return 0;
    return price * 0.1;
}
                

✅ Functions Are the Building Blocks

Good functions are like good LEGO bricks — small, well-shaped, and easy to snap together. When you write a function, ask: "Could someone understand what this does just from its name and parameters?"

Hands-on Exercise

🏋️ Exercise: Mini Utility Library

Objective: Build a small collection of reusable helper functions using declarations, expressions, and arrow functions.

Instructions:


// Mini Utility Library

// 1. capitalize(str) — Function Declaration
// Takes a string and returns it with the first letter
// capitalized and the rest lowercase.
// capitalize("hello")   → "Hello"
// capitalize("jAVAsCRIPT") → "Javascript"

// 2. clamp(value, min, max) — Function Expression
// Returns the value if it's within the range,
// otherwise returns the nearest boundary.
// clamp(5, 1, 10)   → 5
// clamp(-3, 1, 10)  → 1
// clamp(15, 1, 10)  → 10

// 3. pluralize(count, singular, plural) — Arrow Function
// Returns the correct word form based on count.
// Use a default parameter for 'plural' (singular + "s").
// pluralize(1, "cat")        → "1 cat"
// pluralize(3, "cat")        → "3 cats"
// pluralize(2, "child", "children") → "2 children"

// 4. temperatureConverter(value, fromUnit) — Your choice!
// Converts between Celsius and Fahrenheit.
// temperatureConverter(32, "F")  → "32°F = 0°C"
// temperatureConverter(100, "C") → "100°C = 212°F"
// If fromUnit is invalid, return "Invalid unit"
                    
💡 Hint

capitalize: Use str[0].toUpperCase() for the first character and str.slice(1).toLowerCase() for the rest.

clamp: Use Math.max(min, Math.min(max, value)) or chain conditionals.

pluralize: Set the default parameter plural = singular + "s", then use a ternary to pick the right form.

temperatureConverter: F to C: (value - 32) * 5/9. C to F: value * 9/5 + 32.

✅ Solution

// 1. capitalize — Function Declaration
function capitalize(str) {
    if (!str) return "";
    return str[0].toUpperCase() + str.slice(1).toLowerCase();
}

console.log(capitalize("hello"));       // "Hello"
console.log(capitalize("jAVAsCRIPT")); // "Javascript"
console.log(capitalize(""));           // ""

// 2. clamp — Function Expression
const clamp = function(value, min, max) {
    return Math.max(min, Math.min(max, value));
};

console.log(clamp(5, 1, 10));   // 5
console.log(clamp(-3, 1, 10));  // 1
console.log(clamp(15, 1, 10));  // 10

// 3. pluralize — Arrow Function
const pluralize = (count, singular, plural = singular + "s") =>
    `${count} ${count === 1 ? singular : plural}`;

console.log(pluralize(1, "cat"));              // "1 cat"
console.log(pluralize(3, "cat"));              // "3 cats"
console.log(pluralize(2, "child", "children")); // "2 children"

// 4. temperatureConverter
function temperatureConverter(value, fromUnit) {
    const unit = fromUnit.toUpperCase();

    if (unit === "F") {
        const celsius = ((value - 32) * 5 / 9).toFixed(1);
        return `${value}°F = ${celsius}°C`;
    }
    if (unit === "C") {
        const fahrenheit = (value * 9 / 5 + 32).toFixed(1);
        return `${value}°C = ${fahrenheit}°F`;
    }

    return "Invalid unit";
}

console.log(temperatureConverter(32, "F"));   // "32°F = 0.0°C"
console.log(temperatureConverter(100, "C"));  // "100°C = 212.0°F"
console.log(temperatureConverter(72, "K"));   // "Invalid unit"
                        

🎯 Quick Quiz

Question 1: What does a function return if it has no return statement?

Question 2: Which can be called before it's defined in the code?

Question 3: What is the output of const fn = n => n * 3; console.log(fn(4));?

Summary

🎉 Key Takeaways

  • Function declarations are hoisted and use the function keyword
  • Function expressions store a function in a variable and are not hoisted
  • Arrow functions (=>) are concise expressions, ideal for callbacks
  • Parameters are placeholders; arguments are the actual values passed in
  • Default parameters provide fallback values for missing arguments
  • return sends a value back to the caller — without it, functions return undefined
  • Write functions that do one thing, have clear names, and use early returns

📚 Additional Resources

🚀 What's Next?

Now that you can create reusable functions, the next big question is: where can your variables be accessed? In the next lesson, you'll explore scope and hoisting — understanding which parts of your code can see which variables, and why that matters for writing bug-free programs.