🧩
CodingπŸŽ“ Ages 14-18Intermediate 14 min read

JavaScript Functions in Depth

Master JavaScript functions: parameters and arguments, return values, default parameters, arrow functions, scope and closures, and functions passed as callbacks. Includes runnable code, a worked example and a quiz.

Key takeaways

  • Parameters are placeholders; arguments are the real values you pass in
  • return sends a value back; without it a function returns undefined
  • Default parameters give a fallback when an argument is missing
  • Arrow functions are a shorter syntax, great for short callbacks
  • Scope decides where variables are visible; functions can be passed as values

Functions, the heart of a program

A function is a named, reusable block of code. You've already used simple ones in JavaScript Variables and Functions. This lesson goes much deeper: how data flows in and out, modern arrow syntax, scope, and the powerful idea of passing functions around as values. These are the concepts that separate a beginner from someone who can read real-world JavaScript.

Parameters vs arguments

When you define a function, the names in the brackets are parameters β€” placeholders. When you call it, the real values you supply are arguments.

function greet(name, times) {   // name and times are PARAMETERS
  for (let i = 0; i < times; i++) {
    console.log("Hello, " + name + "!");
  }
}

greet("Ada", 3);   // "Ada" and 3 are ARGUMENTS

The argument "Ada" is copied into the parameter name for the duration of that call. Keeping the words straight makes documentation and error messages much easier to follow.

Returning values

A function can send a value back to whoever called it using return. That value can be stored, printed or used in more maths:

function area(width, height) {
  return width * height;   // hand a value back
}

let a = area(4, 5);        // a is now 20
console.log(a * 2);        // 40

The moment a return runs, the function stops. Any code after it is skipped. If a function has no return, calling it produces undefined β€” useful to remember when something "comes back empty."

Default parameters

Sometimes an argument is optional. A default parameter supplies a fallback when nothing is passed:

function greet(name = "friend") {
  return "Hi, " + name + "!";
}

console.log(greet("Sam"));   // "Hi, Sam!"
console.log(greet());        // "Hi, friend!"  β€” default kicks in

The default is used only when the argument is undefined (i.e. missing). This removes a lot of clunky if (name === undefined) checks.

Arrow functions

Arrow functions are a shorter way to write a function. These three all do the same thing:

// 1. Function declaration
function double(n) { return n * 2; }

// 2. Function expression stored in a variable
const double2 = function (n) { return n * 2; };

// 3. Arrow function
const double3 = n => n * 2;

The arrow version drops the function keyword and, for a single expression, the return and braces too β€” the result is implicitly returned. If there are zero or several parameters, use brackets: () => 42 or (a, b) => a + b. For multiple statements, keep the braces and write an explicit return:

const describe = (name, age) => {
  let label = age >= 18 ? "adult" : "minor";
  return name + " is a " + label;
};

Arrows shine for short callbacks, which you'll see next.

Scope: where variables live

Scope controls where a variable is visible. A variable declared with let or const inside a function exists only inside that function β€” that's local scope:

function counter() {
  let count = 0;        // local to counter
  count++;
  return count;
}

console.log(count);     // ❌ ReferenceError β€” count isn't visible out here

Locality is a feature: it stops far-apart parts of your code clashing. A variable declared at the top level of a file is global and visible everywhere, but you should keep most variables in the smallest scope that works.

Functions as values (callbacks)

In JavaScript a function is a value, just like a number or string. That means you can store it in a variable, put it in an array, or pass it to another function. A function passed in so it can be called later is a callback:

function repeat(times, action) {
  for (let i = 0; i < times; i++) {
    action(i);            // call the function we were given
  }
}

repeat(3, i => console.log("Step " + i));
// Step 0
// Step 1
// Step 2

This pattern is everywhere. Array methods like map, filter and forEach all take a callback, and so does addEventListener from JavaScript Events and Clicks.

A complete worked example

Save this as a .js file and run it with Node, or paste it into your browser console. It combines defaults, returns, arrow callbacks and scope.

// A reusable function with a default and a return value
function makePriceTag(amount, currency = "$") {
  return currency + amount.toFixed(2);
}

const prices = [4, 19.5, 3.25];

// map takes an ARROW CALLBACK and returns a new array
const tags = prices.map(p => makePriceTag(p));
console.log(tags);            // ["$4.00", "$19.50", "$3.25"]

// filter takes a callback that returns true/false
const pricey = prices.filter(p => p > 5);
console.log(pricey);          // [19.5]

// A function that builds and returns another function (a closure)
function adder(step) {
  return n => n + step;       // the inner arrow remembers 'step'
}
const addTen = adder(10);
console.log(addTen(5));       // 15
console.log(addTen(100));     // 110

Reading the result: makePriceTag shows a default parameter (currency = "$") and a return. map and filter each take an arrow callback. The adder function returns a new arrow function that "remembers" the step value from its scope β€” that captured-variable behaviour is called a closure, and it's one of JavaScript's most powerful features.

Try it yourself

  1. Add a tax helper. Write withTax(amount, rate = 0.2) that returns the amount plus tax, then map it over the prices array.
  2. Build a multiplier. Following the adder example, write multiplier(factor) that returns a function multiplying its input by factor. Make a triple and test it.
  3. Pass different callbacks. Reuse the repeat function above but pass it an arrow that builds a string of stars ("*".repeat(i + 1)) and logs it, making a little triangle.

Challenge: Write a function pipeline(value, ...steps) that takes a starting value and any number of functions, and runs the value through each function in turn, returning the final result. For example pipeline(3, n => n + 1, n => n * 2) should give 8. Loop over the steps array and reassign value each time. This is a real pattern used in data-processing code.

Quick quiz

Test yourself and earn XP

What is the difference between a parameter and an argument?

What does a function return if it has no return statement?

What does a default parameter do?

Which is a valid arrow function that doubles n?

What is a callback function?

FAQ

Use arrow functions for short, inline callbacks β€” like the function you pass to map, filter or addEventListener β€” because they're compact. Use a regular named function (or function declaration) for your main, reusable building blocks, especially top-level helpers you call from several places, since named functions are easier to read in stack traces and can be 'hoisted' (used before they appear in the file).

Scope is the set of places where a variable is visible. Variables declared with let or const inside a function (or block) only exist there β€” that's local scope. This is a good thing: it prevents different parts of your program from accidentally overwriting each other's variables. Keep variables in the smallest scope that works.