In JavaScript, functions are “first-class citizens.” This means that the language treats functions just like any other value (like a string, a number, or an object). You can pass a function as an argument to another function, return a function from another function, and most importantly, assign a function to a variable.
When you define a function as part of a larger expression—typically by assigning it to a variable—it is known as a function expression. The main difference between a function expression and a function declaration is the function name, which can be omitted in function expressions to create what we call anonymous functions.
Let’s look at how we can assign an anonymous function to a constant variable add and then invoke it using that variable name.
javascript
const add = function(a, b) { return a + b;};console.log(add(2, 3));
Output
5
Your output
The Danger of Hoisting
Function declarations in JavaScript are famously “hoisted.” This means the JavaScript engine effectively moves the function definition to the top of the file before running any code, allowing you to call a function before it actually appears in your script.
However, function expressions are not hoisted. The variable declaration itself might be hoisted (depending on whether you used var, let, or const), but the initialization (the actual function assignment) is not. This leads to one of the most common beginner pitfalls in JavaScript: trying to call a function expression before it has been properly assigned.
Can you predict what kind of error occurs if we try to call a const function expression too early? We’ll wrap it in a try...catch block so we can catch the error and print its name instead of crashing the program.
Predict the outputjavascript
Read the code. What exactly will it print? Commit to an answer before you look.
Because we used const, the variable greet is in the “Temporal Dead Zone” until its declaration is reached, which results in a ReferenceError. If we had used var, it would have been initialized as undefined, resulting in a TypeError (because undefined is not a function).
Anatomy of Function Forms
Let’s visualize the structural difference between a standard function declaration and an anonymous function expression.
A function declaration must be named and is hoisted, so it can be called from anywhere in its scope. A function expression is assigned to a variable and only exists once that line has run.
Named Function Expressions
While function expressions are often anonymous, they don’t have to be. A function expression can optionally have a name. If it is named, the name is local only to the function body. This is extremely useful for recursive functions, or when you want the function name to appear clearly in a stack trace during debugging.
javascript
const math = function factorial(n) { if (n <= 1) return 1; return n * factorial(n - 1);};console.log(math(3));
Output
6
Your output
If you try to access the function name outside its body, it simply doesn’t exist in that scope, which will result in a ReferenceError.
javascript
try { const math = function factorial(n) { return n; }; console.log(factorial(3));} catch(e) { console.log(e.name);}
Output
ReferenceError
Your output
Read-Only Names
Unlike function declarations (which can theoretically be reassigned if not using strict block scoping), the internal name of a named function expression is strictly read-only. Trying to reassign it inside its body throws a TypeError in strict mode, or silently fails in non-strict mode.
Because function expressions evaluate to a function object immediately, they can be invoked the exact moment they are defined. This pattern is known as an Immediately Invoked Function Expression (IIFE). By wrapping the anonymous function in parentheses (), you tell the JavaScript engine to treat it as an expression, and the trailing () executes it instantly.
javascript
(function() { console.log('I ran immediately!');})();
Output
I ran immediately!
Your output
This pattern is frequently used in older codebases to create private scopes and avoid polluting the global namespace.
Visualizing Execution
Let’s visualize how a function expression binds parameters and executes compared to a function declaration.
javascript · visualize
const multiply = function(a, b) { let result = a * b; console.log(result);};multiply(3, 4);
Check yourself
Are function expressions hoisted in JavaScript?
Reveal answer
No, they are not hoisted — Function expressions are not hoisted, meaning they cannot be used before they are defined in the code.
What happens if you try to use the name of a named function expression outside of its body?
Reveal answer
It throws a ReferenceError — The name of a named function expression is only accessible from within its own function body. Accessing it outside throws a ReferenceError.
What error is thrown when you try to reassign the name of a named function expression inside its body in strict mode?
Reveal answer
TypeError — The name of a function expression is read-only inside its body. In strict mode, reassigning it throws a TypeError.
Challenges
Challenge 1 +20 XP
Assign an anonymous function to the variable 'sayHi' that returns the string 'Hi'.