Skip to content

Function Expressions

In JavaScript, functions are values. They can be assigned to variables, stored in objects, and passed to other functions. A function expression makes this visible: instead of declaring a function with the function keyword at the top level, you assign it to a variable.

const formatPrice = function(price) {
return '$' + price.toFixed(2);
};
console.log(formatPrice(149)); // '$149.00'

The function is written as an expression — no function name after the keyword — and assigned to a const variable. You call it exactly the same way as a declaration: formatPrice(149).

The function in the example above is anonymous — no name between function and (:

const formatPrice = function(price) { ... }; // anonymous

You can add a name, making it a named function expression:

const formatPrice = function formatPriceFn(price) {
return '$' + price.toFixed(2);
};

The internal name (formatPriceFn) is only accessible inside the function body itself and is useful for stack traces when debugging — the name appears in error messages rather than anonymous. In everyday code, anonymous expressions are more common.

Function expressions are not hoisted. You cannot call them before they are defined.

// With a function declaration — works fine (hoisted)
formatSummary();
function formatSummary() {
console.log('Cascade Ridge Hike');
}
// With a function expression — crashes
formatSummary(); // ReferenceError: Cannot access 'formatSummary' before initialization
const formatSummary = function() {
console.log('Cascade Ridge Hike');
};

The ReferenceError is immediate and clear. const and let declarations are not available before their definition line — this is called the temporal dead zone. The variable exists but is not initialized until the assignment runs.

Because a function expression is just a variable holding a value, functions can be:

Stored in a variable:

const format = formatPrice;
console.log(format(149)); // '$149.00'

Stored in an object:

const tourHelpers = {
formatPrice: function(price) { return '$' + price.toFixed(2); },
isAvailable: function(available) { return available === true; },
};
console.log(tourHelpers.formatPrice(149));

Passed as an argument:

function applyToPrice(price, fn) {
return fn(price);
}
console.log(applyToPrice(149, formatPrice)); // '$149.00'

Passing functions as arguments — the pattern in the last example — is called passing a callback. You will use this pattern extensively in Module 06 when attaching event listeners.

Use declarations forUse expressions for
Top-level named utility functionsAssigning conditionally
Functions that need hoistingPassing as a callback argument
Functions defined once at the module levelStoring in an object or array

In practice, many developers use arrow functions (next lesson) for expressions and reserve function declarations for top-level named utilities. The key rule: define before you call.

Rewrite formatPrice from Lesson 02 as a function expression and verify it works the same way:

const formatPrice = function(price) {
return '$' + price.toFixed(2);
};
console.log(formatPrice(149)); // '$149.00'
console.log(formatPrice(99)); // '$99.00'

Then move the console.log(formatPrice(149)) call to before the const formatPrice = ... line and reload. Observe the ReferenceError — this is the hoisting difference in action. Restore the call to after the definition when done.

  • A function expression assigns a function to a variable: const fn = function() { ... }.
  • Named expressions include a function name useful for stack traces; anonymous expressions are more common.
  • Function expressions are not hoisted — calling one before its definition throws a ReferenceError.
  • Functions are values: they can be stored in variables, objects, and passed as arguments to other functions.
  • Passing a function as an argument is called a callback — the pattern behind event listeners in Module 06.