Functions as Reusable Logic

Course: JavaScript Beginner

Lesson 10: Functions as Reusable Logic

Level

Beginner

Estimated Reading Time

10–15 minutes

Goal

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

  • Declare functions using function declarations and arrow functions
  • Define parameters and use return values to pass data out of a function
  • Explain why functions make code easier to reuse, test, and understand

Frontend Development Context

Functions are the fundamental unit of reuse in JavaScript. When you find yourself writing the same logic in two places, or when a block of code grows long enough that you have to scroll to understand it, the right tool is a function. Wrapping logic in a named function lets you call it by name, pass in different data each time, and get a result back.

In frontend development, functions are used everywhere: to handle button clicks, to format data before displaying it, to validate form fields, to fetch data from an API, and to update the DOM based on state. Every event handler you write is a function. Learning to write good functions — with clear names, focused responsibilities, and meaningful return values — is one of the highest-leverage skills in JavaScript.

Explanation

A function declaration defines a named function using the function keyword. It can be called anywhere in the same scope, even before the line where it is defined (because of hoisting). A function expression assigns a function to a variable — it is not hoisted, so it must be defined before it is called. An arrow function is a compact syntax introduced in ES6 that is commonly used in modern JavaScript, especially for callbacks and short transformations.

Parameters are the named inputs a function expects. When you call a function, you pass arguments — the actual values — which are received as the parameter names. A function can have zero or many parameters. Parameters without a matching argument are undefined inside the function.

The return keyword sends a value back to the caller and immediately exits the function. A function without a return statement (or with return;) returns undefined. The returned value can be stored in a variable, passed to another function, or used in an expression.

Functions should do one clear thing. A function named formatPrice should format a price — not also fetch data and update the DOM. Keeping functions focused makes them easier to understand, reuse, and debug.

Default parameter values let you define a fallback for parameters that might not be provided: function greet(name = "Guest") means if no name is passed, it defaults to "Guest".

Code Example

This example uses functions to format a product's price and render a product card to the DOM:

function formatPrice(amount, currency = "USD") {
  return new Intl.NumberFormat("en-US", {
    style: "currency",
    currency: currency,
  }).format(amount);
}

function createProductCard(product) {
  const card = document.createElement("div");
  card.className = "product-card";
  card.innerHTML = `
    <h3>${product.name}</h3>
    <p>${formatPrice(product.price)}</p>
  `;
  return card;
}

const product = { name: "Wireless Keyboard", price: 79.99 };
const card = createProductCard(product);
document.querySelector("#product-grid").appendChild(card);

Code Explanation

formatPrice takes an amount and an optional currency (defaulting to "USD") and uses the built-in Intl.NumberFormat API to produce a properly formatted currency string. This logic is wrapped in a function so it can be called many times across the app with different amounts and currencies without repeating the formatting setup.

createProductCard takes a product object and builds a DOM element representing that product. It calls formatPrice internally to format the price — this is function composition, where one function uses another. The function returns the completed DOM element rather than directly appending it to the page, giving the caller flexibility over where it ends up.

The last three lines create one product and add its card to the DOM. If there were 50 products in an array, you could loop over them and call createProductCard for each one — which is exactly how real product listing pages work.

Notice that neither function uses global state or modifies the DOM directly from inside the formatting function. formatPrice is a pure function — given the same input, it always returns the same output. This makes it easy to test and reuse.

Pattern Highlights

Positive Pattern: Name functions after what they do (a verb or verb phrase): formatPrice, createCard, validateEmail, fetchUser. Clear function names make code self-documenting.

⚠️ Neutral Note: Arrow functions do not have their own this binding, which matters inside object methods or event handlers in some contexts. For simple utility functions and callbacks, arrow functions are ideal.

Negative Pattern: Writing functions that do too many things — fetching data, formatting it, validating it, and updating the DOM — in one function makes it impossible to reuse any single part and very hard to debug.

Common Mistakes

Mistake 1: Forgetting to return a value

A function that performs a calculation but forgets return gives back undefined to the caller, which causes silent failures.

function double(n) {
  const result = n * 2;
  // Missing: return result
}

const value = double(5);
console.log(value); // undefined — not 10

If a function computes something the caller needs, always end with a return statement.

Mistake 2: Using a function expression before it is defined

Function expressions assigned to const or let are not hoisted. Calling them before they are defined throws a ReferenceError.

const greeting = sayHello("Alex"); // ReferenceError: Cannot access before initialization

const sayHello = (name) => `Hello, ${name}!`;

Either use a function declaration (which is hoisted) or ensure the expression is defined before it is called.

Mistake 3: Modifying a parameter directly when it is an object

When you pass an object to a function and modify its properties inside, you are modifying the original object — not a copy.

function applyDiscount(product) {
  product.price = product.price * 0.9; // modifies the original!
}

const item = { name: "Hat", price: 30 };
applyDiscount(item);
console.log(item.price); // 27 — the original object was changed

If you do not intend to mutate the original, create a new object inside the function: return { ...product, price: product.price * 0.9 }.

Quick Recap

  • Functions are named, reusable blocks of logic that can accept parameters and return a value
  • Use return to send a value back to the caller — without it, the function returns undefined
  • Default parameters (function fn(x = 0)) provide fallback values when an argument is not passed
  • Keep functions focused on one responsibility — small, well-named functions are easier to reuse and debug

Ready to test your understanding? Take the quiz to reinforce what you learned.

© 2026 Ant Skillsv.26.07.07-23:01