JavaScript Functions: Declaration vs Expression

If you have written even a little JavaScript, you have probably copy-pasted code more than once. You wrote the same logic in two places and told yourself you would clean it up later. Functions are exactly that cleanup. They let you write a piece of logic once, give it a name, and call it whenever you need it.
This article covers what functions are, the two main ways to write them, and a real practical difference between them — hoisting — explained without any jargon.
Why Functions Exist
Consider this code that greets three different people:
// Without functions — repeating the same logic
console.log("Hello, Arjun! Welcome.");
console.log("Hello, Priya! Welcome.");
console.log("Hello, Rohan! Welcome.");
Now imagine you need to change the greeting from "Welcome" to "Good to see you." You update three lines. If there were thirty names, you update thirty lines. This is the problem functions solve.
// With a function — write the logic once, use it anywhere
function greet(name) {
console.log("Hello, " + name + "! Good to see you.");
}
greet("Arjun"); // Hello, Arjun! Good to see you.
greet("Priya"); // Hello, Priya! Good to see you.
greet("Rohan"); // Hello, Rohan! Good to see you.
Now if the greeting changes, you update one place and every call benefits. That is the core idea: functions are reusable, named blocks of code.
Anatomy of a Function
Before looking at the two syntax styles, here is what every function is made of:
function greet ( name ) {
│ │ │ │
│ │ │ └── body: the code that runs
│ │ └────────── parameter: input the function accepts
│ └────────────────── name: how you call it
└─────────────────────────── keyword
Parameters are the variables listed in the parentheses when you define the function.
Arguments are the actual values you pass when you call the function.
The return value is what the function sends back after it finishes.
// name is the parameter
function greet(name) {
return "Hello, " + name; // return sends a value back
}
// "Arjun" is the argument
let message = greet("Arjun");
console.log(message); // "Hello, Arjun"
Function Declaration
A function declaration is the most straightforward way to define a function. You use the function keyword followed by a name, and the body goes inside curly braces.
// Function declaration
function add(a, b) {
return a + b;
}
// Calling it
let result = add(5, 3);
console.log(result); // 8
A few more examples to make the pattern concrete:
// No parameters, no return value
function sayHello() {
console.log("Hello!");
}
sayHello(); // Hello!
// One parameter
function double(n) {
return n * 2;
}
console.log(double(7)); // 14
// Multiple parameters
function fullName(first, last) {
return first + " " + last;
}
console.log(fullName("Arjun", "Sharma")); // "Arjun Sharma"
// Used in a calculation
function calculateArea(length, width) {
return length * width;
}
let area = calculateArea(10, 5);
console.log("Area:", area); // Area: 50
Function Expression
A function expression assigns a function to a variable. Instead of giving the function a name directly after the function keyword, you create the function and store it in a variable — just like you would store a number or a string.
// Function expression
const add = function(a, b) {
return a + b;
};
// Calling it — same as before
let result = add(5, 3);
console.log(result); // 8
The function on the right side of = has no name of its own — it is called an anonymous function. The variable add holds a reference to it.
// The same examples rewritten as expressions
const sayHello = function() {
console.log("Hello!");
};
sayHello(); // Hello!
const double = function(n) {
return n * 2;
};
console.log(double(7)); // 14
const fullName = function(first, last) {
return first + " " + last;
};
console.log(fullName("Arjun", "Sharma")); // "Arjun Sharma"
const calculateArea = function(length, width) {
return length * width;
};
let area = calculateArea(10, 5);
console.log("Area:", area); // Area: 50
The behavior is identical. The difference is entirely in how and when JavaScript makes the function available — which brings us to hoisting.
Hoisting: The Key Practical Difference
Hoisting is what JavaScript does before it runs your code. The engine scans your file first and pulls certain declarations to the top of their scope. Function declarations are hoisted completely — both the name and the body. Function expressions are not.
This means you can call a function declaration before the line where you wrote it, and it works. Try the same thing with a function expression and you get an error.
// Calling a declaration BEFORE it is defined — works fine
let result = add(4, 6);
console.log(result); // 10
function add(a, b) {
return a + b;
}
JavaScript moved the entire add function to the top before running anything. By the time add(4, 6) runs, the function already exists.
// Calling an expression BEFORE it is defined — throws an error
let result = multiply(4, 6); // ReferenceError: Cannot access 'multiply' before initialization
const multiply = function(a, b) {
return a * b;
};
With expressions, the variable multiply is created but its value (the function) is not assigned until that line executes. Calling it before that point throws an error.
Here is a side-by-side summary:
DECLARATION EXPRESSION
──────────────────────────────────── ──────────────────────────────────────
function add(a, b) { const add = function(a, b) {
return a + b; return a + b;
} };
✔ Can be called before definition ✘ Cannot be called before definition
✔ Hoisted fully by JavaScript ✘ Variable hoisted, value is not
✔ Has its own name ✘ Anonymous (no name on the function)
Comparison Table
| Function Declaration | Function Expression | |
|---|---|---|
| Syntax | function name() {} |
const name = function() {} |
| Hoisted? | Yes — fully | No — throws if called early |
| Has a name? | Yes | Usually anonymous |
Ends with ;? |
No | Yes (it's an assignment) |
| When available | Entire scope immediately | Only after the line runs |
Execution Flow of a Function Call
Here is what happens step by step when JavaScript runs a function call:
Your Code What JavaScript Does
───────────────────────── ──────────────────────────────────────
1. Scans file, hoists declarations
let result = add(4, 6); ───────► 2. Calls add with arguments 4 and 6
3. Jumps into function body
a = 4, b = 6
function add(a, b) { 4. Executes: return 4 + 6
return a + b; ◄─────── 5. Returns 10 back to the call site
}
6. result = 10
console.log(result); ───────► 7. Prints 10
For a function expression, step 1 changes — the function is not hoisted, so calling it before the assignment line causes the engine to stop with an error.
When to Use Each
Use a function declaration when:
The function is a core part of your program (a utility you call throughout the file)
You want the flexibility to call it anywhere in the file without worrying about order
You are defining named, standalone pieces of logic
// Good candidate for a declaration — used throughout the file
function formatCurrency(amount) {
return "₹" + amount.toFixed(2);
}
console.log(formatCurrency(1500)); // ₹1500.00
console.log(formatCurrency(99.9)); // ₹99.90
Use a function expression when:
You are passing a function as an argument to another function
You want to conditionally assign different functions to a variable
You are writing a function that should only exist in a specific scope
// Good candidate for an expression — assigned conditionally
let greet;
const isLoggedIn = true;
if (isLoggedIn) {
greet = function(name) {
return "Welcome back, " + name + "!";
};
} else {
greet = function(name) {
return "Hello, " + name + ". Please log in.";
};
}
console.log(greet("Arjun")); // "Welcome back, Arjun!"
Both are valid. Most developers use declarations for main logic and expressions for callbacks, conditional assignments, and short inline functions.
Assignment: Try It Yourself
Open your browser console or playcode.io and work through these steps.
Step 1: Write a function declaration that multiplies two numbers
function multiply(a, b) {
return a * b;
}
Step 2: Write the same logic as a function expression
const multiplyExpr = function(a, b) {
return a * b;
};
Step 3: Call both and print the results
let result1 = multiply(6, 7);
let result2 = multiplyExpr(6, 7);
console.log("Declaration result:", result1); // 42
console.log("Expression result:", result2); // 42
Step 4: Try calling them before defining — observe the difference
// Test 1: Call the declaration before its definition
console.log(multiplyEarly(4, 5)); // 20 — works because of hoisting
function multiplyEarly(a, b) {
return a * b;
}
// Test 2: Call the expression before its definition
console.log(multiplyLate(4, 5)); // ReferenceError — not hoisted
const multiplyLate = function(a, b) {
return a * b;
};
Run Test 1 first, see it print 20. Then run Test 2 and observe the ReferenceError. That single experiment makes the hoisting difference completely clear.
Quick Recap
Functions are reusable blocks of code that take inputs, do work, and optionally return a value
Function declarations use
function name() {}and are fully hoisted — callable anywhere in the fileFunction expressions assign a function to a variable:
const name = function() {}— only available after that line runsHoisting is JavaScript pre-scanning declarations before executing code — declarations benefit from it, expressions do not
Use declarations for standalone utility functions, expressions for callbacks and conditional assignments
Comments
Join the discussionNo comments yet. Be the first to comment.
