IIFE — Immediately Invoked Function Expressions
An IIFE (pronounced "iffy") is a function that is defined and called in the same expression. It was a critical pattern in pre-ES6 JavaScript for creating private scopes; modern code uses it less, but you still see it in libraries, bundled scripts, and clever one-liners.
The shape
(function () {
console.log("runs immediately");
})();
// Arrow version
(() => {
console.log("also runs immediately");
})();
// With arguments
(function (name) {
console.log("Hello, " + name);
})("Ada");runs immediately also runs immediately Hello, Ada
The outer parentheses turn the function declaration into an expression, which can then be called with (). Without the parens, the parser sees function at the start of a statement and complains that a name is missing.
Two valid placements of the call parens
// Most common — call parens outside
(function () { /* ... */ })();
// Equally valid — call parens inside
(function () { /* ... */ }());Both work. Douglas Crockford recommended the second form ("dog balls"); the first is what most codebases use today. Either is fine — be consistent.
Why IIFEs existed: private scope
Before let and const, JavaScript had only var, which is function-scoped. The only way to create a private scope was to wrap code in a function. The classic pattern:
var counter = (function () {
var count = 0; // private — invisible from outside
return {
inc: function () { count++; return count; },
reset: function () { count = 0; },
};
})();
console.log(counter.inc()); // 1
console.log(counter.inc()); // 2
console.log(counter.count); // undefined — truly privateThat little block creates a module: data hidden inside, only the returned object exposed. This was the module pattern, and it powered jQuery plugins and most pre-2015 libraries.
Avoiding global pollution
When several <script> tags shared a page, anything declared at the top level lived on window. Wrapping a whole file in an IIFE kept its helpers private.
(function () {
var helper = function () { /* ... */ };
var state = { /* ... */ };
// expose only what you mean to
window.myLib = { doThing: function () { helper(); } };
})();Open the source of older minified libraries (jQuery, Underscore, Backbone) and you will see one giant IIFE wrapping the entire file.
What modern code uses instead
Block-scoped variables —
letandconstare block-scoped, so a{ ... }block on its own gives you a private scope without a function call.Modules — ES modules (
import/export) make every file its own scope automatically. Anything not exported is invisible outside.Async IIFE —
asyncfunctions cannot be top-levelawaitin older runtimes, soawaitat module top is sometimes faked with(async () => { await x(); })().
Modern block-scope replacement
{
const count = 0;
// not visible outside this block
}
// Module replacement
// file: counter.js
let count = 0;
export function inc() { return ++count; }Async IIFE — still useful
When you need top-level await in a place that does not support it (older Node, plain scripts), wrap the work in an async IIFE.
(async () => {
const res = await fetch("/api/data");
const data = await res.json();
console.log(data);
})();