Function Declaration vs Function Expression: What’s the Difference?

Functions are one of the most important building blocks in JavaScript. If you understand functions well, everything else (arrays, objects, async code, frameworks) gets much easier.
In this article, we’ll go from what functions are to function declarations vs function expressions, with hoisting explained in simple terms and practical examples you can try immediately.
What Is a Function and Why Do We Need It?
A function is a reusable block of code that performs a specific task.
Instead of repeating the same code over and over, you:
Define it once as a function.
Call (or “invoke”) it whenever you need it.
Simple example: adding two numbers
function add(a, b) {
return a + b;
}
const result = add(3, 5);
console.log(result); // 8
Why this is useful:
Reusability: you can call
add(3, 5),add(100, 25), etc.Organization: complex logic can be broken into smaller named functions.
Readability:
add(price, tax)It is easier to understand than to doprice + taxeverywhere.
Function Declaration Syntax
A function declaration is the “classic” way to define a function in JavaScript.
Syntax:
function functionName(parameter1, parameter2, ...) {
// function body
// optional: return something
}
Example: multiply two numbers:
function multiply(a, b) {
return a * b;
}
console.log(multiply(4, 6)); // 24
Key points:
Starts with the
functionkeyword.Has a name (
multiply).Can have parameters (
a,b).Can be used
returnto send a value back.
Characteristics of function declarations
Named: they always have a name.
Hoisted: You can call them before their definition in the code (we’ll explain hoisting soon).
Typically used for top-level, reusable utilities that you want available everywhere in a file.
Function Expression Syntax
A function expression is when you create a function and assign it to a variable.
Syntax:
const functionName = function(parameter1, parameter2, ...) {
// function body
};
or with an anonymous function:
const functionName = function(a, b) {
return a * b;
};
Example: multiply two numbers (function expression)
const multiply = function(a, b) {
return a * b;
};
console.log(multiply(4, 6)); // 24
Here:
function(a, b) { ... }is a function expression.It has no name (this is an anonymous function).
It is assigned to a variable called
multiply.
You can also use arrow functions (a more modern syntax), but we’ll stick to regular function expressions for clarity.
Function Declaration vs Function Expression (Side by Side)
Let’s see the same logic written both ways.
Function declaration
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Alice")); // "Hello, Alice!"
Function expression
const greet = function(name) {
return `Hello, ${name}!`;
};
console.log(greet("Alice")); // "Hello, Alice!"
At runtime, once everything is defined, both work similarly:
You call them with
greet("Alice").They return the same result.
The big differences are:
How they are defined in the code (syntax).
How do they behave with hoisting?
How you use them in the design and structure of your code.
Key Differences: Declaration vs Expression
Here’s a high-level comparison.
1. Syntax and Style
- Declaration
function add(a, b) {
return a + b;
}
- Expression
const add = function(a, b) {
return a + b;
};
Declarations feel more “global” and top-level.
Expressions feel more “value-like”: a function as data assigned to a variable.
2. Hoisting Behavior
Hoisting is the JavaScript behavior where certain things are effectively “moved” to the top of their scope before the code runs.
At a very high level:
Function declarations are fully hoisted.
Function expressions (assigned to variables) are not usable before the line where they are defined.
We’ll show concrete examples in the hoisting section below.
3. Naming and Flexibility
Declarations: always named.
Expressions: can be anonymous or named.
This allows function expressions to be:
Passed as arguments to other functions.
Stored in objects or arrays.
Created conditionally.
Example: function expression as a callback
setTimeout(function() {
console.log("Executed later");
}, 1000);
Here we didn’t even save it to a variable—we just used the function expression directly.
4. When You Might Prefer One Over the Other
Function declarations:
Good for core utilities and helpers.
Good when you want functions available throughout the file.
Good when readability is improved by seeing all the “main” functions at the top.
Function expressions:
Good for callbacks (e.g., event handlers, array methods).
Good when you want to control the scope and not pollute the global or outer scope with many names.
Good when you treat functions like values (assign, pass around, etc.).
We’ll go deeper into “when to use which” later.
Hoisting: Simple, Practical Explanation
Let’s keep hoisting high-level and practical, without going deeply into execution contexts or spec details.
The mental model
You can think of JavaScript as doing two simple steps:
Setup phase: It scans your code and:
Registers function declarations.
Reserves space for variables.
Execution phase: It runs your code line by line.
Because of this:
You can call function declarations before their actual code appears.
But you cannot reliably use a function expression before the line where it’s defined.
Hoisting with Function Declarations
You can do this:
sayHello("Alice"); // Works, even though sayHello is defined later
function sayHello(name) {
console.log(`Hello, ${name}!`);
}
Why it works:
During the setup phase, JavaScript sees the function declaration
function sayHello(...) { ... }and registers it.When it starts executing line by line,
sayHelloit already exists.
Hoisting with Function Expressions
You cannot safely do this:
sayHello("Alice"); // Error (or undefined), depending on how it's declared
const sayHello = function(name) {
console.log(`Hello, ${name}!`);
};
Why it fails:
During setup, JavaScript knows there is a variable
sayHello, but it does not yet assign the function to it.At the time you call
sayHello("Alice"),sayHellois not ready.Result: you get an error like “Cannot access 'sayHello' before initialization” or similar.
Same idea with let or var, but details differ. The safe rule of thumb:
- Do not call function expressions before their line of definition.
When to Use Function Declarations vs Function Expressions
Both are valid and useful. The trick is choosing based on intent and code organization.
Use Function Declarations When…
You’re defining main utilities or core helpers in a file.
You like having your API-like functions at the top of the file.
You want them to be fully available anywhere in that scope (because of hoisting).
Example: core helpers
function calculateTotal(price, taxRate) {
return price + price * taxRate;
}
function formatCurrency(amount) {
return `$${amount.toFixed(2)}`;
}
// Usage below...
Use Function Expressions When…
You’re defining functions inside other functions.
You’re using callbacks (e.g.,
map,filter,setTimeout, event handlers).You want to limit the scope and avoid putting lots of function names in the outer level.
You’re building objects or modules where functions are properties.
Examples:
// 1. Callback in array methods
const numbers = [1, 2, 3];
const doubled = numbers.map(function(n) {
return n * 2;
});
// 2. Methods in an object
const calculator = {
add: function(a, b) {
return a + b;
},
multiply: function(a, b) {
return a * b;
}
};
Assignment Idea (Hands-On Practice)
Here’s a small assignment you can try in a .js file or DevTools console.
1. Function Declaration: multiply two numbers
Write a function declaration that multiplies two numbers:
function multiplyDeclaration(a, b) {
return a * b;
}
2. Function Expression: same logic
Write a function expression with the same logic:
const multiplyExpression = function(a, b) {
return a * b;
};
3. Call both functions and print the results
console.log("Declaration result:", multiplyDeclaration(3, 4)); // 12
console.log("Expression result:", multiplyExpression(3, 4)); // 12
4. Try calling them BEFORE their definitions
Now, let’s experiment with hoisting.
Try this version:
console.log(multiplyDeclaration(2, 5)); // What happens?
console.log(multiplyExpression(2, 5)); // What happens?
function multiplyDeclaration(a, b) {
return a * b;
}
const multiplyExpression = function(a, b) {
return a * b;
};
What you should observe:
multiplyDeclaration(2, 5)works.multiplyExpression(2, 5)throws an error (because the variable is not initialized yet at that point).
This small experiment makes hoisting very concrete.
Visual: Comparison Table (Declaration vs Expression)
You can imagine/translate this into a diagram or table in your slides or notes.
| Aspect | Function Declaration | Function Expression |
|---|---|---|
| Basic form | function foo() { ... } |
const foo = function() { ... }; |
| Name | Always has a name | Can be anonymous or named |
| Hoisting | Fully hoisted (can call before definition) | Not usable before the definition line |
| Common usage | Core helpers, top-level utilities | Callbacks, inline logic, assigning to variables/objects |
| Readability | Good for “API-like” public functions | Good for keeping related logic close to where it’s used |
| Flexibility as a value | Less obvious, but still a value | Very natural: it’s literally assigned to a variable |
Visual: Simple Execution Flow of a Function Call
Here’s a conceptual, text-based “diagram” of a function call flow.
- Your code:
function add(a, b) {
return a + b;
}
const result = add(2, 3);
console.log(result);
- High-level flow:
addis defined (either by declaration or expression).You call
add(2, 3).JavaScript:
Takes
a = 2,b = 3.Runs the function body.
Reaches
return a + b.Gives back
5.
resultbecomes5.console.log(result)prints5.
No need to think about execution contexts or call stacks at this stage—just follow input → function → output.
Summary
Functions are reusable blocks of code that take input, optionally process it, and optionally return a result.
Function declarations use
function name(...) { ... }, are hoisted and are great for core helpers.Function expressions create functions as values and assign them to variables; they’re not callable before their line, and are perfect for callbacks, inlining, and scoped utilities.
Hoisting (simple rule):
You can call function declarations before they appear.
You should not call function expressions before their line of definition.
If you internalize these ideas, you’ll have a solid foundation for understanding more advanced topics like:
Arrow functions
Methods in classes and objects
Closures and higher-order functions
Async functions and callbacks
Happy coding!




