JavaScript functions are the backbone of every application you'll ever build. But early on, many developers stumble on a subtle yet important question:
What's the difference between a Function Declaration and a Function Expression?
They look similar. They both create functions. But they behave differently in ways that will catch you off guard if you don't understand them.
By the end of this article, you'll know exactly when to use each one — and why it matters.
🧠 Why Functions Exist (Quick Recap)
Before diving in, let's make sure we're aligned on why functions matter.
Imagine you need to calculate a discount price across 50 different products. Without a function, you'd copy-paste logic 50 times — a maintenance nightmare.
// ❌ Without a function — repetitive and fragile
let price1 = 100 - (100 * 0.1);
let price2 = 250 - (250 * 0.1);
let price3 = 80 - (80 * 0.1);
// ✅ With a function — clean and reusable
function applyDiscount(price, rate) {
return price - (price * rate);
}
console.log(applyDiscount(100, 0.1)); // 90
console.log(applyDiscount(250, 0.1)); // 225
console.log(applyDiscount(80, 0.1)); // 72
Functions make your code DRY (Don't Repeat Yourself), readable, and easy to maintain.
Now let's look at the two main ways to define them.
📌 Function Declaration
A Function Declaration defines a named function using the function keyword as a standalone statement.
Syntax
function functionName(parameters) {
// code to execute
return value;
}
Example
function greet(name) {
return `Hello, ${name}!`;
}
console.log(greet("Arjun")); // Hello, Arjun!
Simple, familiar, and widely used for utility functions.
📌 Function Expression
A Function Expression assigns a function — often anonymous — to a variable.
Syntax
const variableName = function(parameters) {
// code to execute
return value;
};
Example
const greet = function(name) {
return `Hello, ${name}!`;
};
console.log(greet("Priya")); // Hello, Priya!
The output is identical, but how JavaScript treats this behind the scenes is completely different.
⚡ The Critical Difference: Hoisting
This is where most developers get tripped up.
Hoisting is JavaScript's behavior of moving declarations to the top of their scope before the code runs. Function Declarations and Function Expressions are hoisted very differently.
✅ Function Declaration — Fully Hoisted
// Called BEFORE it's defined — works perfectly
console.log(multiply(3, 4)); // 12
function multiply(a, b) {
return a * b;
}
JavaScript moves the entire function definition to the top of the scope during compilation. You can call it anywhere in the scope, even before it's written.
❌ Function Expression — NOT Fully Hoisted
// Called BEFORE assignment — throws an error
console.log(multiply(3, 4)); // ❌ ReferenceError
const multiply = function(a, b) {
return a * b;
};
ReferenceError: Cannot access 'multiply' before initialization
Why? Because only the variable declaration (const multiply) is hoisted — not the function assigned to it. The variable exists in a Temporal Dead Zone (TDZ) until the assignment line is reached.
📊 Side-by-Side Comparison
| Feature | Function Declaration | Function Expression |
|---|---|---|
| Syntax | function name() {} |
const name = function() {} |
| Hoisting | ✅ Fully hoisted | ❌ Not fully hoisted |
| Call before definition | ✅ Works | ❌ Throws ReferenceError |
| Naming | Must be named | Can be anonymous |
| Use in callbacks | Possible but verbose | ✅ Natural fit |
| Use in closures | Less common | ✅ Ideal |
| Flexibility | Less flexible | ✅ Highly flexible |
🔍 Execution Flow Visualized
Understanding the order in which JavaScript processes your code helps everything click:
┌─────────────────────────────────────────────┐
│ COMPILATION PHASE │
│ │
│ ✅ Function Declarations → Hoisted fully │
│ ⚠️ Variable names (const/let) → Hoisted │
│ but values are NOT assigned yet │
└─────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────┐
│ EXECUTION PHASE │
│ │
│ Code runs line by line │
│ Function Expressions get assigned here │
│ They're callable only AFTER assignment │
└─────────────────────────────────────────────┘
🎯 When to Use Each
Use Function Declaration when:
- You need a simple, reusable utility function
- You want the function to be available anywhere in the file
- You're building named top-level functions (helpers, handlers, etc.)
// ✅ Great as a utility function
function formatCurrency(amount) {
return `₹${amount.toFixed(2)}`;
}
Use Function Expression when:
- Passing a function as a callback
- Working with event listeners
- Building higher-order functions
- Creating closures or IIFEs (Immediately Invoked Function Expressions)
- You want to conditionally assign a function
// ✅ Callbacks
const numbers = [5, 1, 8, 3];
const sorted = numbers.sort(function(a, b) { return a - b; });
// Or with arrow functions (also a function expression):
const sortedArrow = numbers.sort((a, b) => a - b);
// ✅ Event listeners
button.addEventListener("click", function() {
console.log("Clicked!");
});
// ✅ Conditional assignment
let process;
if (isAdmin) {
process = function() { return "Admin access granted"; };
} else {
process = function() { return "Standard access"; };
}
🚀 Bonus: Arrow Functions Are Also Function Expressions
Arrow functions, introduced in ES6, are a concise form of Function Expression:
// Traditional function expression
const square = function(n) {
return n * n;
};
// Arrow function expression — same behavior, shorter syntax
const square = (n) => n * n;
console.log(square(5)); // 25
Key things to know about arrow functions:
- They are not hoisted (same as regular function expressions)
- They do not have their own
this— great for callbacks inside classes/objects - They cannot be used as constructors
💡 Quick Summary
// ─── Function Declaration ───────────────────────
sayHello(); // ✅ Works — hoisted!
function sayHello() {
console.log("Hello from Declaration!");
}
// ─── Function Expression ────────────────────────
sayBye(); // ❌ ReferenceError — not hoisted!
const sayBye = function() {
console.log("Goodbye from Expression!");
};
sayBye(); // ✅ Works after assignment
🧩 Key Takeaways
- Function Declaration → Use for named, reusable, top-level functions. Fully hoisted, available anywhere in scope.
- Function Expression → Use for callbacks, closures, conditional logic, and event handlers. Not hoisted — must be defined before calling.
-
Arrow functions are Function Expressions with a compact syntax and lexical
this. - When in doubt, understand when in the execution cycle you need the function available — that's your deciding factor.
📚 What to Learn Next
Now that you understand how functions are defined, here are some natural next steps:
-
Arrow Functions &
thisbinding — How arrow functions differ in OOP contexts - Closures — How function expressions "remember" their outer scope
- IIFEs — Immediately Invoked Function Expressions and why they're useful
-
Higher-Order Functions —
map,filter,reduceunder the hood -
varvsletvsconsthoisting — How variable declarations affect execution
Found this helpful? Drop a ❤️ and share it with someone learning JavaScript. Have a question or correction? Let's discuss in the comments below!
Top comments (0)