Skip to main content
Back to module
JavaScript Essentials · Language Fundamentals

Functions in depth

Reading36mLesson 2 of 4 in this module

Write reusable functions with parameters, default values and return values, and understand where variables are visible.

Why this matters

A function is a named, reusable piece of code. Instead of writing the same calculation in five places, you write it once and call it five times. When it needs fixing, you fix one place. Functions are also how you break a big problem into small pieces you can understand and test one at a time. Every JavaScript program you will ever read, from a to-do list to a React app, is built out of functions.

Declaring and calling a function

function greet(name) {
  return `Hello, ${name}!`;
}

const message = greet("Ada");  // call it with an argument
console.log(message);          // Hello, Ada!
  • name is a parameter: a placeholder in the definition.
  • "Ada" is an argument: the actual value passed in when you call it.
  • return sends a value back to the caller and stops the function immediately.

A function without a return gives back undefined. That is a common source of confusion: console.log shows a value, but it does not return it.

function addLog(a, b) {
  console.log(a + b);   // prints 5...
}
const result = addLog(2, 3);
console.log(result);    // ...but result is undefined

If another piece of code needs the answer, return it.

Multiple parameters and defaults

Functions can take several parameters. You can give a parameter a default value that is used when the caller leaves it out:

function applyDiscount(price, percent = 10) {
  return price - price * (percent / 100);
}

applyDiscount(50, 20);  // 40
applyDiscount(80);      // 72  (percent defaults to 10)

Extra arguments are ignored and missing ones are undefined (unless they have a default), so JavaScript will not complain if you call a function wrongly. Your checks and tests have to catch that.

Functions are values

In JavaScript a function is a value, like a number or a string. You can store it in a variable, pass it to another function, or return it from a function. That idea powers array methods, event listeners and promises later in this course.

function shout(text) {
  return text.toUpperCase() + "!";
}
function applyTwice(fn, value) {
  return fn(fn(value));
}
applyTwice(shout, "hi");  // "HI!!"

Arrow functions

Arrow functions are a shorter way to write a function, popular for small helpers:

const square = (n) => n * n;          // one expression: return is implied
const add = (a, b) => a + b;
const logAll = (items) => {            // braces: you must write return yourself
  for (const item of items) console.log(item);
};

With a single expression and no braces, the result is returned automatically. With braces, you need an explicit return. Forgetting this is the number one arrow-function bug:

const broken = (n) => { n * n };  // returns undefined!

Arrow functions also handle the this keyword differently, which matters later for objects and classes. For now, use function declarations for named, top-level functions and arrows for short callbacks.

Scope: where variables are visible

Variables declared with let or const inside a function (or inside any { } block) only exist there. This is called scope.

const taxRate = 0.2;            // global: visible everywhere

function priceWithTax(price) {
  const tax = price * taxRate;  // local: only inside this function
  return price + tax;
}

priceWithTax(100);  // 120
console.log(tax);   // ReferenceError: tax is not defined

Inner code can read outer variables (taxRate above), but outer code cannot see inner ones. Keeping variables local means two functions can both use a variable called total without interfering with each other.

A worked example: formatting prices

Small functions combine well:

function formatPrice(amount) {
  return "$" + amount.toFixed(2);
}

function receiptLine(item, price, percentOff = 0) {
  const finalPrice = applyDiscount(price, percentOff);
  return `${item}: ${formatPrice(finalPrice)}`;
}

receiptLine("Notebook", 25, 10);  // "Notebook: $22.50"
receiptLine("Pen", 2);            // "Pen: $2.00"

Each function does one job and has a clear name. toFixed(2) turns a number into a string with exactly two decimal places.

Common mistakes

  • Printing instead of returning. If the caller needs the value, return it.
  • Arrow function with braces but no return. Add return, or remove the braces.
  • Calling before you mean to. button.addEventListener("click", handle()) calls handle immediately. Pass handle without parentheses.
  • Relying on outer variables that change. Pass values in as parameters so the function is predictable.
  • Code after return. It never runs.

Quick recap

  • Declare with function name(params) { ... }, call with name(args).
  • return sends a value back; without it you get undefined.
  • Default parameters fill in missing arguments.
  • Arrow functions are short; one expression returns automatically.
  • let/const inside a function are local to it.

Try it

Open the Do tab and write three small functions: a discount calculator with a default, a price formatter and an arrow function.

Also in this lesson: