Academic Block

JS Immediately Invoked Functions (IIFE) Get Certified

An Immediately Invoked Function Expression, or IIFE (pronounced “iffy”), is a function that runs as soon as it is defined, without needing to be called separately afterward.

IIFEs were especially popular before ES6 modules existed, as a way to avoid polluting the global scope. They’re less common today, but understanding them is still useful for reading older code and for a few specific modern use cases.

In this lesson, you will learn the syntax for writing an IIFE, why developers use them, and a few variations you might encounter.

Basic IIFE Syntax

An IIFE wraps a function expression in parentheses, then immediately calls it with a second set of parentheses.

What this code does: An IIFE (Immediately Invoked Function Expression) defines a function and runs it right away. The outer parentheses make it an expression; the final () calls it immediately.

(function () {
    console.log("Application initialized.");
})();

The outer parentheses around the function are required. Without them, JavaScript would try to parse function as a function declaration, which cannot be immediately invoked this way.

Why the Parentheses Are Required

Wrapping the function in parentheses turns it into a function expression instead of a function declaration. Function expressions can be called immediately; function declarations cannot be followed directly by () in this way.

What this code does: A function declaration cannot be invoked with trailing () like that— it causes a SyntaxError. Wrapping the function in parentheses turns it into an expression that can be called immediately.

// This causes a SyntaxError:
// function () {
//     console.log("This won't work.");
// }();
console.log("Wrapping in parentheses turns it into an expression.");

Different IIFE Styles

There are a few equally valid ways to write the parentheses around an IIFE. Both styles below behave identically.

What this code does: Two common IIFE styles: parentheses around the whole call, or around just the function. Both run immediately and produce the same result.

(function () {
    console.log("Style 1: parentheses around the whole thing.");
})();
(function () {
    console.log("Style 2: parentheses around just the function.");
}());

Arrow Function IIFE

An arrow function can also be used to create an IIFE, following the same wrapping pattern.

What this code does: Arrow functions can also be used as IIFEs. The arrow function is wrapped in parentheses and invoked immediately with ().

(() => {
    console.log("Arrow function IIFE running now.");
})();

Passing Arguments to an IIFE

Just like any other function, an IIFE can accept parameters. The arguments are passed in the final set of parentheses that invokes it.

What this code does: IIFEs can accept arguments. Values passed in the final () become parameters inside the function—here, app name and version are logged on startup.

(function (appName, version) {
    console.log(appName + " v" + version + " starting up...");
})("InventoryTracker", "2.1");

Avoiding Global Scope Pollution

One of the main historical reasons to use an IIFE is to keep variables out of the global scope, preventing them from accidentally colliding with other scripts on the same page.

What this code does: Variables declared inside an IIFE stay private. cacheVersion is available inside the IIFE but causes a ReferenceError outside it.

(function () {
    const cacheVersion = "v3";
    const buildId = "20240611";
    console.log("Cache version:", cacheVersion, "| Build:", buildId);
})();
try {
    console.log(cacheVersion);
} catch (err) {
    console.log("Error:", err.message);
}

Since cacheVersion and buildId are declared inside the IIFE, they simply don’t exist outside of it — there’s no risk of another script overwriting them by accident.

IIFEs That Return a Value

An IIFE can return a value, which is often used to expose only a specific, limited API while keeping other details private. This is sometimes called the module pattern.

What this code does: The classic module pattern: an IIFE returns an object with methods that close over private data. stockLevels is hidden; only getStock and sell can access it.

const inventory = (function () {
    let stockLevels = { apples: 50, bananas: 30 };
    return {
        getStock: function (item) {
            return stockLevels[item];
        },
        sell: function (item, quantity) {
            stockLevels[item] -= quantity;
        }
    };
})();
console.log(inventory.getStock("apples"));
inventory.sell("apples", 5);
console.log(inventory.getStock("apples"));

The stockLevels object is private, accessible only through the two functions returned by the IIFE.

Named IIFEs

An IIFE can have a name, which is mainly useful for debugging — it will show up more clearly in stack traces and browser developer tools.

What this code does: A named IIFE can appear in stack traces, which helps with debugging. The name is local to the function and does not pollute the outer scope.

(function initializeApp() {
    console.log("Named IIFE running.");
})();

The name initializeApp is only accessible from inside the function itself, since it’s still a function expression, not a declaration.

Async IIFEs

An IIFE can also be declared with async, allowing you to use await immediately, even outside of an async function elsewhere in your code.

What this code does: An async IIFE lets you use await at the top level of a script without wrapping everything in an async function. The task starts, waits briefly, then finishes.

(async function () {
    console.log("Starting async task...");
    await new Promise(resolve => setTimeout(resolve, 500));
    console.log("Async task finished.");
})();

IIFEs vs. Modern Alternatives

Goal IIFE Approach Modern Alternative
Avoid polluting global scope Wrap code in an IIFE Use an ES6 module (import/export)
Private state Module pattern via IIFE Class with private fields, or module-scoped variables
Run async code immediately Async IIFE Top-level await (in supporting environments)

Practical Example

Let’s use an IIFE to generate a one-time configuration object based on the current environment, without leaving any temporary variables behind in the global scope.

What this code does: A practical config setup: the IIFE computes values once and returns a config object. Private flags like isProduction never leak into the global scope.

const config = (function () {
    const isProduction = false;
    const baseUrl = isProduction
        ? "https://api.example.com"
        : "https://staging-api.example.com";
    return {
        baseUrl: baseUrl,
        timeoutMs: isProduction ? 5000 : 15000
    };
})();
console.log(config);

Common Mistakes With IIFEs

  • Forgetting the parentheses around the function, resulting in a SyntaxError.
  • Forgetting the final () that actually invokes the function, which leaves it defined but never called.
  • Overusing IIFEs in modern projects where ES6 modules already provide proper scoping without the extra syntax.
  • Trying to call an IIFE again later, forgetting that it only runs once at the moment it’s defined.
  • Missing a semicolon before an IIFE on the line above it, which can occasionally cause the two lines to be interpreted together incorrectly.

Best Practices for IIFEs

  • Prefer ES6 modules over IIFEs for scoping in modern JavaScript projects, when modules are available.
  • Use an IIFE for quick, one-time setup logic that shouldn’t leave variables behind in the global scope.
  • Name an IIFE when it’s useful for debugging or when it will appear in stack traces.
  • Keep IIFEs short and focused on a single setup task.
  • End the statement before an IIFE with a semicolon to avoid ambiguous parsing.

Quick Summary

  • An IIFE is a function that runs immediately after it’s defined.
  • Wrapping the function in parentheses turns it into an expression, which can then be invoked right away.
  • IIFEs can accept arguments, return values, and even be declared with async.
  • A common historical use case is avoiding global scope pollution and creating private state (the module pattern).
  • Modern JavaScript often uses ES6 modules instead of IIFEs for these same goals.

Try It Yourself

Modify the IIFE below. Try changing the arguments passed to it, or adding a return statement to expose a value.

What this code does: Practice example: change the username argument or add a return value, then click Test My Code to see how the IIFE runs immediately with the new input.

(function (username) {
    console.log("Welcome back, " + username + "!");
})("Michael");
Tip: Before reaching for an IIFE in modern code, ask whether an ES6 module would solve the same problem more simply. IIFEs still show up in older codebases and certain bundler outputs, so understanding them is worthwhile even if you rarely write one yourself.

Understanding IIFEs is useful before moving on to modules, Promises, and more advanced JavaScript concepts.

🧪 Test Your JavaScript Code

Edit the code on the left and click “Run Code” to see the console output on the right.

📝 JavaScript Code
👁 Console Output

Click “Run Code” to see the output here.

Ctrl + Enter to run code Esc to close