JavaScript Function Expressions
In JavaScript, functions can be defined in two main ways: function declarations and function expressions. A function expression assigns a function to a variable. Understanding the differences between them — especially regarding hoisting — is crucial for writing predictable code.
Function Declaration vs Expression
A function declaration uses the 'function' keyword followed by a name. A function expression assigns an (often anonymous) function to a variable. The key difference is hoisting: declarations are hoisted, expressions are not.
// Function Declaration — hoisted, can be called before definition
console.log(add(2, 3));
function add(a, b) {
return a + b;
}
// Function Expression — NOT hoisted
// console.log(subtract(5, 2)); // Would throw ReferenceError
const subtract = function(a, b) {
return a - b;
};
console.log(subtract(5, 2));Anonymous Function Expressions
Anonymous functions have no name after the 'function' keyword. They are commonly used as function expressions, callbacks, and event handlers. The variable name serves as the identifier.
// Anonymous function expression
const greet = function(name) {
return `Hello, ${name}!`;
};
console.log(greet("Alice"));
// Anonymous function as a callback
let numbers = [3, 1, 4, 1, 5];
let sorted = numbers.sort(function(a, b) {
return a - b;
});
console.log(sorted);
// Anonymous function in setTimeout
setTimeout(function() {
console.log("Delayed message");
}, 0);
// Arrow function (also anonymous)
const double = (n) => n * 2;
console.log(double(5));Named Function Expressions
A named function expression has a name, but that name is only accessible inside the function itself. This is useful for recursion and for better stack traces during debugging.
// Named function expression
const factorial = function fact(n) {
if (n <= 1) return 1;
return n * fact(n - 1); // 'fact' is accessible inside
};
console.log(factorial(5));
console.log(factorial(10));
// The name 'fact' is NOT accessible outside
// console.log(fact(5)); // Would throw ReferenceError
// Named expressions have better debugging
const handler = function onClickHandler() {
return "clicked";
};
console.log(handler.name);
// Anonymous expression uses variable name
const myFunc = function() {};
console.log(myFunc.name);Hoisting Differences
Function declarations are fully hoisted — they can be called before they appear in code. Function expressions (with var, let, or const) are NOT hoisted — the variable exists but the function is not assigned yet.
// Declarations are hoisted completely
console.log(declaredFunc());
function declaredFunc() {
return "I'm hoisted!";
}
// var expression: variable hoisted as undefined
try {
console.log(varFunc());
} catch (e) {
console.log("var error:", e.message);
}
var varFunc = function() { return "var func"; };
// let/const expression: in temporal dead zone
try {
console.log(constFunc());
} catch (e) {
console.log("const error:", e.message);
}
const constFunc = function() { return "const func"; };
// After assignment, everything works
console.log(varFunc());
console.log(constFunc());IIFE (Immediately Invoked Function Expression)
An IIFE is a function expression that is defined and immediately called. It creates a private scope, preventing variables from polluting the global namespace. This was essential before ES6 modules and let/const.
// Classic IIFE
(function() {
let secret = "hidden";
console.log("IIFE executed, secret:", secret);
})();
// 'secret' is not accessible here
// console.log(secret); // ReferenceError
// IIFE with return value
const module = (function() {
let count = 0;
return {
increment() { return ++count; },
getCount() { return count; }
};
})();
console.log(module.increment());
console.log(module.increment());
console.log(module.getCount());
// Arrow IIFE
const result = (() => {
return 42;
})();
console.log(result);| Feature | Declaration | Expression |
|---|---|---|
| Syntax | function name() {} | const name = function() {} |
| Hoisting | Fully hoisted | Not hoisted |
| Name required | Yes | No (anonymous OK) |
| Can be IIFE | No | Yes |
| this binding | Dynamic | Dynamic (arrow: lexical) |
| Use in blocks | Inconsistent | Reliable with let/const |