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

By Saurabh Prajapati | IBM Software Engineer | WebDev Cohort 2026
Why I Wanted to Explore This
When I first started writing JavaScript, I would just write functions like this and move on:
function greet() {
console.log("Hello!");
}
Simple enough, right? But then I started seeing code like this all over the place:
const greet = function() {
console.log("Hello!");
}
Wait... aren't these doing the same thing? Why would anyone write it differently?
Let me walk you through everything I discovered.
First, What Even Is a Function?
Before we compare, let's get on the same page.
A function is just a reusable block of code. Instead of writing the same logic again and again, you wrap it in a function and call it whenever you need it.
Think of it like a recipe. You write the steps once, and then you can "use" that recipe anytime you want to cook that dish.
Here's the simplest example:
function addNumbers(a, b) {
return a + b;
}
console.log(addNumbers(3, 5)); // Output: 8
console.log(addNumbers(10, 20)); // Output: 30
You write the logic once. You reuse it everywhere. That's the power of functions.
Function Declaration
This is the classic way. You've probably seen it a hundred times.
function multiply(a, b) {
return a * b;
}
console.log(multiply(4, 5)); // Output: 20
The syntax is straightforward:
Start with the
functionkeywordGive it a name (
multiply)Define the parameters inside
()Write the logic inside
{}
That's it. Clean, simple, readable.
Function Expression
Now here's where it gets interesting.
In a function expression, you create a function and assign it to a variable.
const multiply = function(a, b) {
return a * b;
};
console.log(multiply(4, 5)); // Output: 20
Notice what changed:
No function name after the
functionkeyword (this is called an anonymous function)The function is stored inside a
constvariable calledmultiplyThere's a
;at the end (because it's a variable assignment)
The result is the same. But how JavaScript treats these two approaches behind the scenes? Very different.
Side-by-Side Comparison
Here's a quick visual to make it stick:
┌─────────────────────────────────┬─────────────────────────────────────┐
│ Function Declaration │ Function Expression │
├─────────────────────────────────┼─────────────────────────────────────┤
│ function greet() { ... } │ const greet = function() { ... }; │
├─────────────────────────────────┼─────────────────────────────────────┤
│ Has a name always │ Usually anonymous │
├─────────────────────────────────┼─────────────────────────────────────┤
│ Hoisted (can call before it's │ NOT hoisted (must define first, │
│ defined) │ then call) │
├─────────────────────────────────┼─────────────────────────────────────┤
│ Great for utility functions │ Great for callbacks, passing │
│ used everywhere │ functions around │
├─────────────────────────────────┼─────────────────────────────────────┤
│ Defined at parse time │ Defined at runtime │
└─────────────────────────────────┴─────────────────────────────────────┘
The Big Difference: Hoisting
Okay, this is the part that tripped me up the most. Let me explain it simply.
Hoisting means JavaScript reads through your code before running it and "moves" certain things to the top. Not literally — but it behaves that way.
Function Declarations Are Hoisted ✅
// Calling BEFORE the function is defined
console.log(greet()); // Output: "Hello!"
function greet() {
return "Hello!";
}
Wait — how did this work? We called greet() before we even wrote the function!
That's hoisting. JavaScript saw the function declaration, mentally "lifted" it to the top, and made it available everywhere in the file.
Function Expressions Are NOT Hoisted ❌
// Calling BEFORE the function expression is defined
console.log(greet()); // ❌ Error: Cannot access 'greet' before initialization
const greet = function() {
return "Hello!";
};
This throws an error. Because greet is just a variable here. And variables declared with const or let are not available before their line in the code.
I made this mistake early on. I was confused why one worked and the other didn't. Now I know exactly why.
Key rule to remember:
Function declaration → Call it anywhere in the file ✅
Function expression → Must define it first, then call ✅
When Should You Use Each One?
Honestly, this is a judgment call. But here's how I think about it:
Use Function Declaration when:
The function is a general utility used across many places
You want the flexibility to call it anywhere in your file
You're defining named, standalone functions
// Good use of declaration - utility function used everywhere
function formatDate(date) {
return new Date(date).toLocaleDateString();
}
Use Function Expression when:
You're passing a function as an argument (callback)
You want to conditionally define a function
You're using arrow functions (a modern form of expression)
// Good use of expression - passing as a callback
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(function(num) {
return num * 2;
});
console.log(doubled); // [2, 4, 6, 8]
A Quick Look at Arrow Functions (Bonus)
You've probably seen this syntax too:
const multiply = (a, b) => a * b;
console.log(multiply(3, 4)); // Output: 12
Arrow functions are a shorter form of function expressions. They're very popular in modern JavaScript.
Same rules apply — they are NOT hoisted.
Assignment: Try It Yourself
Here's a small challenge to solidify your understanding:
Step 1 — Write a Function Declaration
function multiplyDeclaration(a, b) {
return a * b;
}
console.log(multiplyDeclaration(6, 7)); // Expected: 42
Step 2 — Write the Same as a Function Expression
const multiplyExpression = function(a, b) {
return a * b;
};
console.log(multiplyExpression(6, 7)); // Expected: 42
Step 3 — Try Calling Before Defining
// Test hoisting behavior
console.log(multiplyDeclaration(3, 3)); // ✅ Works — 9
console.log(multiplyExpression(3, 3)); // ❌ Error — try it!
function multiplyDeclaration(a, b) {
return a * b;
}
const multiplyExpression = function(a, b) {
return a * b;
};
Run this in your browser console or Node.js. See the error with your own eyes. That experience will make hoisting click better than any explanation.
What I Wish I Knew Earlier
A few quick "I wish I knew this" moments:
The name on a function expression is optional — but you can name it for better stack traces in errors
Arrow functions are expressions too — same hoisting rules apply
varbehaves differently fromconst/letwith hoisting — but that's a rabbit hole for another dayMost modern codebases use both — don't feel like you have to pick just one
Key Takeaways
Let's wrap up what we covered:
A function is a reusable block of code
Function declaration uses the
functionkeyword with a name — hoisted ✅Function expression assigns a function to a variable — not hoisted ❌
Use declarations for general-purpose utilities
Use expressions for callbacks and dynamic logic
Arrow functions are a modern shorthand for expressions
About the Author
Saurabh Prajapati is a Full-Stack Software Engineer at IBM India Software Lab, working on enterprise-level cloud-native solutions using Maximo. He specializes in GenAI, React, and modern web technologies, and loves sharing his learning journey with other developers.
🐙 GitHub: prajapatisaurabh
💼 LinkedIn: saurabh-prajapati


