Die meisten Programmiersprachen kennen genau einen Wert für „nichts": null in Java, nil in Ruby, None in Python. JavaScript hat zwei: null und undefined. Die Trennung wirkt zunächst wie ein Designfehler, hat aber eine klare Logik — undefined bedeutet „noch nicht gesetzt" (eine Sache, die die Engine selbst sagt), null bedeutet „explizit ohne Wert" (eine Sache, die der Programmierer sagt). Dieser Artikel zeigt im Detail, wann welcher Wert auftritt, wie sie sich in ==, ===, ??, JSON, Default-Parametern und Optional Chaining unterscheiden — und welches Idiom (== null) beide gleichzeitig abfängt.

Zwei „leere" Werte

JavaScript trennt zwei Konzepte sauber: die Abwesenheit eines Wertes (Sprache hat noch nichts gesetzt) und die explizite Leere (Programmierer hat bewusst „nichts" gewählt). Die ECMAScript-Spec dokumentiert beides als eigene Primitive-Typen mit jeweils einem einzigen Wert.

JavaScript zwei-leere-werte.js
let unset;            // automatisch undefined — Sprache sagt: noch nicht gesetzt
let leer = null;      // explizit null — Programmierer sagt: kein Wert

console.log(unset);   // undefined
console.log(leer);    // null

console.log(typeof unset);  // 'undefined'
console.log(typeof leer);   // 'object'  (historischer Bug)
Output
undefined
null
undefined
object

undefined: „nicht gesetzt"

undefined ist der Wert, den JavaScript an mehreren Stellen automatisch einsetzt — überall dort, wo etwas vom Programmierer hätte gesetzt werden können, aber nicht wurde. Vier kanonische Quellen.

JavaScript undefined-quellen.js
// 1) Nicht-initialisierte Variable
let x;
console.log(x);                 // undefined

// 2) Fehlender Function-Parameter
function gruss(name) {
    return `Hallo ${name}`;
}
console.log(gruss());           // 'Hallo undefined'

// 3) Fehlender Property-Zugriff
const obj = { a: 1 };
console.log(obj.b);             // undefined

// 4) Function ohne return
function nichts() { /* still */ }
console.log(nichts());          // undefined

// Bonus: Array-Loch
const arr = [1, , 3];           // sparse array
console.log(arr[1]);            // undefined
Output
undefined
Hallo undefined
undefined
undefined
undefined

In all diesen Fällen hat niemand explizit undefined gesetzt — die Sprache hat es als Default eingefügt, weil ein Wert fehlte.

null: „explizit leer"

null taucht nie automatisch auf. Wer null sieht, weiß: hier hat jemand bewusst entschieden, dass dieser Slot leer sein soll. Das macht null zum natürlichen Sentinel-Wert für API-Antworten („User nicht gefunden"), für aufgegebene Reference-Variablen (Memory-Hint an den GC) oder für das Ende einer Prototype-Chain.

JavaScript null-als-explizit.js
// API-Antwort signalisiert "nicht gefunden" via null
function findeUser(id) {
    const user = datenbank.get(id);
    return user ?? null;        // explizit: kein Treffer
}

// DOM: querySelector liefert null bei keinem Match
const el = document.querySelector('.gibts-nicht');
if (el === null) {
    console.log('Kein Element gefunden');
}

// Prototype-Chain endet bei null
const obj = {};
console.log(Object.getPrototypeOf(Object.prototype)); // null
Output
Kein Element gefunden
null

Diese Konvention ist in der DOM-Welt fest verankert: querySelector, getElementById, parentNode und nextElementSibling geben alle null zurück, wenn nichts da ist — niemals undefined.

Konvention: wann was?

Die übliche Daumenregel in modernen Codebasen:

  • undefined = „nicht gesetzt", „nicht relevant", „nicht angegeben". Tritt bei optionalen Parametern, fehlenden Properties, frisch deklarierten Variablen auf.
  • null = „bewusst ohne Wert", „expliziter Reset", „leerer Sentinel". Tritt in API-Verträgen, Datenmodellen, expliziten Lösch-Operationen auf.

Manche Codebasen normalisieren auf nur eine der beiden — typischerweise null für Datenbank-/JSON-Lasten und undefined für interne Sprach-Slots. Beide Strategien sind valide; wichtig ist, dass das Team sich auf eine einigt.

JavaScript konvention-praxis.js
// Konvention: API-Datenmodell nutzt null für "leer"
type User = {
    name: string;
    telefon: string | null;     // explizit null wenn nicht hinterlegt
    partner: User | null;
};

// Konvention: Funktions-Optionen nutzen undefined
function rendere(opt) {
    const farbe = opt?.farbe ?? '#000';     // undefined → Default
    const groesse = opt?.groesse ?? 16;
    // ...
}

typeof-Verhalten und der null-Bug

typeof undefined liefert sauber 'undefined'. typeof null liefert dagegen 'object' — der berühmte historische Bug aus der ersten JS-Implementation, der aus Backwards-Compat-Gründen nie repariert wurde. Daraus folgt: für robuste Tests auf null oder undefined nutzt man immer den direkten Vergleich, nicht typeof.

JavaScript typeof-verhalten.js
console.log(typeof undefined);  // 'undefined'
console.log(typeof null);       // 'object'  (Bug)

// Robuste Tests:
const wert = null;

if (wert === undefined) { /* nur undefined */ }
if (wert === null)      { /* nur null */ }
if (wert == null)       { /* beide — siehe nächste Section */ }

// Nicht-robust (wegen typeof null === 'object'):
if (typeof wert === 'object') {
    // greift auch für null! Falsch wenn man Plain-Object erwartet.
}

Eine klassische Falle: if (typeof wert === 'object') { wert.eigenschaft } — bei wert === null wirft das einen TypeError, weil null eben kein „echtes" Object ist, obwohl typeof es so behauptet. Korrekt wäre if (wert !== null && typeof wert === 'object').

Vergleichs-Verhalten — == vs. ===

Bei === (Strict Equality) sind null und undefined zwei verschiedene Werte: null === undefined ist false. Bei == (Loose Equality) macht JavaScript eine Sonder-Regel: null == undefined ist true. Diese Sonder-Regel ist die einzige Stelle, an der die meisten modernen Linter eine Ausnahme von „immer === benutzen" zulassen — denn das Idiom value == null ist ein extrem kompakter Test auf „beide leeren Werte gleichzeitig".

JavaScript value-equals-null.js
// Idiom: == null fängt beide ab
function isLeer(v) {
    return v == null;       // true für null UND undefined
}

console.log(isLeer(null));        // true
console.log(isLeer(undefined));   // true
console.log(isLeer(0));           // false
console.log(isLeer(''));          // false
console.log(isLeer(false));       // false
console.log(isLeer(NaN));         // false

// Strikt-Variante (expliziter, doppelt so lang)
function isLeerStrict(v) {
    return v === null || v === undefined;
}
Output
true
true
false
false
false
false

void 0 — die historische Konstante

void ist ein unärer Operator, der seinen Operanden auswertet und immer undefined zurückgibt. void 0 ist die kürzeste Variante und tauchte historisch in zwei Kontexten auf: erstens als sichere Konstante, weil in alten JavaScript-Engines undefined selbst als Identifier überschreibbar war (undefined = 'oops' war legal), zweitens in Minified-Code, wo void 0 drei Zeichen kürzer ist als undefined.

JavaScript void-zero.js
console.log(void 0);              // undefined
console.log(void 'irgendwas');    // undefined
console.log(void (1 + 2));        // undefined

// Historisch: sichere Variante
if (x === void 0) {
    // gleiche Bedeutung wie x === undefined
}
Output
undefined
undefined
undefined

In modernem Code (ES5+) ist undefined ein non-writable, non-configurable Property auf globalThis — überschreiben geht nicht mehr. Damit ist void 0 heute überflüssig im Source-Code; in Bundler-Output kommt es weiter vor.

JSON: null ja, undefined nein

JSON kennt nur null, kein undefined — das ist eine harte Spec-Regel. JSON.stringify reagiert entsprechend unterschiedlich: Properties mit undefined-Wert werden komplett weggelassen, Properties mit null-Wert werden als "null" serialisiert. In Arrays werden undefined-Slots zu null umgesetzt, weil ein Array keine Lücken haben darf.

JavaScript json-verhalten.js
const obj = {
    a: 1,
    b: undefined,    // verschwindet
    c: null,         // bleibt als "null"
    d: 'text'
};
console.log(JSON.stringify(obj));
// '{"a":1,"c":null,"d":"text"}'

// Arrays: undefined wird zu null
const arr = [1, undefined, null, 4];
console.log(JSON.stringify(arr));
// '[1,null,null,4]'

// Round-Trip: undefined kann nicht wiederhergestellt werden
const klon = JSON.parse(JSON.stringify(obj));
console.log(klon);
// { a: 1, c: null, d: 'text' }   — b ist weg
Output
{"a":1,"c":null,"d":"text"}
[1,null,null,4]
{ a: 1, c: null, d: 'text' }

Das ist auch der Grund, warum API-Schemas (REST, GraphQL) in der Regel null als „explizit leer" definieren: nur null überlebt die Serialisierung. Wer undefined über die Wire schicken will, muss es vorher in null umwandeln.

?? — Nullish Coalescing

Der ??-Operator (ES2020) ist explizit auf null und undefined zugeschnitten: er liefert den linken Wert, außer wenn dieser nullish ist — dann den rechten. Das unterscheidet ihn vom älteren ||, das auf jeden Falsy-Wert reagiert (also auch auf 0, '', false, NaN).

JavaScript nullish-coalescing.js
console.log(null      ?? 'default');   // 'default'
console.log(undefined ?? 'default');   // 'default'
console.log(0         ?? 'default');   // 0       — nicht überschrieben!
console.log(''        ?? 'default');   // ''
console.log(false     ?? 'default');   // false
console.log(NaN       ?? 'default');   // NaN

// Vergleich mit || (die alte Falle)
const lautstaerke = 0;
console.log(lautstaerke || 50);   // 50  — Bug! 0 wird durch Default ersetzt
console.log(lautstaerke ?? 50);   // 0   — korrekt: 0 ist ein gültiger Wert
Output
default
default
0

false
NaN
50
0

?? ist die richtige Wahl für numerische Defaults und für boolean Defaults, wo 0 und false gültige Werte sein dürfen. || bleibt brauchbar für reine String-Defaults, wo der leere String und null/undefined denselben Effekt haben sollen.

Praxis: Defaults und Optional Chaining

Drei moderne Sprach-Features arbeiten zusammen, um nullish-Werte sauber zu handhaben: Default-Parameter in Funktions-Signaturen, Optional Chaining (?.) für sichere Property-Zugriffe und Nullish Coalescing (??) für Fallbacks. Wichtig zu wissen: Default-Parameter greifen nur bei undefined, nicht bei null. null wird als „expliziter Wert" durchgelassen.

JavaScript defaults-und-chaining.js
function konfiguriere(opt = {}) {
    // 1) Default-Parameter: greift nur bei undefined
    const farbe   = opt.farbe   ?? '#000';
    const groesse = opt.groesse ?? 16;

    // 2) Optional Chaining für verschachtelten Zugriff
    const tema = opt?.theme?.name ?? 'default';

    // 3) Optional Chaining mit Methodenaufruf
    opt?.onInit?.();

    return { farbe, groesse, tema };
}

console.log(konfiguriere());
// { farbe: '#000', groesse: 16, tema: 'default' }

console.log(konfiguriere({ farbe: '#f00', theme: { name: 'dark' } }));
// { farbe: '#f00', groesse: 16, tema: 'dark' }

// Default-Parameter ignoriert null nicht:
function f(x = 'default') { return x; }
console.log(f());           // 'default'
console.log(f(undefined));  // 'default'  — Default greift
console.log(f(null));       // null       — Default greift NICHT
Output
{ farbe: '#000', groesse: 16, tema: 'default' }
{ farbe: '#f00', groesse: 16, tema: 'dark' }
default
default
null

null vs. undefined — die Übersicht

Aspektundefinednull
QuelleEngine setzt automatischProgrammierer setzt explizit
Bedeutung„noch nicht gesetzt"„explizit ohne Wert"
typeof'undefined''object' (historischer Bug)
== Vergleichnull == undefined ist truenull == undefined ist true
=== Vergleichnur gegen undefined truenur gegen null true
JSON-SerialisierungProperty entfällt; Array → nullbleibt als "null"
?? OperatorDefault greiftDefault greift
|| OperatorDefault greift (mit Falsy-Falle)Default greift (mit Falsy-Falle)
Default-Parametergreiftgreift NICHT
Optional Chaining (?.)bricht ab, gibt undefined zurückbricht ab, gibt undefined zurück
Numerische Coercion+undefinedNaN+null0

Eine Detail-Falle: null + 1 ist 1 (weil null zu 0 gecoerced wird), aber undefined + 1 ist NaN. Wer numerisch rechnet, sollte sich auf keine der beiden Coercions verlassen — explizit konvertieren oder ?? für Defaults nutzen.

Häufig gestellte Fragen

Wann nutzt man null statt undefined?

null dort, wo man EXPLIZIT „kein Wert" signalisieren will — typischerweise als API-Antwort („User nicht gefunden"), als Datenbank-NULL-Mapping, als bewussten Reset einer Reference-Variable. undefined dort, wo „noch nicht gesetzt" gemeint ist — fehlende optionale Parameter, nicht-initialisierte Variablen, fehlende Object-Properties. Manche Codebasen normalisieren bewusst auf nur einen der beiden Werte; das ist legitim, solange das Team sich an die Konvention hält.

Warum ist typeof null gleich 'object'?

Frühe JavaScript-Implementierungen (1995) kodierten Werte als Tagged-Pointer: drei Bits Type-Tag, der Rest war der Wert. Object hatte den Type-Tag 0, und der NULL-Pointer (0x00) hatte ebenfalls alle Bits auf null — also Tag 0, also Object. Ein Vorschlag, das in ES5.1 zu reparieren (typeof null === 'null'), wurde nach Diskussion verworfen, weil zu viele Codebasen implizit auf das alte Verhalten bauen.

Soll ich `== null` oder `=== null || === undefined` schreiben?

Beides ist akzeptabel. value == null ist idiomatisch, kürzer und in der ESLint-Regel eqeqeq mit Option "allow-null" ausdrücklich erlaubt. value === null || value === undefined ist expliziter und passt zu Code-Standards, die ALLE ==-Verwendungen verbieten. Wichtig: konsistent bleiben innerhalb eines Projekts — beide Varianten ständig zu mischen verwirrt mehr, als die Wahl entscheidet.

Wird undefined in JSON serialisiert?

Nein. JSON.stringify({ a: undefined }) ergibt '{}' — die Property wird komplett weggelassen. In Arrays werden undefined-Slots zu null: JSON.stringify([1, undefined, 3]) ergibt '[1,null,3]'. Round-Trip JSON.parse(JSON.stringify(obj)) verliert also alle undefined-Properties — wer das nicht möchte, muss vor dem Stringify auf null normalisieren.

Was passiert bei `null + 1` und `undefined + 1`?

null wird in numerischem Kontext zu 0 gecoerced, also null + 1 === 1. undefined wird zu NaN gecoerced, also undefined + 1 ergibt NaN. Das ist eine selten genutzte Asymmetrie, die regelmässig zu subtilen Bugs führt — etwa wenn ein optionaler Parameter weggelassen wird (NaN-Folge) oder explizit auf null gesetzt wird (0-Folge). Beste Praxis: vor numerischen Operationen explizit defaulten, z. B. (x ?? 0) + 1.

Default-Parameter mit null oder undefined?

Default-Parameter greifen NUR bei undefined, nicht bei null. function f(x = 'd') { return x }: f() liefert 'd', f(undefined) liefert 'd', f(null) liefert null. Das ist gewollt — null ist eine bewusste Wahl, die nicht überschrieben werden soll. Wer auch bei null defaulten will, nutzt ?? innerhalb der Funktion: const v = x ?? 'd';.

?? oder || — wann was?

?? reagiert nur auf null und undefined; || reagiert auf alle Falsy-Werte (0, '', false, NaN, null, undefined). Bei numerischen oder boolean Defaults ist ?? fast immer richtig: lautstaerke ?? 50 respektiert 0 als gültigen Wert, lautstaerke || 50 nicht. || bleibt sinnvoll für rein optionale Strings, wo der leere String wirklich „kein Wert" bedeutet.

Optional Chaining mit Methoden — wie geht das?

obj.method?.() ruft die Methode nur auf, wenn obj.method nicht null oder undefined ist; sonst ist der Gesamtausdruck undefined. Auch verschachtelt: obj?.sub?.method?.(arg) bricht beim ersten nullish-Wert ab. Wichtig: das ?. kommt VOR der aufrufenden Klammer (method?.(), nicht method.()?). Der Operator gilt ausschliesslich für nullish-Werte — wenn method z. B. 0 ist, wirft der Aufruf wie üblich einen TypeError.

Weiterführende Ressourcen

Externe Quellen

/ Weiter

Zurück zu Datentypen

Zur Übersicht