Eine IIFE (Immediately Invoked Function Expression) ist eine Funktion, die direkt nach ihrer Definition aufgerufen wird. Das klassische Muster: (function() { ... })();. In der Pre-ES2015-Welt war das die Standard-Form für Scope-Isolation — privater State, kein Verschmutzen des globalen Namensraums, Modul-Ersatz. Mit let/const (Block-Scope) und ESM-Modulen ist die IIFE in modernem Code weitgehend überflüssig — aber sie taucht überall in Legacy-Code auf und hat ein paar Nischen-Anwendungen, in denen sie weiterhin praktisch ist. Dieser Artikel zeigt die Syntax-Varianten, die historischen Gründe und die heute noch sinnvollen Einsatz-Felder.

Grundform

Eine IIFE besteht aus einer Function-Expression — in Klammern, damit der Parser sie als Ausdruck erkennt — und einem direkten Aufruf-Klammerpaar dahinter.

JavaScript iife-grundform.js
(function() {
    const geheim = 'privat';
    console.log('IIFE läuft, geheim =', geheim);
})();

// console.log(geheim); // ReferenceError — von außen unsichtbar
Output
IIFE läuft, geheim = privat

Die Variable geheim lebt nur im Function-Scope der IIFE — von außen unerreichbar. Das ist der Hauptzweck: Scope-Isolation ohne Namens-Konflikte.

Syntax-Varianten

Mehrere Schreibweisen sind gleichwertig. Die Engine muss die Funktion als Expression parsen — daher braucht es einen Trigger, der den Statement-Kontext bricht.

JavaScript iife-varianten.js
// Klammern außen (Crockford-Stil)
(function() { console.log('A'); }());

// Klammern außen, Aufruf außerhalb (verbreiteter)
(function() { console.log('B'); })();

// Unary-Operator als Trigger
!function() { console.log('C'); }();
+function() { console.log('D'); }();
-function() { console.log('E'); }();

// Arrow-IIFE (ES2015+)
(() => { console.log('F'); })();

// Named — für Stack-Traces
(function eigen() { console.log('G'); })();
Output
A
B
C
D
E
F
G

Die Klammer-Form (function() {})() ist die am häufigsten gesehene. Die !function(){}()-Form spart ein Zeichen und war in Minifier-Output beliebt.

IIFE mit Argumenten

Die IIFE kann beim Aufruf Argumente übergeben — praktisch, um globale Objekte (window, jQuery) als lokale Aliase zu übergeben.

JavaScript iife-args.js
// Klassisches Pattern: globale Objekte als Aliase einbinden
(function(global, doc) {
    console.log('Document-Title:', doc.title);
    global.MyLib = { version: '1.0' };
})(globalThis, typeof document !== 'undefined' ? document : { title: 'Node' });

console.log(globalThis.MyLib);
Output
Document-Title: Node
{ version: '1.0' }

Die Aliase haben drei Vorteile: minifier-freundlich (g statt globalThis), schneller (Property-Lookup ohne Scope-Chain), und ein klarer Vertrag zwischen Modul und Umgebung.

IIFE als Modul-Pattern

Pre-ESM war eine IIFE, die ein Objekt zurückgibt, das Standard-Pattern für „Module".

JavaScript iife-modul.js
const Counter = (function() {
    let stand = 0;
    const max = 100;

    function inkrement() {
        if (stand < max) stand++;
        return stand;
    }
    function reset() { stand = 0; }
    function lese() { return stand; }

    return { inkrement, reset, lese };   // public API
})();

Counter.inkrement();
Counter.inkrement();
Counter.inkrement();
console.log('Stand:', Counter.lese());
Counter.reset();
console.log('Nach reset:', Counter.lese());
Output
Stand: 3
Nach reset: 0

Das ist im Kern dieselbe Idee wie ein ESM-Modul: privater State innen, öffentliche API außen. ESM macht es nur deklarativer und tooling-fähiger.

Async-IIFE — Modern noch praktisch

Eine moderne Anwendung: async-IIFE für Top-Level-Init in CommonJS oder Skripten ohne Top-Level-Await-Support.

JavaScript async-iife.js
(async function init() {
    console.log('Start');
    // simuliertes asynchrones Laden
    const data = await new Promise(r => setTimeout(() => r({ ok: true }), 10));
    console.log('Geladen:', data);
})();

console.log('Sync-Code läuft weiter');
Output
Start
Sync-Code läuft weiter
Geladen: { ok: true }

Das ist eine der wenigen Situationen, in denen IIFE auch in modernem Code idiomatisch ist. In ESM mit Top-Level-Await ist auch das überflüssig — await foo() darf direkt im Modul-Top-Level stehen.

Block-Scope statt IIFE

Für reine Variable-Isolation reicht heute ein einfacher Block mit let/const:

JavaScript block-vs-iife.js
// Alt — IIFE für Scope-Isolation
(function() {
    var temp = 'altmodisch';
    console.log(temp);
})();

// Neu — einfacher Block mit let/const
{
    const temp = 'modern';
    console.log(temp);
}

// temp ist außerhalb beider Formen unsichtbar
Output
altmodisch
modern

Der Block ist kürzer, einfacher und genauso effektiv für Scope-Isolation. Eine IIFE für reine Scope-Isolation ist in ES2015+ obsolet.

UMD — Universal Module Definition

Eine erweiterte IIFE-Form, die in alten Library-Bundles üblich ist: das UMD-Pattern prüft die Umgebung (AMD, CommonJS, Browser) und exportiert entsprechend.

JavaScript umd-pattern.js
(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD (RequireJS)
        define([], factory);
    } else if (typeof module === 'object' && module.exports) {
        // CommonJS / Node
        module.exports = factory();
    } else {
        // Browser-Global
        root.MyLib = factory();
    }
})(typeof self !== 'undefined' ? self : this, function() {
    return {
        version: '1.0',
        gruss() { return 'Hallo'; },
    };
});

UMD ist heute nur noch in Legacy-Library-Bundles relevant. Moderne Libraries publizieren ESM + CJS-Builds parallel, ohne den UMD-Detector. Aber: wer mit alten CDN-Skripten arbeitet, sieht das Muster ständig.

Wann heute noch eine IIFE?

In modernem Code reduziert sich der Einsatz auf wenige Fälle:

  • Top-Level await in Nicht-ESM-Umgebung (Browser-Skript ohne type="module", ältere Node-Versionen).
  • Sofort-Ausführung mit Argumenten, etwa zur One-Liner-Konfiguration.
  • Generierter Code in Bundlern oder Source-Maps, der mehrere Module in eine Datei wickelt.
  • CDN-Library-Bundles, die ohne Module-Loader auskommen müssen.
JavaScript moderner-einsatz.js
// Sofort-Auswertung mit Argument
const config = (function(env) {
    return {
        api: env === 'prod' ? 'https://api.app' : 'http://localhost:3000',
        debug: env !== 'prod',
    };
})('dev');

console.log(config);
Output
{ api: 'http://localhost:3000', debug: true }

In modernem Code wäre dasselbe meist ohne IIFE lesbar als Funktion mit Parameter und direktem Aufruf — die IIFE ist nur dann motivierter, wenn der Code-Block einmalig und nicht wiederverwendbar ist.

Performance — kein Argument

Moderne Engines inline-optimieren IIFEs aggressiv. Es gibt keinen messbaren Performance-Vorteil oder -Nachteil gegenüber Block-Scope oder normalen Funktionen.

Frühere Annahmen, dass lokale Variablen-Lookups in einer IIFE schneller wären, gelten heute nicht mehr — V8, SpiderMonkey und JSC erkennen die Pattern und optimieren entsprechend.

Die Wahl IIFE vs. Alternative ist eine Lesbarkeits- und Style-Entscheidung, keine Performance-Frage.

Interessantes

Klammer-Trigger nötig: function() direkt am Statement-Start ist SyntaxError

function() { return 1; }(); als Top-Level wirft SyntaxError: Function statements require a function name. Die Engine parst function als Statement-Anfang. Lösung: in Klammern wickeln, Unary-Operator davorstellen, oder das Resultat einer Zuweisung sein lassen — alles macht den Kontext zu einer Expression.

Crockford-Stil (function () {}()) vs. (function () {})()

Douglas Crockford hat in seinem JavaScript-Stil-Guide die Form (function() { ... }()) bevorzugt — Aufruf innerhalb der Klammer. Die verbreitetere Form ist (function() { ... })() — Aufruf außerhalb. Beide sind funktional identisch. Linter wie ESLint haben dazu die Regel wrap-iife für konsistente Wahl.

Block-Scope mit let/const macht IIFEs überflüssig für Scope-Isolation

Pre-ES2015 brauchten Entwickler IIFEs, weil var function-scoped ist. Mit let/const reicht ein nackter { ... }-Block für dieselbe Isolation. Eine IIFE für reine Variable-Trennung ist heute reine Style-Tradition, kein technischer Bedarf.

ESM ersetzt das IIFE-Modul-Pattern komplett

Jedes ESM-Modul ist intrinsisch eine Closure mit privatem State. const private = ...; export const public = ... macht dasselbe wie das IIFE-Modul-Pattern, aber deklarativer. Bundler (Webpack, Rollup, esbuild) generieren beim CommonJS-Output oft noch IIFE-Wrappers — als Implementation-Detail, nicht als Konvention für Quellcode.

Named IIFE für Stack-Traces — auch heute noch nützlich

(function init() { ... })(); taucht in Stack-Traces als init auf statt als anonymous. Bei Debugging hilft das. Wer eine längere IIFE schreibt, lohnt sich der Funktions-Name allein dafür.

Async-IIFE: häufigster moderner Use-Case

(async () => { await ... })(); ist die Standard-Form, um await in einem Top-Level-Kontext zu nutzen, der Top-Level-Await nicht unterstützt — etwa in CommonJS-Modulen oder klassischen Browser-Skripten. In ESM mit aktiviertem TLA ist auch das überflüssig.

Unary-Operator als IIFE-Trigger — Mini-Optimierung der Minifier

!function() {}() spart ein Klammerpaar gegenüber (function() {})(). Minifier wie UglifyJS und Terser haben das jahrelang genutzt, um Output zu verkürzen. In handgeschriebenem Code unüblich, weil weniger lesbar.

IIFE für Schleifen-Closures vor let — Legacy-Pattern

Vor ES2015 wurde eine IIFE pro Schleifen-Durchlauf eingesetzt, um die var i-Closure-Falle zu umgehen. Heute löst let i dasselbe Problem ohne Boilerplate. Wer altes Code-Material liest, begegnet diesem Pattern täglich — gut zu erkennen, weil es heute nichts Neues mehr lehrt.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Funktionen

Zur Übersicht