Free lesson · JavaScript Basic to Advance
Function Declarations vs Expressions
Two Ways to Create a Function
JavaScript gives you two distinct ways to create a function: function declarations and function expressions. They look similar. They produce functions that work the same way when called. But the differences between them affect hoisting, scope, and how readable your code is.
// Function declaration
function declaredFunction(x) {
return x * 2;
}
// Function expression (assigned to a variable)
const expressedFunction = function(x) {
return x * 2;
};
console.log(declaredFunction(5));
console.log(expressedFunction(5));
Both functions do the same thing. The difference is in how they're created — declarations use the function keyword as a statement; expressions use function as part of a value being assigned.
Knowing both forms is essential because real codebases use both. Knowing when to prefer each is part of writing JavaScript well.
Interactive animation: The same function written two ways - function as a statement, and function as a value assigned to a variable.
Function Declarations
A function declaration creates a named function as a standalone statement. The structure is:
function functionName(parameters) {
// body
return something;
}
The function has a name (functionName) that's automatically available in the surrounding scope. You can call the function by that name. The function exists from the start of the scope, because it's fully hoisted — we covered hoisting in Topic 3.4.
// You can call the function BEFORE its declaration line
console.log(greet("Olena"));
function greet(name) {
return `Hello, ${name}!`;
}
This works because function declarations are fully hoisted. By the time console.log runs, greet already exists as a complete callable function.
This hoisting behavior is what makes function declarations distinctive. You can organize a file with the "important" code at the top and "helper" functions at the bottom, and the helpers will still be available because of hoisting. This was a popular pattern in earlier JavaScript code.
Function Expressions
A function expression creates a function as part of a value — usually assigned to a variable. The function itself may have a name or be anonymous.
// Anonymous function expression (most common modern form)
const greet = function(name) {
return `Hello, ${name}!`;
};
console.log(greet("Olena"));
The function on the right side of the = is the value. The variable greet receives that value. From that point on, you call greet(...) and it runs the function.
Function expressions are not hoisted the way declarations are. The variable name is hoisted (according to its declaration keyword — var, let, or const), but the function value isn't available until the line of code that assigns it.
// This will throw an error
// console.log(greetExpression("Olena")); // ❌ ReferenceError or TypeError
const greetExpression = function(name) {
return `Hello, ${name}!`;
};
console.log(greetExpression("Olena")); // ✅ works after assignment
If greetExpression was declared with const, accessing it before the assignment line would throw a ReferenceError (Temporal Dead Zone — Topic 3.5). If it was declared with var, the variable would be undefined and trying to call it would throw a TypeError ("undefined is not a function").
Function expressions can also be named expressions — they have a name internal to the function but the function is still assigned to a variable:
const factorial = function fact(n) {
return n <= 1 ? 1 : n * fact(n - 1);
};
console.log(factorial(5));
// console.log(fact(5)); // ❌ ReferenceError — 'fact' isn't accessible outside
The internal name fact is only visible inside the function itself, which is useful for recursion. From outside, the function is accessed through the variable factorial. Named function expressions are rare in modern code but appear occasionally — they're worth recognizing.
Interactive animation: Name inference - a function expression assigned to a variable takes its name from that variable.
The Practical Differences
graph TD
A["Function Declaration<br/>function name() { }"] --> B[Fully hoisted]
A --> C[Can be called before declaration line]
A --> D[Has its own name available]
E["Function Expression<br/>const name = function() { }"] --> F[Variable is hoisted but uninitialized]
E --> G[Can only be called after assignment]
E --> H[Name comes from the variable]
style A fill:#7c3aed,color:#fff
style E fill:#10b981,color:#fff
This diagram contrasts function declarations and function expressions. Function declarations are fully hoisted, meaning you can call them before the line where they appear in the source. They have their own internal name. Function expressions are assigned to variables. The variable is hoisted but uninitialized, so you can only use the function after the assignment line. The function's name comes from the variable it was assigned to.
For practical use:
Function declarations are convenient when you want hoisting — when defining helper functions at the bottom of a file or wanting a flat file structure where order doesn't matter.
Function expressions are more flexible — they can be passed around as values, stored in objects, and assigned conditionally. Modern JavaScript style guides often prefer them because they're more consistent with how other values are declared (const x = ...).
Interactive animation: Called above their own lines - the declaration is ready from the start, the expression only after its assignment.
The Modern Preference
Most modern JavaScript style guides prefer function expressions assigned to const:
// Modern preferred style
const greet = function(name) {
return `Hello, ${name}!`;
};
// Even more common: arrow function (covered in next Topic)
const greetArrow = (name) => `Hello, ${name}!`;
The reasons:
- Consistency. Variables declared with
constare immutable bindings. The function can't be accidentally reassigned later. With function declarations, someone could later writegreet = something elseand the original definition would be lost. - Explicit values. Assigning a function to a variable makes it clear that functions are values. This matches the mental model from Topic 5.1.
- No surprising hoisting. Reading from top to bottom, you know a function exists once you've passed its definition. With declarations, you have to keep "hoisting" in mind to understand which functions are available where.
The trade-off: you lose the ability to call the function above its definition. In practice, most modern code is organized so this isn't needed.
This is a style preference, not a correctness issue. Both forms work. Both produce functions that behave identically when called. The choice is about code organization and readability. Pick a style and stick with it within a file or project.
When Function Declarations Still Win
There's one place function declarations remain popular: when you want a function to be accessible throughout a module, defined alongside other related functions, without thinking about ordering.
// All three functions are accessible throughout
// because of hoisting
console.log(formatName("olena"));
console.log(validateName("olena"));
function formatName(name) {
return capitalize(name);
}
function capitalize(str) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
function validateName(name) {
return typeof name === "string" && name.length > 0;
}
Three functions, all callable from anywhere in the file. No "this depends on that, so put it first" thinking. For small files of related utilities, this can be cleaner than ordering-sensitive const declarations.
Interactive animation: When each form wins - declarations for hoisted top-level helpers, expressions for values you pass around and store.
Common Pitfalls
Warning: The traps with the two forms.
Assuming function expressions are hoisted. They're not, in the usable sense. The variable name might be hoisted, but the function value isn't available until the assignment line. Beginners coming from function-declaration habits sometimes call expressions too early and get TypeError or ReferenceError.
Forgetting the semicolon after a function expression. Function declarations don't need a semicolon. Function expressions do, because they're part of a statement (
const x = ...;). Forgetting the semicolon usually doesn't break code, but it can cause subtle issues with automatic semicolon insertion in edge cases.Reassigning function declarations accidentally.
function greet() {}creates a regular variable binding. You can later writegreet = "something else"and the function is gone. Withconst greet = function() {}, the same reassignment throws an error — which is usually what you want.
Why This Matters in Interviews
Interview: "What's the difference between a function declaration and a function expression?" comes up in many JavaScript interviews. The expected answer covers three points:
- Hoisting. Declarations are fully hoisted. Expressions are not — the variable is hoisted but the function value isn't usable until assignment.
- Reassignability. Declarations create regular bindings that can be reassigned. Expressions assigned to
constcannot.- Style preference. Modern style typically favors function expressions assigned to
const, especially with arrow functions (next Topic).Topic 5.8 — Interview Lens: Functions and Higher-Order Patterns — covers the deeper interview-prep treatment, including the output-prediction puzzles that test whether you understand the hoisting differences.
Try It
Exercise: Predict the output of each line, then run them:
console.log(typeof declared); console.log(typeof expressed); // declared(); // expressed(); function declared() { return "I'm declared"; } const expressed = function() { return "I'm expressed"; }; console.log(declared()); console.log(expressed());What does
typeof declaredreturn at the top? What doestypeof expressedreturn? Why are they different? Once you can trace through this, you've internalized the hoisting differences between the two forms.Uncomment the two commented-out calls and run again. One of them works. One throws a different kind of error. Identify which is which and explain why.