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 case | Modern replacement |
|---|---|
| Private top-level variables | { } block with let/const |
| Avoiding global namespace collisions | ES modules (import / export) |
Top-level await | Top-level await in modules |
| Module pattern for encapsulation | ES modules, classes with #private fields |
| Aliasing globals for minification | Module 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
awaitin 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.