JavaScript Function Expressions vs Declarations
Function declarations are hoisted. Function expressions are not. Learn the practical differences, when to use each, and why the distinction matters in real code.
Both function declarations and function expressions create callable functions. The difference is when the function becomes available to call. A function declaration is hoisted to the top of its scope. A function expression is not -- it only exists after the line that assigns it runs.
Here is the core difference in one example:
// Declaration: callable before definition
console.log(double(4)); // 8
function double(n) {
return n * 2;
}
// Expression: NOT callable before definition
console.log(triple(4)); // ReferenceError
const triple = function(n) {
return n * 3;
};That one behavioral difference -- hoisting -- drives most of the practical reasons to choose one form over the other.
Quick Comparison
| Function Declaration | Function Expression | |
|---|---|---|
| Syntax | function name() {} | const name = function() {} |
| Hoisted | Yes -- entire function | No -- variable is hoisted but function is not assigned |
| Name | Required | Optional (can be anonymous or named) |
| Scope of name | Current scope | Only inside the function body (if named) |
| Can use before line | Yes | No |
| Semicolon after | No | Yes (it is an assignment) |
| Common use | Standalone utility functions | Callbacks, conditional assignment, IIFEs |
Hoisting in Detail
Function declarations are fully hoisted. The entire function -- name and body -- is moved to the top of the scope during the creation phase, before any code runs:
// What you write:
console.log(greet("Alex"));
function greet(name) {
return `Hi, ${name}`;
}
// What JavaScript effectively sees:
function greet(name) {
return `Hi, ${name}`;
}
console.log(greet("Alex"));Function expressions use variables. The variable declaration is hoisted, but the assignment happens at the original line. Before that line, a var-declared variable holds undefined, while a let or const variable sits in the temporal dead zone:
console.log(typeof sayHi); // "undefined" (var is hoisted but not assigned)
var sayHi = function() {
return "Hi!";
};
console.log(typeof sayHi); // "function"Modern code almost always uses const or let instead of var for function expressions, and that choice changes the failure mode. With const, accessing the variable before its declaration line throws a ReferenceError instead of silently returning undefined:
console.log(sayBye); // ReferenceError: Cannot access 'sayBye' before initialization
const sayBye = function() {
return "Bye!";
};For more on hoisting behavior, see Understanding JavaScript Hoisting for Beginners.
Named vs Anonymous Function Expressions
A function expression can be anonymous (no name) or named. An anonymous one is only reachable through the variable it is assigned to:
const add = function(a, b) {
return a + b;
};A named function expression adds an internal name after the function keyword, which stays private to the function itself and never leaks into the surrounding scope:
const addNamed = function addNumbers(a, b) {
return a + b;
};The name addNumbers is only visible inside the function body and in stack traces. It is not accessible in the outer scope:
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1); // Uses the internal name for recursion
};
console.log(factorial(5)); // 120
console.log(typeof fact); // "undefined" -- name is not in outer scopeNamed function expressions are useful for recursion and debugging. When an error occurs, the stack trace shows the function name instead of an anonymous placeholder.
| Anonymous Expression | Named Expression | |
|---|---|---|
| Callable from outer scope | Only through its variable | Only through its variable |
| Callable from inside itself | No, cannot self-reference | Yes, via the internal name |
| Shows in stack traces | As "anonymous" | With its own name |
| Typical use | Short callbacks | Recursive functions, debugging |
When to Use a Function Declaration
Use a declaration when:
- The function is a standalone utility used throughout a file.
- You want the function available anywhere in its scope, regardless of definition order.
- You want clear stack traces with the function name.
- The function does not depend on a runtime condition to be defined.
// Good: declaration for a utility used throughout the module
function formatCurrency(amount, currency = "USD") {
return new Intl.NumberFormat("en-US", {
style: "currency",
currency,
}).format(amount);
}
console.log(formatCurrency(19.99)); // "$19.99"
console.log(formatCurrency(50, "EUR")); // "€50.00"When to Use a Function Expression
Use an expression when:
- You need to assign a function conditionally.
- You are passing a function as a callback argument.
- The function makes sense only in one specific place.
- You want to limit where the function is available.
// Conditional assignment
let discountStrategy;
if (isHolidaySeason()) {
discountStrategy = function(price) {
return price * 0.5;
};
} else {
discountStrategy = function(price) {
return price * 0.9;
};
}The same reasoning applies to callbacks. A function expression written inline inside a method call never needs a name of its own, since the array method is the only thing that calls it:
// Callback passed directly to an array method
const prices = [10, 20, 30];
const withTax = prices.map(function(price) {
return price * 1.08;
});Declarations Inside Blocks
A function declaration inside a block, such as an if statement, behaves differently in strict vs non-strict mode:
"use strict";
if (true) {
function blockScoped() {
return "inside block";
}
}
console.log(typeof blockScoped); // "undefined" in strict modeIn strict mode, block-level function declarations are scoped to the block. Outside the block, they do not exist. This is well-defined in modern JavaScript but was historically inconsistent across browsers. A safer approach is to use a function expression assigned to a variable:
let blockScoped;
if (true) {
blockScoped = function() {
return "inside block";
};
}
console.log(blockScoped()); // "inside block"Which One Should You Use?
As a practical guideline, most experienced developers follow this pattern:
- Function declarations for the main functions in a module -- the ones you want visible and callable from anywhere in the file.
- Function expressions (or arrow functions) for inline callbacks, event handlers, and one-off logic.
There is no single right answer. The important thing is to understand the hoisting difference so you can debug unexpected ReferenceError or undefined is not a function errors when they appear.
For the arrow function alternative, see JavaScript Arrow Functions: A Complete ES6 Guide.
Rune AI
Key Insights
- Function declarations are hoisted -- callable before their definition line. Expressions are not.
- Declarations create a variable with the function name in the current scope. Expressions do not.
- Use declarations for standalone functions. Use expressions for callbacks and conditional assignment.
- Named function expressions help with debugging by showing the name in stack traces.
- Arrow functions are a compact form of function expression with no own this binding.
Frequently Asked Questions
Which one should I use by default?
Can a function expression have a name?
Do arrow functions count as function expressions?
Conclusion
Both forms create callable functions. The difference comes down to hoisting: declarations are available before their line, expressions are not. Use declarations for top-level utility functions where hoisting is convenient. Use expressions when you need conditional assignment, when passing functions as arguments, or when the function only makes sense in one place.
More in this topic
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.
How to Loop Through Arrays Using JS for Loops Guide
Looping through arrays is the most practical use of JavaScript for loops. Learn how to access each element by index, avoid off-by-one errors, and choose between for, for...of, and forEach.