JavaScript Function Expressions Get Certified
A function expression defines a function as part of a larger expression, typically by assigning it to a variable. Unlike a function declaration, a function expression is not hoisted the same way, and it doesn’t need a name.
Function expressions are extremely common in JavaScript, especially for passing functions around as values — for example, as callbacks.
In this lesson, you will learn how to write function expressions, how they differ from function declarations, and how they are used in everyday JavaScript code.
Basic Function Expression Syntax
A function expression is created by assigning an anonymous (or named) function to a variable.
What this code does: A function expression creates a function and stores it in a variable.
Here greet holds an anonymous function. Calling greet() runs that function and prints the message.
const greet = function () {
console.log("Hello, Michael!");
};
greet();
const greet = function () { is a function expression. The function is created on the right side of the = and stored in the constant greet. The function has no name of its own (it is anonymous), so the variable name greet is how we will call it. The empty parentheses mean it takes no parameters, and the brace starts the function body.console.log("Hello, Michael!"); is the code inside the function. It runs each time the function is called, and prints Hello, Michael!.}; closes the function body with } and then ends the whole statement with a semicolon. The semicolon is needed here because this is a variable assignment (const greet = ...;), unlike a function declaration, which does not end with one.greet(); calls the function through the variable: the name followed by parentheses. The body runs and prints Hello, Michael!. Nothing is printed until this line.
Here, the function has no name of its own — it’s called an
anonymous function. The variable
greet is what allows us to call it later.
Function Expressions With Parameters
Just like function declarations, function expressions can accept parameters and return values.
What this code does: Function expressions can take parameters and return values just like declarations.
addNumbers receives 5 and 10, returns their sum, and the result is printed.
const addNumbers = function (a, b) {
return a + b;
};
console.log(addNumbers(5, 10));
const addNumbers = function (a, b) { stores an anonymous function in the constant addNumbers. The parameters a and b work exactly as they do in a function declaration. They receive the arguments in order.return a + b; adds the two parameters and sends the result back to the caller, ending the function.}; closes the function body and ends the assignment statement with a semicolon.addNumbers(5, 10) passes 5 to a and 10 to b. The function returns 15, and console.log prints it. The output is 15.Named Function Expressions
A function expression can also have its own name. This name is mainly useful for debugging (it appears in stack traces) and for allowing the function to refer to itself, such as in recursion.
What this code does: A named function expression has an internal name for recursion or clearer stack traces.
calculateFactorial calls itself until it reaches 1. The factorial of 5 (120) is printed.
const factorial = function calculateFactorial(n) {
if (n <= 1) {
return 1;
}
return n * calculateFactorial(n - 1);
};
console.log(factorial(5));
const factorial = function calculateFactorial(n) { stores a function in the variable factorial, and the function also has its own name, calculateFactorial. This inner name is only visible inside the function, which lets it call itself. The parameter n is the number whose factorial we want.if (n <= 1) { is the base case of the recursion. When n reaches 1 (or less) the function must stop calling itself, otherwise it would repeat forever.return 1; ends the recursion by returning 1. The factorial of 1 is 1.} closes the if block that began on line 2.return n * calculateFactorial(n - 1); multiplies n by the factorial of the next smaller number. The function calls itself through its inner name calculateFactorial. For n = 5 this unfolds as 5 * (4 * (3 * (2 * 1))).}; closes the function body and ends the assignment with a semicolon.factorial(5) calls the function through the variable name (the inner name is not available out here). The result is 5 × 4 × 3 × 2 × 1 = 120, which is printed: 120.
Notice that the name calculateFactorial is only
accessible from inside the function itself — outside the
function, you must still refer to it through the variable
factorial.
Function Expressions Are Not Hoisted the Same Way
Unlike function declarations, a function expression cannot be called before it is defined. The variable holding the function is hoisted, but its value (the function itself) is not assigned until that line of code actually runs.
What this code does: Unlike function declarations, function expressions are not hoisted.
Calling square before it is defined causes an error. The catch block prints that error message.
try {
square(5);
} catch (err) {
console.log("Error:", err.message);
}
const square = function (number) {
return number * number;
};
try { starts a try block. Code inside it is run normally, but if it throws an error, JavaScript jumps to the catch block instead of crashing the program. We use it here so that we can show the error.square(5); tries to call square before the line that creates it has run. With a function declaration this would work (hoisting), but with a function expression stored in a const, the variable exists but is still uninitialized. It is in what is called the temporal dead zone. Using it now throws a ReferenceError.} catch (err) { runs when an error was thrown in the try block. The error object is stored in the variable err.console.log("Error:", err.message); prints the label and the error’s message text. The output is Error: Cannot access 'square' before initialization.} closes the catch block. The program then continues with the next line.const square = function (number) { is where the function is really created and stored in square. Only after this line has run can square be used.return number * number; would return the number multiplied by itself. It is never run in this example.}; closes the function body and ends the assignment. Moving the try block below this line would make the call succeed.
Since square was declared with const,
trying to use it before the declaration line throws a
ReferenceError, because it is still in the temporal
dead zone at that point.
Passing Functions as Arguments (Callbacks)
Because functions are values in JavaScript, a function expression can be passed directly as an argument to another function. A function passed this way is often called a callback.
What this code does: A classic callback pattern: pass a function expression as an argument.
processUser runs its logic, then calls the callback with the name, which prints the welcome message.
function processUser(name, callback) {
console.log("Processing user:", name);
callback(name);
}
processUser("Michael", function (name) {
console.log("Welcome, " + name + "!");
});
function processUser(name, callback) { declares a function with two parameters. The second one, callback, is expected to be a function. Since functions are values in JavaScript, they can be passed as arguments just like numbers or text.console.log("Processing user:", name); prints a progress message with the name. The output is Processing user: Michael.callback(name); calls the function that was passed in, handing it the name. What the callback does is decided by the code that called processUser, not by processUser itself.} ends the processUser function body.processUser("Michael", function (name) { calls processUser with two arguments: the text "Michael" and an anonymous function expression written right in the call. This function becomes the callback parameter. It is not stored in a variable first.console.log("Welcome, " + name + "!"); is the body of the callback. It runs when processUser calls callback(name), so the output is Welcome, Michael!.}); closes the callback’s body with }, then closes the argument list with ) and ends the statement with ;. The two lines of output are Processing user: Michael and Welcome, Michael!.Instead of assigning the function expression to a variable first, it is written directly where it’s needed. This is a very common pattern with array methods and event handlers.
What this code does: Array methods like map() accept a function expression as a callback.
Each number is doubled, producing a new array that is then printed.
const numbers = [1, 2, 3, 4, 5];
const doubled = numbers.map(function (number) {
return number * 2;
});
console.log(doubled);
const numbers = [1, 2, 3, 4, 5]; creates an array of five numbers.numbers.map(function (number) { calls the array method map(), passing an anonymous function expression as the callback. map() calls this function once for each item in the array, passing the item as number, and builds a new array out of the values the function returns. The result is stored in doubled.return number * 2; returns twice the current item. For the items 1, 2, 3, 4 and 5 it returns 2, 4, 6, 8 and 10.}); closes the callback body, the call to map() and the statement. The original numbers array is not changed.console.log(doubled); prints the new array containing 2, 4, 6, 8, 10.Immediately Invoked Function Expressions (IIFE)
A function expression can be called immediately after it’s defined by wrapping it in parentheses and calling it right away. This pattern is known as an Immediately Invoked Function Expression, or IIFE.
What this code does: An IIFE (Immediately Invoked Function Expression) defines a function and runs it right away.
The extra parentheses make the function an expression, and the final () calls it immediately.
(function () {
console.log("This runs immediately!");
})();
(function () { starts an IIFE. The opening parenthesis ( before the function keyword tells JavaScript to treat what follows as a function expression. Without it, a function with no name at the start of a statement would be a syntax error. This function is anonymous and takes no parameters.console.log("This runs immediately!"); is the code inside the function. It will run as soon as the function is called, which happens right after it is defined.})(); has three parts: } ends the function body, ) closes the wrapping parentheses, and the final () calls the function at once, followed by ;. The output is This runs immediately!. There is no name, and the function can never be called again.IIFEs are often used to create a private scope, so that variables defined inside don’t leak into the surrounding code.
What this code does: Variables declared inside an IIFE stay private to that scope.
secretValue is accessible inside the IIFE but causes a ReferenceError outside it.
(function () {
let secretValue = 42;
console.log("Inside the IIFE:", secretValue);
})();
try {
console.log(secretValue);
} catch (err) {
console.log("Error:", err.message);
}
(function () { starts an immediately invoked function expression. Everything declared inside this function belongs to the function’s own private scope.let secretValue = 42; creates a variable inside the function. It exists only while the function runs and can only be seen by code inside the function.console.log("Inside the IIFE:", secretValue); prints the variable from inside the function, where it is visible. The output is Inside the IIFE: 42.})(); closes the function and calls it immediately. After this line, the function has finished and secretValue is gone.try { starts a try block, so that the error we expect can be caught and shown instead of stopping the program.console.log(secretValue); tries to use secretValue in the outer code, where it does not exist, because it was private to the IIFE. This throws a ReferenceError.} catch (err) { runs because an error was thrown. The error object is stored in err.console.log("Error:", err.message); prints the message. The output is Error: secretValue is not defined. This shows that the IIFE kept its variable from leaking into the surrounding code.} closes the catch block.Function Expressions as Object Methods
A function expression is often used directly as a property value in an object, creating what is called a method.
What this code does: Object methods are often written as function expressions.
calculator.add and calculator.subtract are called and their results are printed.
const calculator = {
add: function (a, b) {
return a + b;
},
subtract: function (a, b) {
return a - b;
}
};
console.log(calculator.add(10, 5));
console.log(calculator.subtract(10, 5));
const calculator = { creates an object: a collection of named properties inside curly braces, stored in the constant calculator.add: function (a, b) { defines a property named add whose value is a function expression. A function stored as a property of an object is called a method.return a + b; returns the sum of the two parameters.}, closes the function body. The comma separates this property from the next one in the object.subtract: function (a, b) { defines the second method, again a function expression as a property value.return a - b; returns the first parameter minus the second.} closes the function body. There is no comma, because this is the last property.}; closes the object and ends the statement.calculator.add(10, 5) uses dot notation to get the method from the object, and calls it. It returns 15, which is printed.calculator.subtract(10, 5) returns 10 - 5, which is 5. The output is 15 and then 5.Function Declarations vs. Function Expressions
| Aspect | Function Declaration | Function Expression |
|---|---|---|
| Syntax | function name() {} | const name = function () {} |
| Hoisting | Fully hoisted (usable before defined) | Not usable before the assignment line |
| Name required? | Yes | No (can be anonymous) |
| Common use case | General-purpose named functions | Callbacks, methods, IIFEs |
Practical Example
Let’s use a function expression as a callback to filter an array of numbers.
What this code does: Store a function expression in a variable and reuse it as a callback.
isEven is passed to filter(), which keeps only the even numbers from the list.
const numbers = [3, 8, 15, 22, 6, 40];
const isEven = function (number) {
return number % 2 === 0;
};
const evenNumbers = numbers.filter(isEven);
console.log(evenNumbers);
const numbers = [3, 8, 15, 22, 6, 40]; creates an array of six numbers, a mix of odd and even.const isEven = function (number) { stores an anonymous function in the constant isEven. Storing it gives the callback a clear name, and lets you reuse it elsewhere.return number % 2 === 0; uses the remainder operator %. An even number has a remainder of 0 when divided by 2, so the comparison gives true for even numbers and false for odd ones. A function that returns true or false like this is often called a predicate.}; closes the function body and ends the assignment.numbers.filter(isEven) passes the function itself (note: isEven without parentheses, because we are passing the function, not calling it). filter() calls it for every item and keeps only the items for which it returns true. The new array goes into evenNumbers.console.log(evenNumbers); prints the filtered array: 8, 22, 6, 40. The numbers 3 and 15 were left out because they are odd.
Storing the function expression in isEven first makes
the code more readable, but it could also be written directly
inline as an anonymous function.
Common Mistakes With Function Expressions
- Trying to call a function expression before the line where it’s defined, expecting hoisting to work like it does with function declarations.
- Forgetting the semicolon after a function expression assigned to a variable.
- Forgetting to wrap an IIFE’s function in parentheses, resulting in a syntax error.
- Assuming an anonymous function expression has a usable name for recursion, when only named function expressions can refer to themselves this way.
- Overusing IIFEs in modern code where ES6 modules already provide proper scoping.
Best Practices for Function Expressions
- Use
constwhen assigning a function expression to a variable, unless you have a specific reason to reassign it. - Give a function expression a name when it will appear in stack traces or needs to reference itself recursively.
- Use function expressions (or arrow functions) for callbacks passed to methods like
map(),filter(), andforEach(). - Prefer named, reusable function expressions over duplicating the same anonymous function in multiple places.
- Consider arrow function syntax as a more concise alternative for simple function expressions.
Quick Summary
- A function expression defines a function as part of an expression, usually by assigning it to a variable.
- Function expressions can be anonymous or named.
- Unlike function declarations, function expressions cannot be used before the line where they are defined.
- Function expressions are commonly used as callbacks and object methods.
- An IIFE is a function expression that runs immediately after being defined.
- Because functions are values, function expressions can be passed around, stored, and returned just like any other value.
Try It Yourself
Modify the code below. Try changing the function expression’s
logic, or passing it into a different array method like
filter() or forEach().
What this code does: Practice example: change the mapping logic or try filter() / forEach(),
then click Test My Code to see the new result.
const numbers = [1, 2, 3, 4, 5];
const squared = numbers.map(function (number) {
return number * number;
});
console.log(squared);
const numbers = [1, 2, 3, 4, 5]; creates the array that will be transformed.numbers.map(function (number) { calls map() with an anonymous function expression. It runs once for every item, and the values it returns make up the new array stored in squared. Try replacing map with filter or forEach and see how the result changes.return number * number; returns each item multiplied by itself: 1, 4, 9, 16 and 25. Change this line to experiment, for example to number * 10.}); closes the callback body, the call to map() and the statement.console.log(squared); prints the new array: 1, 4, 9, 16, 25.Tip: If you’re not sure whether you need a function declaration or a function expression, ask yourself whether you need to call the function before it’s defined in the file. If not, a function expression (or arrow function) works just as well and is often the more common style for callbacks.
Understanding function expressions is essential before moving on to arrow functions, closures, and more advanced JavaScript concepts.
🧪 Test Your JavaScript Code
Edit the code on the left and click “Run Code” to see the console output on the right.
Click “Run Code” to see the output here.
Code Explanation
The complete example stays visible. Use Previous and Next to move through its logical blocks.
Select Next to begin exploring the code.