Function Expressions
Understand the difference between a function declaration and a function expression, including anonymous and named function expressions, and how function expressions enable the callback pattern.
A function expression defines a function as part of a larger expression, typically by assigning it to a variable, rather than declaring it as its own standalone statement. This subtle difference changes how and when the function becomes usable, and it is the mechanism behind passing functions around as values.
Function declarations vs function expressions
A function declaration (function greet() { ... }) is a statement on its own and is hoisted in full, so it can be called before its line in the code. A function expression (const greet = function() { ... }) creates the function as part of an assignment — the variable is hoisted, but the function itself is not assigned to it until that line actually executes.
This means calling a function defined via a function expression before its assignment line runs will fail (either with a TypeError, or a ReferenceError if declared with let/const), while the equivalent function declaration would work fine.
Anonymous function expressions
An anonymous function expression is a function with no name of its own, defined directly where it is needed — most often as an argument passed straight into another function. Since it is usually assigned to a variable or passed inline, it does not need its own name to be referred to later.
Anonymous functions are extremely common as one-off callbacks, such as the function passed to array.forEach(function(item) { ... }) or an event handler passed to addEventListener.
Named function expressions
A function expression can still have a name (const factorial = function fact(n) { ... }). That internal name is only usable inside the function's own body — not in the surrounding scope — which is most useful for writing a recursive function expression that needs to call itself by name.
Named function expressions also make stack traces and debugger output more readable, since the function shows up with its given name instead of as "anonymous".
Callbacks: passing functions as values
Because JavaScript treats functions as first-class values, a function expression can be passed directly as an argument into another function — this is called a callback. The receiving function decides when (and whether, and how many times) to actually invoke that callback.
This pattern underlies most asynchronous and array-processing code in JavaScript: setTimeout(callback, 1000), array.map(callback), and button.addEventListener("click", callback) are all cases of a function expression being handed off as a value for something else to call later.
Function expression forms
The variable name (square, factorial) is how outside code refers to the function. In a named function expression, the internal name (fact) is only visible inside its own body. Function expressions are NOT hoisted the way function declarations are — the function only exists after its line runs.
// anonymous function expression
const square = function (n) {
return n * n;
};
// named function expression
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
// used directly as a callback
[1, 2, 3].forEach(function (item) {
console.log(item);
});Code Examples
multiply is a variable holding an anonymous function; calling multiply(6, 7) invokes that function with a = 6 and b = 7. Expected result: 42 const multiply = function (a, b) {... }; assigns the function to a variable rather than declaring it by name directly.
processArray is a higher-order function that accepts any callback and applies it to every element. The named function expression double is passed in directly as that callback, and processArray calls it once per array item. Expected result: [2, 4, 6] callback(item) is where the passed-in function expression actually gets invoked, once per loop iteration.
Common mistake: Trying to call double(5) from outside processArray would fail with a ReferenceError — the name double is only visible inside the function expression's own body, not in the surrounding scope.
const multiply = function (a, b) {
return a * b;
};
console.log(multiply(6, 7));function processArray(arr, callback) {
const results = [];
for (const item of arr) {
results.push(callback(item));
}
return results;
}
const doubled = processArray([1, 2, 3], function double(n) {
return n * 2;
});
console.log(doubled);Passing a function expression as a callback
The anonymous function is created at the call site and handed to processArray as a value; processArray decides when to actually run it.
const doubled = processArray([1,2,3], function(n) { return n * 2; });
└──────────┬──────────┘
passed in as `callback`
processArray(arr, callback) {
...
callback(item) ← invoked here, inside processArray, once per item
}Important Rules
Function declarations are hoisted in full; function expressions are not — the function only exists once its assignment line executes.
An anonymous function expression has no name of its own and is typically used inline as a one-off callback.
A named function expression's internal name is visible only inside its own body, most useful for self-recursion.
Any function expression can be passed as a value into another function — this is the basis of the callback pattern.
Calling a function-expression variable before its assignment runs throws an error, unlike a hoisted function declaration.
Where this shows up
Function expressions are everywhere callbacks are used: event listeners (button.addEventListener("click", function() {...})), array methods (data.filter(function(x) {...})), and timers (setTimeout(function() {...}, 2000)) all rely on defining a function inline and handing it off as a value.
console.log(square(4)); const square = function (n) { return n * n; };
const square = function (n) { return n * n; }; console.log(square(4));
Because square is declared with const and assigned via a function expression, it is not usable before its declaration line runs — calling it earlier throws a ReferenceError (const/let are in the "temporal dead zone" until their line executes).
const factorial = function fact(n) { return n <= 1 ? 1: n * fact(n - 1); }; fact(5); // called from outside const factorial = function fact(n) { return n <= 1 ? 1: n * fact(n - 1); }; factorial(5); // called from outside using the variable name
The internal name fact in a named function expression is scoped to the function's own body only — outside code must call it through the variable it was assigned to (factorial), not the internal name.
[Multiple Choice] Why might you give a function expression an internal name, as in const fn = function inner() {...}? A. It makes the function run faster B. It allows the function to call itself by that name for recursion, and improves stack traces C. It is required by JavaScript syntax D. It makes the function hoisted like a declaration
B. It allows the function to call itself by that name for recursion, and improves stack traces — The internal name of a named function expression is usable inside its own body (handy for recursion) and shows up in stack traces/debuggers instead of "anonymous" — but it does not affect hoisting or performance.
Write an anonymous function expression assigned to a variable isEven that takes a number and returns true if it is even. Then write a named function expression sumTo that recursively sums all integers from 1 up to n.
const isEven = function (n) { return n % 2 === 0; }; const sumTo = function sum(n) { return n <= 1 ? n : n + sum(n - 1); }; console.log(isEven(8), sumTo(5)); // true 15
For sumTo, the base case is n <= 1 returning n (or 1); the recursive case adds n to sumTo(n - 1) using the internal name.