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

Search for a command to run...

No comments yet. Be the first to comment.
Realtime Systems Deep Dive: Polling, WebSockets, SSE, and Pub/Sub Explained TL;DR: Realtime communication is not one technology — it's a spectrum of patterns, each with distinct trade-offs in latency

Realtime Systems Deep Dive: Polling, WebSockets, SSE, and Pub/Sub Explained TL;DR: Realtime communication is not one technology — it's a spectrum of patterns, each with distinct trade-offs in latency

How ChatGPT Understands Your Questions: A Deep Dive into LLMs, Tokenization, and Transformers TL;DR: ChatGPT doesn't "read" your question the way you do. It converts text into numbers, processes thos

Building Scalable Systems: Caching, Rate Limiting, and Observability TL;DR: Scaling isn't just about adding more servers. It's about protecting your system with intelligent caching, guarding your API

Kafka Explained Like You're 5: Events, Partitions, and Consumer Groups TL;DR: Kafka is a distributed event streaming platform that decouples producers from consumers using topics and partitions, enab

By Saurabh Prajapati | IBM Software Engineer | WebDev Cohort 2026
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.
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.
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 function keyword
Give it a name (multiply)
Define the parameters inside ()
Write the logic inside {}
That's it. Clean, simple, readable.
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 function keyword (this is called an anonymous function)
The function is stored inside a const variable called multiply
There'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.
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 │
└─────────────────────────────────┴─────────────────────────────────────┘
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.
// 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.
// 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 ✅
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]
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.
Here's a small challenge to solidify your understanding:
function multiplyDeclaration(a, b) {
return a * b;
}
console.log(multiplyDeclaration(6, 7)); // Expected: 42
const multiplyExpression = function(a, b) {
return a * b;
};
console.log(multiplyExpression(6, 7)); // Expected: 42
// 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.
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
var behaves differently from const/let with hoisting — but that's a rabbit hole for another day
Most modern codebases use both — don't feel like you have to pick just one
Let's wrap up what we covered:
A function is a reusable block of code
Function declaration uses the function keyword 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
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