JavaScriptIIFE (Immediately Invoked Functions)

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

JS
(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

JS
// 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:

JS
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 private

That 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.

JS
(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 variableslet and const are 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 IIFEasync functions cannot be top-level await in older runtimes, so await at module top is sometimes faked with (async () => { await x(); })().

Modern block-scope replacement

JS
{
  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.

JS
(async () => {
  const res = await fetch("/api/data");
  const data = await res.json();
  console.log(data);
})();
The takeaway
IIFEs are no longer essential for everyday code — `let`, `const` and modules made them mostly redundant. They still earn their keep for one-off async blocks, bundler output, and tiny scripts that need a private scope without a module system.