Skip to main content

Command Palette

Search for a command to run...

Two Ways to Write a Function: Declarations vs. Expressions in JavaScript

They look similar, but the JavaScript engine treats them entirely differently. Learn how functions work under the hood and why hoisting matters.

Published
5 min read
H
CS Graduate | Technical Writing | Software development | 20K+ impressions

If you have been following along with this series, you know that variables hold your data, and operators allow you to manipulate it. But as your codebase grows, you will find yourself writing the exact same manipulation logic over and over again.

Programming is about efficiency. If you write the same block of code three times, it shouldn't exist three times. It should be packaged into a single, reusable block.

In JavaScript, that block is called a function.

You can think of a function as a custom machine. You feed raw materials (inputs) into one end, the machine does a specific job, and it spits a finished product (output) out the other side. You build the machine once, and then you can use it as many times as you want.

There are two primary ways to build this machine in JavaScript: the Function Declaration and the Function Expression. To the untrained eye, they do the exact same thing. But under the hood, the JavaScript engine treats them entirely differently.

Here is exactly what you need to know.


1. The Function Declaration

The Function Declaration is the traditional, classic way to write a function. It is standalone, explicit, and begins with the function keyword, followed by the name you want to give it.

// Defining the function (building the machine)
function calculateTotal(price, tax) {
  return price + tax;
}

// Calling the function (using the machine)
let finalPrice = calculateTotal(100, 20);
console.log(finalPrice); // Output: 120

It is straightforward. You declare exactly what it is (function), you give it a name (calculateTotal), you define its inputs in parentheses, and you write the logic inside the curly braces.


2. The Function Expression

To understand a Function Expression, you have to understand a fundamental rule of JavaScript: Functions are just values.

In JavaScript, a function is not a special, magical entity. It is a piece of data, no different than a String, a Number, or a Boolean. And if a function is just a value, that means we can take it and store it inside a variable.

When you take a function and put it inside a variable, it is called a Function Expression.

// We create a variable, and assign a function to it
const calculateTotal = function(price, tax) {
  return price + tax;
};

let finalPrice = calculateTotal(100, 20);
console.log(finalPrice); // Output: 120

Notice the subtle difference? We didn't give the function itself a name. We created an "anonymous" function and assigned it to a const variable named calculateTotal.

Because it is an assignment (we are declaring a variable), the block ends with a semicolon ;.


The Big Difference: Hoisting

If both approaches allow us to run calculateTotal(100, 20), why do we have two different syntaxes? Does it actually matter which one you use?

Yes. It matters because of a mechanism in JavaScript called Hoisting.

When you run a JavaScript file, the engine doesn't just blindly execute the code from top to bottom in one go. It actually does a quick invisible "prep-pass" over your file first. During this prep-pass, it looks for any Function Declarations and hoists (pulls) them to the absolute top of the file's memory.

Because of this, you can safely call a Function Declaration before you actually define it in your code.

// This works perfectly fine!
greetUser(); 

function greetUser() {
  console.log("Hello, welcome back!");
}

Even though greetUser() is called on line 1, the code doesn't crash. The engine already knows what greetUser is because it pulled the declaration to the top before the code started running.

Function Expressions do not behave this way.

Because a Function Expression is tied to a variable (const or let), it follows the rules of variables. As we learned in Understanding Variables and Data Types, you cannot use a const or let variable before you define it.

// This will CRASH your program!
greetUser(); // ReferenceError: Cannot access 'greetUser' before initialization

const greetUser = function() {
  console.log("Hello, welcome back!");
};

The prep-pass sees the variable greetUser, but it does not pull the function logic up with it. When the engine hits line 1, the variable exists, but it hasn't been initialized with the function yet. The program panics and throws an error.


Summary: Which one should you use?

Here is a quick reference table to summarize the mechanics:

Feature Function Declaration Function Expression
Syntax function name() {} const name = function() {}
Hoisting Yes. Can be called before it is defined in the code. No. Must be defined before it is called.
Identity Always has a name. Usually anonymous, stored in a variable.

So, which one is better?

Neither is inherently better; they just serve different structural purposes.

Many developers prefer Function Declarations for global, utility functions. Because they are hoisted, you can put all of your helper functions at the very bottom of your file, keeping the top of your file clean and focused on the main logic.

However, modern JavaScript heavily favors Function Expressions—specifically a newer, shorter syntax called the Arrow Function (which we will cover in a future post). Storing functions in const variables forces you to write predictable, top-to-bottom code. If you must define a function before you use it, you are less likely to create tangled, spaghetti-code execution paths.

Whichever you choose, understand the physics of the language. When you inevitably encounter a ReferenceError:[X] is not a function in your terminal, you will now know exactly why the engine is confused, and exactly how to fix it.