Skip to main content

Command Palette

Search for a command to run...

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

Published
10 min read
Function Declaration vs Function Expression: What’s the Difference?

Functions are one of the most important building blocks in JavaScript. If you understand functions well, everything else (arrays, objects, async code, frameworks) gets much easier.

In this article, we’ll go from what functions are to function declarations vs function expressions, with hoisting explained in simple terms and practical examples you can try immediately.

What Is a Function and Why Do We Need It?

A function is a reusable block of code that performs a specific task.

Instead of repeating the same code over and over, you:

  • Define it once as a function.

  • Call (or “invoke”) it whenever you need it.

Simple example: adding two numbers

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

const result = add(3, 5);
console.log(result); // 8

Why this is useful:

  • Reusability: you can call add(3, 5), add(100, 25), etc.

  • Organization: complex logic can be broken into smaller named functions.

  • Readability: add(price, tax) It is easier to understand than to do price + tax everywhere.

Function Declaration Syntax

A function declaration is the “classic” way to define a function in JavaScript.

Syntax:

function functionName(parameter1, parameter2, ...) {
  // function body
  // optional: return something
}

Example: multiply two numbers:

function multiply(a, b) {
  return a * b;
}

console.log(multiply(4, 6)); // 24

Key points:

  • Starts with the function keyword.

  • Has a name (multiply).

  • Can have parameters (a, b).

  • Can be used return to send a value back.

Characteristics of function declarations

  • Named: they always have a name.

  • Hoisted: You can call them before their definition in the code (we’ll explain hoisting soon).

  • Typically used for top-level, reusable utilities that you want available everywhere in a file.

Function Expression Syntax

A function expression is when you create a function and assign it to a variable.

Syntax:

const functionName = function(parameter1, parameter2, ...) {
  // function body
};

or with an anonymous function:

const functionName = function(a, b) {
  return a * b;
};

Example: multiply two numbers (function expression)

const multiply = function(a, b) {
  return a * b;
};

console.log(multiply(4, 6)); // 24

Here:

  • function(a, b) { ... } is a function expression.

  • It has no name (this is an anonymous function).

  • It is assigned to a variable called multiply.

You can also use arrow functions (a more modern syntax), but we’ll stick to regular function expressions for clarity.

Function Declaration vs Function Expression (Side by Side)

Let’s see the same logic written both ways.

Function declaration

function greet(name) {
  return `Hello, ${name}!`;
}

console.log(greet("Alice")); // "Hello, Alice!"

Function expression

const greet = function(name) {
  return `Hello, ${name}!`;
};

console.log(greet("Alice")); // "Hello, Alice!"

At runtime, once everything is defined, both work similarly:

  • You call them with greet("Alice").

  • They return the same result.

The big differences are:

  • How they are defined in the code (syntax).

  • How do they behave with hoisting?

  • How you use them in the design and structure of your code.

Key Differences: Declaration vs Expression

Here’s a high-level comparison.

1. Syntax and Style

  • Declaration
function add(a, b) {
  return a + b;
}
  • Expression
const add = function(a, b) {
  return a + b;
};
  • Declarations feel more “global” and top-level.

  • Expressions feel more “value-like”: a function as data assigned to a variable.

2. Hoisting Behavior

Hoisting is the JavaScript behavior where certain things are effectively “moved” to the top of their scope before the code runs.

At a very high level:

  • Function declarations are fully hoisted.

  • Function expressions (assigned to variables) are not usable before the line where they are defined.

We’ll show concrete examples in the hoisting section below.


3. Naming and Flexibility

  • Declarations: always named.

  • Expressions: can be anonymous or named.

This allows function expressions to be:

  • Passed as arguments to other functions.

  • Stored in objects or arrays.

  • Created conditionally.

Example: function expression as a callback

setTimeout(function() {
  console.log("Executed later");
}, 1000);

Here we didn’t even save it to a variable—we just used the function expression directly.

4. When You Might Prefer One Over the Other

  • Function declarations:

    • Good for core utilities and helpers.

    • Good when you want functions available throughout the file.

    • Good when readability is improved by seeing all the “main” functions at the top.

  • Function expressions:

    • Good for callbacks (e.g., event handlers, array methods).

    • Good when you want to control the scope and not pollute the global or outer scope with many names.

    • Good when you treat functions like values (assign, pass around, etc.).

We’ll go deeper into “when to use which” later.

Hoisting: Simple, Practical Explanation

Let’s keep hoisting high-level and practical, without going deeply into execution contexts or spec details.

The mental model

You can think of JavaScript as doing two simple steps:

  1. Setup phase: It scans your code and:

    • Registers function declarations.

    • Reserves space for variables.

  2. Execution phase: It runs your code line by line.

Because of this:

  • You can call function declarations before their actual code appears.

  • But you cannot reliably use a function expression before the line where it’s defined.

Hoisting with Function Declarations

You can do this:

sayHello("Alice"); //  Works, even though sayHello is defined later

function sayHello(name) {
  console.log(`Hello, ${name}!`);
}

Why it works:

  • During the setup phase, JavaScript sees the function declaration function sayHello(...) { ... } and registers it.

  • When it starts executing line by line, sayHello it already exists.

Hoisting with Function Expressions

You cannot safely do this:

sayHello("Alice"); //  Error (or undefined), depending on how it's declared

const sayHello = function(name) {
  console.log(`Hello, ${name}!`);
};

Why it fails:

  • During setup, JavaScript knows there is a variable sayHello, but it does not yet assign the function to it.

  • At the time you call sayHello("Alice"), sayHello is not ready.

  • Result: you get an error like “Cannot access 'sayHello' before initialization” or similar.

Same idea with let or var, but details differ. The safe rule of thumb:

- Do not call function expressions before their line of definition.

When to Use Function Declarations vs Function Expressions

Both are valid and useful. The trick is choosing based on intent and code organization.

Use Function Declarations When…

  • You’re defining main utilities or core helpers in a file.

  • You like having your API-like functions at the top of the file.

  • You want them to be fully available anywhere in that scope (because of hoisting).

Example: core helpers

function calculateTotal(price, taxRate) {
  return price + price * taxRate;
}

function formatCurrency(amount) {
  return `$${amount.toFixed(2)}`;
}

// Usage below...

Use Function Expressions When…

  • You’re defining functions inside other functions.

  • You’re using callbacks (e.g., map, filter, setTimeout, event handlers).

  • You want to limit the scope and avoid putting lots of function names in the outer level.

  • You’re building objects or modules where functions are properties.

Examples:

// 1. Callback in array methods
const numbers = [1, 2, 3];

const doubled = numbers.map(function(n) {
  return n * 2;
});

// 2. Methods in an object
const calculator = {
  add: function(a, b) {
    return a + b;
  },
  multiply: function(a, b) {
    return a * b;
  }
};

Assignment Idea (Hands-On Practice)

Here’s a small assignment you can try in a .js file or DevTools console.

1. Function Declaration: multiply two numbers

Write a function declaration that multiplies two numbers:

function multiplyDeclaration(a, b) {
  return a * b;
}

2. Function Expression: same logic

Write a function expression with the same logic:

const multiplyExpression = function(a, b) {
  return a * b;
};

3. Call both functions and print the results

console.log("Declaration result:", multiplyDeclaration(3, 4)); // 12
console.log("Expression result:", multiplyExpression(3, 4));  // 12

4. Try calling them BEFORE their definitions

Now, let’s experiment with hoisting.

Try this version:

console.log(multiplyDeclaration(2, 5)); // What happens?
console.log(multiplyExpression(2, 5));  // What happens?

function multiplyDeclaration(a, b) {
  return a * b;
}

const multiplyExpression = function(a, b) {
  return a * b;
};

What you should observe:

  • multiplyDeclaration(2, 5) works.

  • multiplyExpression(2, 5) throws an error (because the variable is not initialized yet at that point).

This small experiment makes hoisting very concrete.

Visual: Comparison Table (Declaration vs Expression)

You can imagine/translate this into a diagram or table in your slides or notes.

Aspect Function Declaration Function Expression
Basic form function foo() { ... } const foo = function() { ... };
Name Always has a name Can be anonymous or named
Hoisting Fully hoisted (can call before definition) Not usable before the definition line
Common usage Core helpers, top-level utilities Callbacks, inline logic, assigning to variables/objects
Readability Good for “API-like” public functions Good for keeping related logic close to where it’s used
Flexibility as a value Less obvious, but still a value Very natural: it’s literally assigned to a variable

Visual: Simple Execution Flow of a Function Call

Here’s a conceptual, text-based “diagram” of a function call flow.

  1. Your code:
function add(a, b) {
  return a + b;
}

const result = add(2, 3);
console.log(result);
  1. High-level flow:
  • add is defined (either by declaration or expression).

  • You call add(2, 3).

  • JavaScript:

    • Takes a = 2, b = 3.

    • Runs the function body.

    • Reaches return a + b.

    • Gives back 5.

  • result becomes 5.

  • console.log(result) prints 5.

No need to think about execution contexts or call stacks at this stage—just follow input → function → output.

Summary

  • Functions are reusable blocks of code that take input, optionally process it, and optionally return a result.

  • Function declarations use function name(...) { ... }, are hoisted and are great for core helpers.

  • Function expressions create functions as values and assign them to variables; they’re not callable before their line, and are perfect for callbacks, inlining, and scoped utilities.

  • Hoisting (simple rule):

    • You can call function declarations before they appear.

    • You should not call function expressions before their line of definition.

If you internalize these ideas, you’ll have a solid foundation for understanding more advanced topics like:

  • Arrow functions

  • Methods in classes and objects

  • Closures and higher-order functions

  • Async functions and callbacks

Happy coding!