Skip to content
JavaScript js functions 4 min read

IIFE

An Immediately Invoked Function Expression (IIFE, pronounced “iffy”) is a function that runs the moment it is defined. By wrapping a function in parentheses and calling it on the spot, you create an isolated scope whose variables never leak into the surrounding code. Before JavaScript had block scoping (let/const) and native modules, the IIFE was the primary tool for keeping the global namespace clean.

The basic syntax

A function declaration cannot be invoked immediately because the parser expects a statement, not an expression. The fix is to turn the function into an expression — usually by wrapping it in parentheses — and then call it with a trailing ().

(function () {
  const secret = "hidden from the outside";
  console.log(secret);
})();

Output:

hidden from the outside

The outer parentheses force the engine to treat function as an expression. The final () invokes it right away. You can also use the arrow-function form, which is more common in modern code:

(() => {
  const id = Math.random().toString(36).slice(2);
  console.log(`Generated id: ${id}`);
})();

Any expression context works as a “trigger” too — a leading !, +, or void operator achieves the same effect, though the wrapping-parentheses style is the clearest and most widely recognized.

!function () {
  console.log("Also an IIFE");
}();

Why IIFEs mattered: private scope

In the var era, every variable declared at the top level of a script became a global. Two scripts that both used a variable named count would silently clobber each other. An IIFE solved this by giving each chunk of code its own function scope.

var counter = (function () {
  let count = 0; // private — unreachable from outside

  return {
    increment() {
      count += 1;
      return count;
    },
    reset() {
      count = 0;
    },
  };
})();

console.log(counter.increment()); // 1
console.log(counter.increment()); // 2
console.log(counter.count);       // undefined

Output:

1
2
undefined

Here count lives entirely inside the IIFE’s closure. The returned object exposes only the methods you choose, giving you true data privacy. This is the foundation of the classic module pattern.

Tip: The “encapsulation” comes from the closure, not the immediate call. The IIFE just gives that closure a clean, throwaway scope to live in.

Passing arguments

Because an IIFE is an expression, you can feed it arguments. A common idiom passes in globals (like window or a library) so they can be aliased locally and minified safely.

((global, factory) => {
  global.MyLib = factory();
})(globalThis, () => {
  return { version: "1.0.0" };
});

console.log(MyLib.version); // 1.0.0

The async IIFE

Top-level await is only available inside ES modules. In a plain script or a CommonJS file, an async IIFE is the standard way to use await at the entry point.

(async () => {
  const res = await fetch("https://api.example.com/status");
  const data = await res.json();
  console.log(data.status);
})();

This wraps your asynchronous logic in a function so await is legal, while still running immediately.

Modern relevance

Block-scoped declarations and native modules have made most IIFE use cases obsolete. The table below shows what replaced them.

Old IIFE use caseModern replacement
Private top-level variables{ } block with let/const
Avoiding global namespace collisionsES modules (import / export)
Top-level awaitTop-level await in modules
Module pattern for encapsulationES modules, classes with #private fields
Aliasing globals for minificationModule bundlers (Vite, esbuild, webpack)

A simple block now does what an IIFE once did for scoping:

{
  const temp = computeExpensiveValue();
  useValue(temp);
}
// `temp` is not accessible here

Note: You will still encounter IIFEs constantly in legacy code, transpiled bundle output, and UMD wrappers. Recognizing the pattern is essential even if you rarely write one by hand today.

Best practices

  • Prefer ES modules or a plain { } block for new code that just needs scoping — reach for an IIFE only when a module isn’t available.
  • Use the async IIFE pattern to run await in non-module scripts or CommonJS entry points.
  • Always terminate the statement before an IIFE with a semicolon (or start the IIFE with ;) to avoid ASI pitfalls when concatenating files.
  • Favor the wrapping-parentheses style (() => {})() over operator tricks like !function(){}() for readability.
  • Return only the API you intend to expose; keep everything else inside the closure for genuine privacy.
  • Don’t reach for an IIFE where a named function or module would communicate intent more clearly.
Last updated June 1, 2026
Was this helpful?