kyle halleman home

Function declarations versus function expressions in JavaScript

January 30, 2024

Recently, a coworker asked what the difference between a function declaration (i.e., function fn() {}) and a function expression (i.e., const fn = () => {}) was. You will see both used throughout any JavaScript codebase. But why would you choose one over the other? And does it matter?

Largely, it doesn’t matter. Historically, it mattered more. Before block-scoped variables (const and let), we only had var to declare variables. var declarations were hoisted to the top of the scope. Consider the following code:

console.log(hello);

var hello = 'hello';

console.log(hello);

The console logs undefined and "hello".

Now let’s use a block-scoped variable:

console.log(hello);

const hello = 'hello';

console.log(hello);

Now what does the console show? ReferenceError: Cannot access uninitialized variable. The program will grind to a halt. With var, it just gave you an undefined until the variable initialized.

Where’s my function?

Why does this matter? Function declarations are hoisted to the top of the scope, including their value (a major difference with var hoisting). Function expressions follow the same logic as the var and const examples above. A function expression declared with var will have its variable name hoisted, but not its value. A function assigned to a var will only have that value once the program reaches that assignment.

If you write a function expression with a named function declaration, the name of the function declaration cannot be used. You must use the variable name:

const myFunction = function funFunction () {
	console.log('Hello!');
}

myFunction();
// "Hello!"
funFunction();
// Uncaught ReferenceError: funFunction is not defined

On the other hand, a possible advantage to using a function expression over a function declaration would be to do something like this:

var funFunction;

function initialize() {
	funFunction = function () {
		console.log('hello from the inside');
	}
}

initialize();

funFunction();
// -> "hello from the inside"

Why would you want to do this? I don’t know. A cleaner, more idiomatic way to have similar functionality would be to have initialize return the funFunction:

function initialize() {
	var funFunction = function () {
		console.log('hello from the inside');
	}
	return { funFunction }
}

const { funFunction } = initialize();

funFunction();
// -> "hello from the inside"

This does not rely on variables from the parent scope, and makes the contract more explicit.

A function expression, since it gets assigned to a variable, can be reassigned (unless you use const). This means the value of var funFunction = () => {} can change throughout the course of your program. That can be dangerous. The rules for how a function declaration can or cannot be redeclared are confusing. Your best bet—no matter how you create a function—is to just not reassign or redeclare it. If you’re writing a function expression, use const.

Which should you use?

My preference: I always use a function declaration, unless it’s a one-line or a curried function. Then I will use a function expression with arrow function syntax.

I prefer function declarations because it makes it easy to scan your code and separate functions from other variables. Also, its hoisting characteristics can be helpful. Often I will use functions before they’re declared in the source code. Sometimes there’s a chicken-and-egg situation that necessitates using function declarations. And finally, I just like the way they look: function runSomething() {} clearly looks nicer than let runSomething = () => {} 😏.

Which should you use? Whichever you prefer. Or use both—you will probably need to at some point. If you understand the differences, it doesn’t really matter.

Further reading