completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Function expressions & callbacks

Learn how JavaScript treats functions as values: store them in variables, pass them into other functions, return them, and use callbacks safely.

By the end, you can
  • 01
    Make function valuesWrite a function expression and tell it apart from a function declaration.
  • 02
    Pass functions aroundStore a function in a variable or an object, or pass it to another function, without calling it too early.
  • 03
    Use callbacksFollow a function that gets another function and calls it at the right moment.

Functions are values

You already know how to write a function and call it. This lesson adds one new idea. In JavaScript, a function is also a value, just like a number or a string. So you can:

  • store a function in a variable,
  • keep it in an object,
  • pass it to another function,
  • return it from a function,
  • put it in an array.

That is why typeof a function gives "function". You met typeof in the Data types lesson. It tells you what kind of value something is.

A function value can do one thing other values can't: you can call it. You call a function by writing round brackets () after it. Without the brackets, you only move the function around. With the brackets, its code runs now.

The short version

A function expression makes a function value. A callback is a function value that you pass to another function. That other function calls it at the right moment.

Real-life analogyA function is a recipe card

Think of a recipe card for pancakes. You can put it in a folder, mail it to a friend, or hand it to a cook. None of these make pancakes. Pancakes happen only when someone cooks from the card. In the same way, add is the card, and add(2, 3) cooks from it now.

In real life: A recipe card
In JavaScript: A function value
In real life: Putting the card in a folder
In JavaScript: Storing it in a variable or an object
In real life: Handing the card to a friend
In JavaScript: Passing it to another function
In real life: Cooking from the card
In JavaScript: Calling it with round brackets ()

Many parts of JavaScript ask you for a function: array methods, button clicks, and timers. A later lesson, Higher-order functions, goes deeper. In this lesson you learn one skill: look at a function and tell whether it is being stored, passed, or called.

Function expressions

CORE IDEA

A function declaration is a line that starts with the word function. It makes a function and gives it a name:

A function declarationPop out in the code editor (opens in a new tab)JavaScript
function add(a, b) {
  return a + b;
}

An expression is any piece of code that gives a value, like 2 + 3. A function expression makes a function in a place where a value goes. The most common place is after =, so the function is stored in a const variable:

A function expression stored in addPop out in the code editor (opens in a new tab)JavaScript
const add = function (a, b) {
  return a + b;
};

console.log(add(2, 3)); // 5

Here is what each part does:

  • The right side of line 1, function (a, b) { ... }, is the function value.
  • The left side, add, is the variable that stores it.
  • Line 5 calls the function with add(2, 3) and prints 5.

The const rule from the Variables lesson still applies. You can't put a different value in add later. You can still call the function as often as you like.

Left: add, with no round brackets, is the box add holding a function card. Right: add(2, 3), with round brackets, runs the function card and gives the value 5.addfunctionaddjust the functionadd(2, 3)function5it runs and gives 5
Without round brackets, add is the function itself, stored in a variable. With round brackets, add(2, 3) runs the function and gives back 5.

A function expression can sit anywhere a value can sit:

  • after =,
  • inside an object,
  • inside an array,
  • between the round brackets of a function call.

Sort the lines below. Each answer explains the clue.

Declaration or expression?
  • function a() {};
  • const b = function () {};
  • setTimeout(function () {}, 0);
  • const c = function named() {};
  • (function () {})();
  • const tools = { run: function () {} };
Try it yourself
0 of 6 correct

Sort each line by how it makes its function. Each answer explains the clue.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Declarations vs expressions

STEP THROUGH

Both shapes make a function you can call. The difference is when the name is ready to use. Before any line runs, JavaScript reads the whole script and sets up every name it finds. This setup step is called hoisting. Each shape is set up in a different way:

  • A declaration is set up with its whole function. You can call it even above the line where it is written.
  • A const function expression is set up as a name that can't be used yet. It becomes usable only when its own line runs. The time before that is called the temporal dead zone. Calling the function there throws a ReferenceError.
  • An older var function expression is set up holding undefined. Calling it early throws a TypeError, because undefined is not a function.

Try all three in the replay below. Line 1 calls sayHello(). Line 3 makes the function.

Call before the definition line
Step 0 of 3Ready
Your turn: follow the blue line

Pick a shape, then step through. See what happens when line 1 calls the function before line 3 makes it.

Running in
  1. script
Next: line 3
sayHello();   console.log("hello from declaration");}
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose how line 3 makes sayHello

Changing your choice starts the replay again. Guess what line 1 does before you step.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Line 1 calls sayHello() before line 3. With function sayHello, the box already holds the function, so it works. With const sayHello, the box is not ready yet, so it throws ReferenceError. With var sayHello, the box holds undefined, so it throws TypeError.function sayHellofunctionsayHelloit worksconst sayHellonot ready yetsayHelloReferenceErrorvar sayHelloundefinedsayHelloTypeError
What is in sayHello when line 1 calls it. A declaration is ready with its function. A const name can't be used yet. A var name holds undefined, which can't be called.

The table compares the two shapes. Read each row as a question, then read the answer for each shape.

How a function declaration and a function expression differ
What you want to knowFunction declarationFunction expression
Can I call it above its line?Yes. Hoisting sets it up with its whole function before any line runs.No. A const or let one throws ReferenceError. A var one still holds undefined, so calling it throws TypeError.
Does it need a name?Yes. The name is required.No. It can have no name, take its name from the variable, or have its own name for clearer errors.
When is it used?For a named helper that you reuse in your code.For passing a function, storing it in an object, or picking one function from many.
How does it read?Like a statement: here is a function called add.Like a value: store this function in the variable add.
The real error messages

Calling a const function expression too early throws ReferenceError: Cannot access 'sayHello' before initialization. Calling a var function expression too early throws TypeError: sayHello is not a function. Your editor may warn you about this before you even run the code.

Which one should you use?

  • Use a declaration for a named helper that reads like a normal part of your code.
  • Use a function expression when you treat the function as data: you store it in an object, pass it to a function, or make it for one spot only.

First-class functions

INTERACTIVE

First-class means functions are treated like any other value. A number can be stored in a variable, passed to a function, returned, and kept in an object. A function can do all of these too. This is a normal rule of the language, not a special trick.

In the playground, an object called operations holds three functions. Pick one, and calculate calls it with your two numbers.

Pick a function and pass it
Functions stored in an objectPop out in the code editor (opens in a new tab)JavaScript
function add(a, b) { return a + b; }function subtract(a, b) { return a - b; }function multiply(a, b) { return a * b; } const operations = { add, subtract, multiply };const chosen = operations.add; function calculate(operation, left, right) {  return operation(left, right);} console.log(calculate(chosen, 8, 3));
The real calladd

calculate(add, 8, 3)

Result: 11

Try it yourself

The object holds three functions. The one you picked is passed to calculate. Then calculate calls it with 8 and 3.

Pick what to do by picking a function from an object.

You will use this pattern often: first pick the data, then pick the function that works on it. For example:

  • A calculator picks operations.add.
  • A menu picks the function for the command the user chose.
  • A form checker keeps one function for each rule.
Left: calculate(add) puts the add function card into the parameter box operation, so calculate can call it and prints 6. Right: calculate(add()) runs add first, which gives NaN, so the box operation holds NaN and calling it throws TypeError.calculate(add)a functionaddaddoperationprints 6calculate(add())a resultNaNNaNoperationTypeError
calculate(add) passes the function, so calculate can call it. calculate(add()) runs add first and passes its result, NaN, which can't be called.
Passing vs calling

calculate(add) passes the function. calculate(add()) calls add first and passes its result. That result is NaN, not a function, so calculate can't call it. The only difference is the round brackets ().

Callbacks

STEP THROUGH

A callback is a function you pass to another function, so that the other function can call it. The other function may call it:

  • right away,
  • once for each step of a loop,
  • after a timer,
  • when a user clicks.

The main point: you hand over the function, and the other function decides when to call it.

Real-life analogyA callback is leaving your phone number

When a restaurant is full, you leave your phone number. You don't pick the minute they call. They call when your table is ready. A callback works the same way: you hand over a function, and the other code calls it at the right time.

In real life: You leave your number at a restaurant
In JavaScript: You pass a function as an argument
In real life: The restaurant calls when your table is ready
In JavaScript: The other function calls your callback when it chooses
In real life: You don't phone yourself while you wait
In JavaScript: Passing a function does not call it
repeat calls the function you pass
Step 0 of 10Ready
Your turn: follow the blue line

A callback is a function you hand to other code. Step through to see that repeat decides when to call it.

Running in
  1. script
Next: line 7
function repeat(times, action) {  for (let count = 1; count <= times; count++) {    action(count);  }}   console.log("Turn " + number);});
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Pick how many times repeat runs

Each time, repeat calls the same function with a new number.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Step 1: you pass the function showTurn to repeat, and it lands in the parameter box action. Step 2: repeat calls action three times, which gives Turn 1, Turn 2, and Turn 3.1. you pass showTurnshowTurnshowTurnactioninside repeat2. repeat calls itTurn 1Turn 2Turn 3
You hand showTurn to repeat, where it is stored in the parameter action. Then repeat decides when to call it: once for each turn.

While the callback runs, the replay's Running in row shows script → repeat → action. That means the main script called repeat, and repeat called the callback, action. All of this happens right now, in order. The line after repeat(...) waits until the loop is done. Code that runs in order like this is called synchronous.

Arrays work the same way. forEach is an array method that calls your function once for each item. You will learn map, filter, and reduce in Stage 3, but forEach already shows the callback idea:

forEach calls a function for each namePop out in the code editor (opens in a new tab)JavaScript
const names = ["Ada", "Grace", "Linus"];

names.forEach(function greet(name) {
  console.log("Hi, " + name);
});

Line 3 passes the function greet to forEach. forEach calls it three times, once for each name. So it prints Hi, Ada, Hi, Grace, and Hi, Linus.

A button works the same way. In the next code, onClick acts like a tiny button: it gets a function and calls it.

Pass save to onClickPop out in the code editor (opens in a new tab)JavaScript
function onClick(callback) {
  callback();
}

function save() {
  console.log("saved");
}

onClick(save);

Line 9 passes the function save to onClick. On line 2, onClick calls it, so the code prints "saved". A real button uses the same shape: you pass a function now, and it is called later, when the click happens.

A mistake: onClick(save()) calls save too earlyPop out in the code editor (opens in a new tab)JavaScript
function onClick(callback) {
  callback();
}

function save() {
  console.log("saved");
}

onClick(save());

Here line 9 has round brackets after save. So save runs first and prints "saved". It gives back undefined, and onClick gets that. Then line 2 throws TypeError: callback is not a function.

A timer callback runs later. setTimeout takes a function and a wait time in milliseconds. It keeps the function, waits, and then calls it. The event loop lesson explains the details. For now, remember this: the function is handed over now and called later.

A timer calls its function laterPop out in the code editor (opens in a new tab)JavaScript
console.log("first");

setTimeout(function onTimer() {
  console.log("later");
}, 0);

console.log("second");

This prints first, then second, then later. Even with a wait time of 0, the timer's function runs only after the rest of the script is done. Code that runs later like this is called asynchronous.

Browser events work the same way. An event is something that happens on the page, like a click. A later lesson covers events. For now, read button.addEventListener("click", callback) as: when a click happens, call this function.

Anonymous functions and named function expressions

An anonymous function is a function with no name after the word function. It is common for a short function that is used only where it is written:

An anonymous function passed to repeatPop out in the code editor (opens in a new tab)JavaScript
repeat(3, function (number) {
  console.log("Turn " + number);
});

That is fine for a short callback. But a name after function helps in two ways:

  • The function can call itself by that name. A function that calls itself is called recursive.
  • The name shows up in error messages, so you can find the broken function faster.

A function expression with its own name is called a named function expression:

A named function expressionPop out in the code editor (opens in a new tab)JavaScript
const factorial = function fact(n) {
  if (n <= 1) return 1;
  return n * fact(n - 1);
};

console.log(factorial(4));
console.log(typeof fact);

Line by line:

  • Line 1 stores the function in the variable factorial. The function's own name is fact.
  • Line 2 stops when n is 1 or less, and returns 1.
  • Line 3 uses the name fact to call the function again, with a number one smaller.
  • Line 6 prints 24, because 4 × 3 × 2 × 1 is 24.
  • Line 7 prints "undefined". The name fact works only inside the function, not outside. For a name that doesn't exist, typeof gives "undefined" instead of throwing an error.

A name also helps when something goes wrong. When an error is thrown, JavaScript records which functions were running. This list is called a stack trace, and you can read it in error.stack.

The next code uses try and catch. The try block runs some code. If that code throws an error, the catch block gets the error. Here, validateCart throws, and the catch block checks that the stack trace contains the name validateCart. It prints true.

The name validateCart shows up in the stack tracePop out in the code editor (opens in a new tab)JavaScript
const validate = function validateCart() {
  throw new Error("missing price");
};

try {
  validate();
} catch (error) {
  console.log(error.stack.includes("validateCart"));
}

A clear name in a stack trace is easier to spot than the word anonymous. The Reading error messages lesson shows how to read a whole stack trace.

Where you’ll use this

Function expressions and callbacks show up whenever code needs to change what a tool does, without rewriting the tool. Here are three common patterns:

  • Lookups: keep functions in an object, then pick one by its name. This replaces a long chain of if statements.
  • Repeated work: write repeat(times, action) once. Then pass a different function to print, draw, or save.
  • Later work: pass a function to a timer or to a click listener. The browser calls it when the time is up or when the user clicks.
Returning functions comes next

A function can also return another function. This works because functions are values. Sometimes the returned function keeps using variables from the place where it was made. That is called a closure, and it has its own lesson.

Common misconceptions

"A function expression runs when it is stored."

Storing a function does not run it. The code inside runs only when something calls it with round brackets.

"Callbacks always run later."

Timer and event callbacks run later. But forEach and this lesson's repeat call their callback right away, in order, before they finish.

"Declarations and expressions are set up the same way."

You can call a declaration above its line. A const expression can't be used before its line. A var expression is only undefined there.

"An anonymous function makes errors impossible to read."

JavaScript can often take a name from the variable, as in const greet = function () {}. But a name written after function still makes stack traces and recursive calls clearer.

"Passing a function and passing its result are almost the same."

They are opposites. calculate(add) hands over the function. calculate(add()) runs add too early and hands over its result.

Practice: function values

5 EXERCISES
Exercise 1 · Warm-upPredict the early-call error

Guess the error name before you run the code.

Starter codePop out in the code editor (opens in a new tab)JavaScript
greet();
const greet = function () {
  console.log("hi");
};

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeWrite applyTwice

    Write a function that takes a starting value and a callback. It should call the callback twice, feeding the first result into the second call.

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    function applyTwice(value, action) {
      return action(action(value));
    }
    
    function double(number) {
      return number * 2;
    }
    
    console.log(applyTwice(5, double));

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFix calculate(add())

      This code passes the result of add() instead of the function itself. Fix it so calculate gets the function.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      console.log(calculate(add()));

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeTurn a declaration into an expression

        shout used to be a function declaration, so calling it on line 1 worked. Now it is a const function expression. What changes?

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        shout("ready");
        
        const shout = function (word) {
          return word.toUpperCase();
        };

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeBuild an operations lookup

          Make an object with three functions: add, subtract, and multiply. Pick multiply and call it.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const operations = {
            add: function (a, b) { return a + b; },
            subtract: function (a, b) { return a - b; },
            multiply: function (a, b) { return a * b; },
          };
          
          const chosen = operations.multiply;
          console.log(chosen(6, 7));

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS

            Read each code example carefully. Watch for round brackets, and for how each function is made.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What is a function expression?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this code print? Line 1 calls the function before its declaration.

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              sayHi();
              function sayHi() {
                console.log("hi");
              }

              Choose an answer to see the explanation.

            3. Question 3 of 7What does line 1 print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              console.log(typeof choose);
              var choose = function () {};

              Choose an answer to see the explanation.

            4. Question 4 of 7What does calculate(add) print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function add(a, b) { return a + b; }
              function calculate(operation) {
                return operation(4, 2);
              }
              console.log(calculate(add));

              Choose an answer to see the explanation.

            5. Question 5 of 7What does this code print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              function repeat(times, action) {
                for (let count = 1; count <= times; count++) {
                  action(count);
                }
              }
              repeat(2, function (n) {
                console.log("#" + n);
              });

              Choose an answer to see the explanation.

            6. Question 6 of 7What does line 4 print, outside the function?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const factorial = function fact(n) {
                return n <= 1 ? 1 : n * fact(n - 1);
              };
              console.log(typeof fact);

              Choose an answer to see the explanation.

            7. Question 7 of 7Which statement about callbacks is true?

              Choose an answer to see the explanation.

            Key takeaways

            • Functions are values. You can store them, pass them, return them, and keep them in objects.
            • A function expression makes a function value in a place where a value goes, like after =.
            • You can call a declaration above its line. You can't call a const function expression before its line.
            • A callback is a function you pass to another function, which calls it at the right moment.
            • Anonymous functions are fine for short callbacks. A name helps with stack traces and with recursive calls.

            Remember the one-liner.
            A function expression makes a function value. A callback is that value, handed to other code to call.

            Up next: Arrow functions. You'll learn a shorter way to write callbacks, and how arrow functions behave differently.

            CompleteFrontend Clear concepts. Working examples.