JavaScriptFunctionsProgramming

Function Declaration vs Function Expression: What's the Difference?

Piyush Kumar
4 min read
Function Declaration vs Function Expression: What's the Difference?

Functions are one of the most important building blocks in JavaScript. There are a few common ways to create them. This article focuses on four that you will see most often: function declarations, function expressions, arrow functions, and async functions. We will compare how they look, how they behave, and when to use each one.

1. Function Declarations

A function declaration defines a named function and makes it available before the code runs.

function add(a, b) {
  return a + b;
}

console.log(add(2, 3)); // 5

Key point: declarations are hoisted, so you can call them before their definition in the file.

2. Function Expressions

A function expression creates a function and assigns it to a variable. The function can be named or anonymous, but the variable is what you use to call it.

const add = function (a, b) {
  return a + b;
};

console.log(add(2, 3)); // 5

Key point: only the variable is hoisted, not the function body. Calling it before the assignment fails.

Named vs Anonymous Expressions

Function expressions can be anonymous or named. A named expression can help with debugging.

const greet = function greetUser(name) {
  return `Hi, ${name}`;
};

3. Arrow Functions

Arrow functions are a shorter way to write function expressions. They also handle this differently, which can be useful in callbacks.

const add = (a, b) => {
  return a + b;
};

If the body is a single expression, you can shorten it even more.

const add = (a, b) => a + b;

Key point: arrow functions do not have their own this. They use this from the surrounding scope.

7. Async Functions

Async functions let you write asynchronous code that looks synchronous. They always return a promise.

Async Function Declaration

async function fetchUser() {
  const res = await fetch("/api/user");
  return res.json();
}

Async Function Expression

const fetchUser = async function () {
  const res = await fetch("/api/user");
  return res.json();
};

Async Arrow Function

const fetchUser = async () => {
  const res = await fetch("/api/user");
  return res.json();
};

Key point: await can only be used inside an async function, and the return value is wrapped in a promise.

Hoisting Behavior

This is the biggest practical difference between declarations and expressions.

Declaration Hoisting

sayHello(); // Works

function sayHello() {
  console.log("Hello");
}

Expression Hoisting

sayHello(); // Error

const sayHello = function () {
  console.log("Hello");
};

Why does this happen? The variable sayHello exists, but it is not initialized yet, so calling it before assignment fails.

Quick Comparison Table

FeatureFunction DeclarationFunction ExpressionArrow FunctionAsync Function
Syntaxfunction name() {}const name = function () {}const name = () => {}async function name() {}
HoistingYes, fullyNo, variable onlyNo, variable onlyYes, if declared
Has its own thisYesYesNoDepends on form
Common usageShared helpersCallbacks, inline logicShort callbacksAsync work

When To Use Which

  • Use a function declaration when you want it available anywhere in the file.
  • Use a function expression when you want to define it close to where it is used.
  • Use an arrow function for short callbacks and when you want this from the outer scope.
  • Use an async function when you need await and promises.

Common Mistakes

  • Calling a function expression or arrow function before it is defined
  • Forgetting that arrow functions do not bind their own this
  • Using await outside an async function

Conclusion

Function declarations, expressions, arrow functions, and async functions are the most used ways to create functions in JavaScript. Each has a clear role. Once you understand hoisting and this, choosing the right one becomes easy.