JavaScript für ungeduldige Programmierer (ES2022-Ausgabe)
Bitte unterstützen Sie dieses Buch: kaufen Sie es oder spenden Sie
(Werbung, bitte nicht blockieren.)

14 Die Nicht-Werte undefined und null



Viele Programmiersprachen haben einen "Nicht-Wert" namens null. Er zeigt an, dass eine Variable derzeit nicht auf ein Objekt verweist – zum Beispiel, wenn sie noch nicht initialisiert wurde.

Im Gegensatz dazu hat JavaScript zwei davon: undefined und null.

14.1 undefined vs. null

Beide Werte sind sehr ähnlich und werden oft austauschbar verwendet. Wie sie sich unterscheiden, ist daher subtil. Die Sprache selbst macht die folgende Unterscheidung:

Programmierer können die folgende Unterscheidung treffen:

14.2 Vorkommen von undefined und null

Die folgenden Unterabschnitte beschreiben, wo undefined und null in der Sprache vorkommen. Wir werden mehreren Mechanismen begegnen, die später in diesem Buch ausführlicher erklärt werden.

14.2.1 Vorkommen von undefined

Uninitialisierte Variable myVar

let myVar;
assert.equal(myVar, undefined);

Parameter x wurde nicht übergeben

function func(x) {
  return x;
}
assert.equal(func(), undefined);

Eigenschaft .unknownProp fehlt

const obj = {};
assert.equal(obj.unknownProp, undefined);

Wenn wir das Ergebnis einer Funktion nicht explizit über eine return-Anweisung angeben, gibt JavaScript undefined für uns zurück.

function func() {}
assert.equal(func(), undefined);

14.2.2 Vorkommen von null

Der Prototyp eines Objekts ist entweder ein Objekt oder am Ende einer Prototypenkette steht null. Object.prototype hat keinen Prototyp.

> Object.getPrototypeOf(Object.prototype)
null

Wenn wir einen regulären Ausdruck (wie /a/) mit einem String (wie 'x') abgleichen, erhalten wir entweder ein Objekt mit übereinstimmenden Daten (wenn der Abgleich erfolgreich war) oder null (wenn der Abgleich fehlschlug).

> /a/.exec('x')
null

Das JSON-Datenformat unterstützt undefined nicht, nur null.

> JSON.stringify({a: undefined, b: null})
'{"b":null}'

14.3 Prüfung auf undefined oder null

Prüfung auf beides

if (x === null) ···
if (x === undefined) ···

Hat x einen Wert?

if (x !== undefined && x !== null) {
  // ···
}
if (x) { // truthy?
  // x is neither: undefined, null, false, 0, NaN, ''
}

Ist x entweder undefined oder null?

if (x === undefined || x === null) {
  // ···
}
if (!x) { // falsy?
  // x is: undefined, null, false, 0, NaN, ''
}

Truthy bedeutet "ist true, wenn zu einem booleschen Wert konvertiert". Falsy bedeutet "ist false, wenn zu einem booleschen Wert konvertiert". Beide Konzepte werden in §15.2 "Falsy und Truthy Werte" ausführlich erklärt.

14.4 Der Nullish Coalescing Operator (??) für Standardwerte [ES2020]

Manchmal erhalten wir einen Wert und möchten ihn nur verwenden, wenn er weder null noch undefined ist. Andernfalls möchten wir einen Standardwert als Fallback verwenden. Das können wir über den *Nullish Coalescing Operator* (??) tun.

const valueToUse = receivedValue ?? defaultValue;

Die folgenden beiden Ausdrücke sind äquivalent.

a ?? b
a !== undefined && a !== null ? a : b

14.4.1 Beispiel: Zählen von Übereinstimmungen

Der folgende Code zeigt ein reales Beispiel:

function countMatches(regex, str) {
  const matchResult = str.match(regex); // null or Array
  return (matchResult ?? []).length;
}

assert.equal(
  countMatches(/a/g, 'ababa'), 3);
assert.equal(
  countMatches(/b/g, 'ababa'), 2);
assert.equal(
  countMatches(/x/g, 'ababa'), 0);

Wenn es eine oder mehrere Übereinstimmungen für regex in str gibt, gibt .match() ein Array zurück. Wenn es keine Übereinstimmungen gibt, gibt es leider null zurück (und nicht das leere Array). Das beheben wir mit dem ??-Operator.

Wir hätten auch Optional Chaining verwenden können.

return matchResult?.length ?? 0;

14.4.2 Beispiel: Angabe eines Standardwerts für eine Eigenschaft

function getTitle(fileDesc) {
  return fileDesc.title ?? '(Untitled)';
}

const files = [
  {path: 'index.html', title: 'Home'},
  {path: 'tmp.html'},
];
assert.deepEqual(
  files.map(f => getTitle(f)),
  ['Home', '(Untitled)']);

14.4.3 Verwendung von Destrukturierung für Standardwerte

In einigen Fällen kann Destrukturierung auch für Standardwerte verwendet werden – zum Beispiel:

function getTitle(fileDesc) {
  const {title = '(Untitled)'} = fileDesc;
  return title;
}

14.4.4 Legacy-Ansatz: Verwendung des logischen ODER (||) für Standardwerte

Vor ECMAScript 2020 und dem Nullish Coalescing Operator wurde das logische ODER für Standardwerte verwendet. Das hat einen Nachteil.

|| funktioniert wie erwartet für undefined und null.

> undefined || 'default'
'default'
> null || 'default'
'default'

Aber es gibt auch für alle anderen falsy Werte den Standardwert zurück – zum Beispiel:

> false || 'default'
'default'
> 0 || 'default'
'default'
> 0n || 'default'
'default'
> '' || 'default'
'default'

Vergleichen Sie das damit, wie ?? funktioniert:

> undefined ?? 'default'
'default'
> null ?? 'default'
'default'

> false ?? 'default'
false
> 0 ?? 'default'
0
> 0n ?? 'default'
0n
> '' ?? 'default'
''

14.4.5 Der Nullish Coalescing Assignment Operator (??=) [ES2021]

??= ist ein logischer Zuweisungsoperator. Die folgenden beiden Ausdrücke sind grob äquivalent:

a ??= b
a ?? (a = b)

Das bedeutet, dass ??= short-circuiting ist: Die Zuweisung erfolgt nur, wenn a undefined oder null ist.

14.4.5.1 Beispiel: Verwenden von ??= zum Hinzufügen fehlender Eigenschaften
const books = [
  {
    isbn: '123',
  },
  {
    title: 'ECMAScript Language Specification',
    isbn: '456',
  },
];

// Add property .title where it’s missing
for (const book of books) {
  book.title ??= '(Untitled)';
}

assert.deepEqual(
  books,
  [
    {
      isbn: '123',
      title: '(Untitled)',
    },
    {
      title: 'ECMAScript Language Specification',
      isbn: '456',
    },
  ]);

14.5 undefined und null haben keine Eigenschaften

undefined und null sind die einzigen beiden JavaScript-Werte, bei denen wir eine Ausnahme erhalten, wenn wir versuchen, eine Eigenschaft zu lesen. Um dieses Phänomen zu untersuchen, verwenden wir die folgende Funktion, die die Eigenschaft .foo liest ("holt") und das Ergebnis zurückgibt.

function getFoo(x) {
  return x.foo;
}

Wenn wir getFoo() auf verschiedene Werte anwenden, können wir sehen, dass es nur für undefined und null fehlschlägt.

> getFoo(undefined)
TypeError: Cannot read properties of undefined (reading 'foo')
> getFoo(null)
TypeError: Cannot read properties of null (reading 'foo')

> getFoo(true)
undefined
> getFoo({})
undefined

14.6 Die Geschichte von undefined und null

In Java (das viele Aspekte von JavaScript inspiriert hat) hängen Initialisierungswerte vom statischen Typ einer Variablen ab:

In JavaScript kann jede Variable sowohl Objektwerte als auch primitive Werte speichern. Wenn null also "kein Objekt" bedeutet, benötigt JavaScript auch einen Initialisierungswert, der "weder Objekt noch primitiver Wert" bedeutet. Dieser Initialisierungswert ist undefined.

  Quiz

Siehe Quiz-App.