JavaScript trennt streng zwischen Expressions (alles, was zu einem Wert auswertet) und Statements (Anweisungen, die etwas tun). Diese Unterscheidung wirkt akademisch, ist aber praktisch überall sichtbar: Sie erklärt, warum eine IIFE in Klammern stehen muss, warum { a: 1 } am Zeilenanfang ein Block und kein Objekt ist, warum return\n5 undefined zurückgibt — und warum Ternary, Comma-Operator und Tagged-Templates Stilmittel sind, die nur in Expression-Position überhaupt funktionieren. Dieser Artikel zeigt die Grammatik dahinter und die Patterns, die sich aus ihr ableiten lassen.
Expression oder Statement?
Eine Expression ist ein Stück Code, das zu einem Wert auswertet — 5, a + b, f(), obj.prop, arr[0], x ? y : z. Ein Statement ist eine Anweisung, die eine Aktion ausführt — if (...), for (...), return, throw, while, ein ;, eine Deklaration. Die Grammatik der Sprache definiert für jede Position genau, was dort erlaubt ist: Manche Positionen verlangen eine Expression (z. B. nach return), manche ein Statement (z. B. der Rumpf einer for-Schleife).
// Expressions: ergeben einen Wert
5
'text'
a + b
Math.max(1, 2)
x === 0 ? 'null' : 'nicht null'
[1, 2, 3]
// Statements: führen Aktionen aus
if (x > 0) { /* ... */ }
for (let i = 0; i < 10; i++) { /* ... */ }
return 42;
throw new Error('boom');
let y = 1;Expressions produzieren Werte
Jede Expression liefert beim Ausführen einen Wert — auch wenn dieser Wert undefined ist. Die ECMAScript-Spezifikation listet ein Dutzend Expression-Kategorien: Literale (42, 'abc', true, null), Identifier-References (x, Math), Property-Access (obj.x, obj['x']), Function-Calls (f()), Operatoren (a + b, !x, typeof y), Conditional-Expressions (Ternary), Assignment-Expressions (x = 5 ist selbst eine Expression mit dem zugewiesenen Wert), Function-Expressions und Arrow-Functions.
// Assignment ist Expression — liefert den zugewiesenen Wert
let a;
const result = (a = 5);
console.log(result); // 5
// Function-Call ist Expression
const double = (n) => n * 2;
console.log(double(7)); // 14
// Function-Expression ist Expression
const fn = function () { return 1; };
console.log(fn()); // 1
// typeof ist Expression
console.log(typeof 'text'); // 'string'5
14
1
stringDiese Eigenschaft ist der Grund, warum sich Expressions verschachteln lassen: jede Expression-Position akzeptiert wieder eine Expression. Der Code f(g(h(x))) ist drei verschachtelte Function-Call-Expressions.
Statements führen Aktionen aus
Statements sind die strukturellen Bausteine eines Programms. Sie haben keinen Wert und können nicht in eine Expression-Position eingesetzt werden. Wer schon einmal versucht hat, const x = if (cond) 1 else 2; zu schreiben, kennt das Problem: if ist ein Statement, kein Ausdruck — die Sprache verlangt an dieser Stelle einen Ternary.
// FALSCH — if ist Statement, kein Wert
// const x = if (cond) 1 else 2;
// RICHTIG — Ternary ist Expression
const cond = true;
const x = cond ? 1 : 2;
// FALSCH — for ist Statement
// const arr = for (let i = 0; i < 3; i++) i;
// RICHTIG — Array.from + map ist Expression-basiert
const arr = Array.from({ length: 3 }, (_, i) => i);Eine wichtige Sonderform sind Declarations (let, const, function, class): Sie sind formal keine Statements, dürfen aber in den meisten Statement-Positionen stehen. Allerdings nicht überall — der Rumpf eines if ohne Block-Klammern darf z. B. kein let enthalten:
// SyntaxError: Lexical declaration cannot appear in single-statement context
// if (cond) let x = 1;
// OK: var ist Statement, kein lexical
if (cond) var x = 1;
// OK: in Block gewickelt
if (cond) { let x = 1; }Hybride: Expression-Statements
Eine Expression-Statement ist ein Statement, dessen Inhalt eine Expression ist. Genau hier verschmelzen die beiden Welten: Wer in einer Datei f(); schreibt, schreibt streng genommen ein Expression-Statement — der Funktionsaufruf ist eine Expression, das Semikolon (oder ASI) macht ein Statement daraus.
// Alles Expression-Statements:
f(); // Function-Call als Statement
x = 5; // Assignment als Statement
x++; // Update-Expression als Statement
a + b; // sinnlos, aber syntaktisch erlaubt
'hi'; // No-Op, außer am Datei-Anfang als Direktive
void 0; // expliziter undefined-Wert, Resultat verworfenAus historischen Gründen gibt es zwei verbotene Kombinationen, die die Mehrdeutigkeit auflösen sollen: Ein Expression-Statement darf nicht mit function beginnen (sonst wäre es eine Function-Declaration) und nicht mit { (sonst wäre es ein Block-Statement). Genau aus dieser Regel folgt das IIFE-Pattern und die Block-vs-Object-Disambiguation.
Block-Statement vs. Object-Literal-Disambiguation
{ a: 1 } kann zwei Dinge bedeuten: Ein Block-Statement mit einem Label a: und der Expression 1 darin — oder ein Object-Literal mit Property a und Wert 1. Die Sprache löst die Mehrdeutigkeit über die Position auf: Am Anfang eines Statement-Kontexts ist { immer ein Block, in Expression-Position immer ein Objekt.
// Block-Statement mit Label — KEIN Object-Literal
{ a: 1 }
// Arrow-Function: Body ist Statement-Kontext, also Block
const fn1 = () => { a: 1 }; // returns undefined!
console.log(fn1()); // undefined
// Klammern erzwingen Expression-Kontext
const fn2 = () => ({ a: 1 });
console.log(fn2()); // { a: 1 }
// eval mit Klammern: Object-Literal
console.log(eval('({ a: 1 })')); // { a: 1 }
// eval ohne Klammern: Block + Label-Statement
console.log(eval('{ a: 1 }')); // 1 — Wert des letzten Statementsundefined
{ a: 1 }
1Die häufigste Falle: Eine Arrow-Function, die ein Objekt zurückgeben soll, ohne Klammern zu nutzen. () => { a: 1 } liefert undefined, weil der Körper ein Block ist und das Label a: mit Expression 1 als Statement gewertet wird.
IIFE: Function-Expression sofort aufrufen
Eine IIFE (Immediately Invoked Function Expression) ist eine Funktion, die direkt nach ihrer Definition aufgerufen wird. Das klassische Pattern stammt aus der Pre-ES6-Zeit, in der man so einen privaten Scope erzeugen konnte, bevor let/const/Module verfügbar waren. Auch heute taucht es noch auf — z. B. in Bundles, die top-level await simulieren, oder in IIFE-Modulen für ältere Browser.
// FALSCH: function am Statement-Anfang ist Function-Declaration
// function() { return 1; }(); // SyntaxError
// RICHTIG: Klammern erzwingen Expression-Kontext
const a = (function () { return 1; })();
console.log(a); // 1
// Alternative: Klammern aussen
const b = (function () { return 2; }());
console.log(b); // 2
// Arrow-Function ist immer Expression — keine Klammer noetig
const c = (() => 3)();
console.log(c); // 3
// async-IIFE: top-level await für Nicht-Module
// (async () => { const data = await fetch('/api'); console.log(data); })();1
2
3Mit ESM ist der primäre Anwendungsfall — Scope-Isolation — entfallen. Module haben ihren eigenen Scope. IIFE bleibt nützlich für lokale Helper-Berechnungen mit mehreren Statements, die in einer Expression-Position stehen müssen.
Comma-Operator: Expression mit Side-Effect
Der Comma-Operator verbindet mehrere Expressions in einer einzigen Expression. Alle werden von links nach rechts ausgewertet, der Wert ist der der letzten. Praktisch nur dort sinnvoll, wo Statement-Position nicht möglich ist — z. B. im Kopf einer for-Schleife.
// for-Kopf: jede Klammer-Sektion ist Expression-Position
for (let i = 0, j = 10; i < j; i++, j--) {
// i und j synchron veraendern
if (i === 5) { console.log(i, j); break; }
}
// Side-Effect plus Wert in einer Expression
let count = 0;
const next = () => (count++, count);
console.log(next()); // 1
console.log(next()); // 2
// Vorsicht: Comma hat NIEDRIGSTE Precedence
// const x = 1, 2; // SyntaxError — Comma der const-Liste, nicht Operator
const x = (1, 2); // x === 2
console.log(x);5 5
1
2
2Der Operator ist verwirrend, weil das gleiche Komma-Zeichen in let a, b, c; oder f(1, 2, 3) keine Operator-Bedeutung hat — dort ist es Listentrenner. Erst in echter Expression-Position wird es zum Operator.
Ternary: die Expression-Alternative zu if
Weil if ein Statement ist und keinen Wert liefert, gibt es den Ternary-Operator cond ? a : b als Expression-Pendant. Er ist nicht nur kürzer — er ist die einzige Möglichkeit, eine Verzweigung in einer Expression-Position zu nutzen, etwa innerhalb eines Template-Literals, in einer Property-Definition oder als Argument.
const score = 72;
// if-Statement: kein Wert
let grade;
if (score >= 90) grade = 'A';
else if (score >= 70) grade = 'B';
else grade = 'C';
// Ternary-Expression: hat einen Wert, direkt zuweisbar
const grade2 = score >= 90 ? 'A' : score >= 70 ? 'B' : 'C';
console.log(grade2); // 'B'
// Innerhalb eines Template-Literals: nur Expressions erlaubt
const note = `Note: ${score >= 60 ? 'bestanden' : 'nicht bestanden'}`;
console.log(note);
// In Property-Definition
const obj = { active: score > 0 ? true : false };
console.log(obj);B
Note: bestanden
{ active: true }Verschachtelte Ternaries sind ein Stilthema — viele Code-Style-Guides verbieten sie. Lesbar bleiben sie, wenn jede Verzweigung auf einer eigenen Zeile mit eingerücktem ?/: steht.
Übersicht: Statements vs. Expressions
| Kategorie | Statements | Expressions |
|---|---|---|
| Werte | nein | ja |
| Beispiele | if, for, while, return, throw | 5, a + b, f(), obj.x, cond ? a : b |
In const x = | nein | ja |
In Template-${} | nein | ja |
| Funktions-Form | Function-Declaration function f() {} | Function-Expression function () {} |
| Verzweigung | if/else, switch | Ternary ?:, &&, || |
| Sequenz | ;-getrennte Statements im Block | Comma-Operator , |
| Wert verwerfen | naturgemäss kein Wert | Expression-Statement (f();) |
Spezialfälle
IIFE braucht Klammern, weil function als Statement geparst wird
Steht function am Anfang eines Statements, ist es eine Function-Declaration — kein Wert. function() { }() bricht beim Parsen, weil eine Declaration einen Namen verlangt. Die führenden Klammern erzwingen Expression-Kontext: (function() { })() oder (function() { }()). Beide Varianten sind verbreitet, der erste Stil ist häufiger.
{ a: 1 } ist im Statement-Kontext ein Block, kein Objekt
Am Statement-Anfang interpretiert der Parser { als Block-Beginn. a: wird Label, 1 ist Expression-Statement. Klassische Falle bei Arrow-Function-Bodies: () => { a: 1 } liefert undefined. Klammern um den Body — () => ({ a: 1 }) — erzwingen Expression-Kontext und damit Object-Literal.
Arrow-Functions sind IMMER Expressions
Die Pfeil-Syntax existiert nur als Expression. Es gibt keine Arrow-Declaration, keine Hoisting. Praktischer Effekt: (() => 5)() ruft direkt auf — der Klammer-Trick aus dem klassischen IIFE ist hier nicht zwingend, weil => nicht mit function verwechselt werden kann. Aber stilistisch werden die Klammern oft trotzdem gesetzt, um den Aufruf-Charakter zu signalisieren.
Comma-Operator hat die NIEDRIGSTE Precedence
Niedriger als Assignment. Deshalb scheitert const x = 1, 2; — das Komma wird als Listentrenner der const-Deklaration interpretiert, nicht als Operator. Mit Klammern funktioniert es: const x = (1, 2); ergibt x === 2. In for-Köpfen ist es der einzige sinnvolle Anwendungsort, weil dort ohnehin Expression-Position ist.
return ist ein Statement — kein Zeilenumbruch nach return
ASI fügt nach return ein Semikolon ein, wenn ein Line-Terminator folgt. return\n5; wird zu return; 5; — die Funktion gibt undefined zurück, der zweite Teil ist toter Code. Wer Werte über mehrere Zeilen returnen will, nutzt Klammern: return (\n { a: 1 }\n);.
async/await: Body ist Statement-Liste, await selbst ist Expression
Eine async-Funktion enthält wie jede Funktion einen Block aus Statements. await ist innerhalb davon eine Expression, nutzbar überall, wo Expressions stehen dürfen — als Argument, in Ternarys, in Template-Literals. await await await x ist legal und kettet drei Awaits. await auf Top-Level ist nur in ESM-Modulen erlaubt (Top-Level Await, ES2022).
do-Expression-Proposal: Block als Expression (Stage 1)
Das TC39-Proposal do { ... } würde einen Block als Expression-Wert nutzbar machen, mit dem Wert des letzten Expression-Statements. const x = do { let a = 1; a + 1; }; wäre 2. Es steht seit Jahren auf Stage 1 und ist nicht in der Sprache. Bis dahin ist eine IIFE der pragmatische Ersatz: const x = (() => { let a = 1; return a + 1; })();.
Weiterführende Ressourcen
Externe Quellen
- Statements and declarations – MDN
- Expressions and operators – MDN
- ECMAScript Specification – Expression Statement
- IIFE – MDN Glossary