Nullish coalescing operator / ES2020-Feature
basics javascript react | Thomas Scharke • | 13 Minuten
Der Nullish coalescing operator ist ein neuer und zusätzlicher JavaScript-Operator, der seit Juni 2020 mit ECMAScript 2020 (ES2020) der Programmiersprache zur Verfügung steht.
Er ist neben den (vielleicht) bekannten binären logische Operatoren (Binary Logical Operators) &&
(AND) und ||
(ODER) der dritte Operator nicht binäre und hat die Schreibweise ??
.
Zum Einsatz kommt er immer dann, wenn ich explizit prüfen möchte ob der Wert einer Variable vorliegt um diesen zu nutzen oder, wenn der Wert nicht vorliegt, mit einem anderen Wert weiter zu arbeiten.
Hier für mich der “Klassiker”: Einmal mit einem if
-Block, dann in einer “vereinfachten” Schreibweise mit dem ODER-Operator und zu guter letzt in der Schreibweise mit dem neuen Nullish coalescing operator.
// Long version
let secondValue = "DEFAULT_VALUE";
if (firstValue !== null && firstValue !== undefined && firstValue !== "") {
secondValue = firstValue;
}
// Shorthand with OR-Operator
secondValue = firstValue || "DEFAULT_VALUE";
// With Nullish-Operator
secondValue = firstValue ?? "DEFAULT_VALUE";
Die erste Vereinfachung, mit dem ODER-Operator, funktioniert in den meisten Fällen, deckt jedoch nicht den Fall ab mit bool‘schen Werten zu arbeiten.
Doch gehen wir es Schritt für Schritt durch und schauen erstmal warum die Varianten mit dem ODER-Operator funktioniert um dann auf den meist “besseren” Nullish coalescing operator auszuweichen.
ODER-Operator
Der binäre logische Operator (Binary Logical Operator) ||
(ODER) ist wie folgt definiert:
{Ausdruck linke Seite} ** ** {Ausdruck rechte Seite}
D.h. liefert der Ausdruck auf der linken Seite den Wert false
wird der Ausdruck auf der rechten Seite interpretiert, ansonsten wird der Ausdruck der linken Seite interpretiert.
Für unsere “Vereinfachung” von oben…
let secondValue = firstValue || "DEFAULT_VALUE";
bedeutet es, dass wenn die Variable firstValue
den Wert true
liefert, wird dieser Wert zurückgegeben (und in diesem Fall der Variablen secondValue
zugewiesen). Liefert die Variable firstValue
allerdings false
wird der Wert der rechten Seite der Variable secondValue
zugewiesen - in meinem Fall also der Wert DEFAULT_VALUE
.
Schritt für Schritt
Gehen wir mein obiges Beispiel Schritt für Schritt durch und schauen was ich meine mit…
Die erste Vereinfachung, mit dem ODER-Operator, funktioniert in den meisten Fällen, deckt jedoch nicht den Fall ab mit bool‘schen Werten zu arbeiten.
und wie uns der Nullish coalescing operator hier hilft.
Dazu packe ich mein Beispiel in eine Funktion und führe diese anschließend aus:
function doSomethingAmazing(firstValue) {
let secondValue = "DEFAULT_VALUE";
if (firstValue !== null && firstValue !== undefined && firstValue !== "") {
// Do somthing greate
secondValue = firstValue;
}
return secondValue;
}
doSomethingAmazing(1); // 1 ✅
doSomethingAmazing(42); // 42 ✅
doSomethingAmazing(null); // DEFAULT_VALUE ✅
doSomethingAmazing(""); // DEFAULT_VALUE ✅
doSomethingAmazing(/* No value means `undefined` as value */);
// DEFAULT_VALUE ✅
doSomethingAmazing(true); // true ✅
doSomethingAmazing(false); // false ✅
🥳 Alles wunderbar und der Code funktioniert auch mit bool’schen Werten. 🥳
Reflexartig setzt bei mir das Gefühl ein diesen Code zu “vereinfachen” und die Möglichkeiten von JavaScript für mich zu nutzen. Denn dass ein Wert vorhanden ist kann ich mit einem if (firstValue)
ermitteln, was zu dieser Version meines Codes führt:
function doSomethingAmazing(firstValue) {
let secondValue = "DEFAULT_VALUE";
if (firstValue) {
secondValue = firstValue;
}
return secondValue;
}
doSomethingAmazing(1); // 1 ✅
doSomethingAmazing(42); // 42 ✅
doSomethingAmazing(null); // DEFAULT_VALUE ✅
doSomethingAmazing(""); // DEFAULT_VALUE ✅
doSomethingAmazing(/* No value means `undefined` as value */);
// DEFAULT_VALUE ✅
doSomethingAmazing(true); // true ✅
doSomethingAmazing(false); // DEFAULT_VALUE ❌ 😮
😮 Upps…Wenn ich ein false
an die Funktion übergebe erhalte ich den Wert DEFAULT_VALUE
zurück und nicht wie erwartet den Wert false
🤔
Ich gehe noch einen Schritt weiter und “vereinfachen” meinen Code noch einmal; und dieses mal nutze ich den ODER-Operator:
function doSomethingAmazing(firstValue) {
// Executes the right operand ("DEFAULT_VALUE")
// only if the left operand (firstValue) is falsy
// Dieser Einzeiler wird auch short-circuiting operator genannt 😃
let secondValue = firstValue || "DEFAULT_VALUE";
return secondValue;
}
doSomethingAmazing(1); // 1 ✅
doSomethingAmazing(42); // 42 ✅
doSomethingAmazing(null); // DEFAULT_VALUE ✅
doSomethingAmazing(""); // DEFAULT_VALUE ✅
doSomethingAmazing(/* No value means `undefined` as value */);
// DEFAULT_VALUE ✅
doSomethingAmazing(true); // true ✅
doSomethingAmazing(false); // DEFAULT_VALUE ❌ 😮
Die letzte “Vereinfachung” meines Codes finde ich noch besser. Diese nimmt mir den if
-Block und macht den Code einfacher zu lesen und übersichtlicher.
Doch beide “Vereinfachung” führen zu dem selben unerwarteten Ergebnis, wenn ich die Funktion mit dem Wert false
aufrufe.
Was habe ich kaputt gemacht? 🤔
Ich habe nichts wirklich kaputt gemacht. Ich habe lediglich, in beiden Vereinfachungen, Funktionalität von JavaScript genutzt die davon ausgeht dass ein Wert falsch (false
) sein muss - also falsy ist. Im konkret Fall, mit meinem if
-Block und dem ODER-Operator, prüfe ich ob der Wert firstValue
falsch ist um dann den Wert DEFAULT_VALUE
zu nutzen.
Wann ist ein Wert “falsy”
In JavaScript ist ein Wert genau dann falsch (false
) oder falsy wenn dieser null
, undefined
, 0
oder false
ist.
Und da dieses in JavaScript nunmal so ist, habe ich mit meiner “Vereinfachung” des Codes auch gleich das Verhalten meiner Implementierung verändert 🤷
Rufe doch die letzten beiden Codebeispiele mal mit 0
(Zero) auf:
doSomethingAmazing(0);
Auch hier möchte ich dass mir der Wert 0
(Zero) zurückgegeben wird, doch ich erhalte - logischerweise - den Wert DEFAULT_VALUE
🤷
Doch kommen wir zurück zur eigentlich Implementierung mit folgendem Ausdruck im if
-Block:
firstValue !== null && firstValue !== undefined && firstValue !== "")
Daraus leitet sich meine Anforderung ab dass ich prüfen möchte ob ein Wert nullish ist und nicht ob ein Wert falsy ist, wie ich es durch meine “Vereinfachungen” (unwissentlich) gemacht habe.
Was heisst nullish
Mit nullish ist gemeint dass ein Ausdruck die Werte null
oder undefined
haben muss, nur dann ist er nullish.
Und genau dieses ist und war es, was ich mit meiner ersten Implementierung haben wollte und umgesetzt habe.
Kann ich jetzt meine einleitendes Beipiels nicht “vereinfachen”? Muss ich, von Hand, alle nullish-Werte in JavaScript selber abfragen?
😱😱😱 N E I N 😱😱😱
Der Neue - Nullish coalescing operator (??
)
Hier kommt der Neue ins Spiel - der dritten logische Operatoren in JavaScript.
Meine Damen und Herren der Nullish coalescing operator 🚀🚀🚀, der in JavaScript als ??
geschrieben wird und wie folgt definiert ist:
{Ausdruck linke Seite} ?? {Ausdruck rechte Seite}
Dieser Operator verhält sich ähnlich wie der ODER-Operator, doch mit dem entscheidenden Unterschied…
Es wird geprüft ob der Ausdruck auf der linken Seite “nullish” ist.
Und nicht wie beim ODER-Operator, ob der Ausdruck false
ist.
Ein paar Beispiele zum Nullish coalescing operator:
1 ?? "DEFAULT VALUE"; // Result is: 1 ✅
42 ?? "DEFAULT VALUE"; // Result is: 42 ✅
null ?? "DEFAULT VALUE"; // Result is: DEFAULT VALUE ✅
undefined ?? "DEFAULT VALUE"; // Result is: DEFAULT VALUE ✅
true ?? "DEFAULT VALUE"; // Result is: true ✅
false ?? "DEFAULT VALUE"; // Result is: false ✅
0 ?? "DEFAULT VALUE"; // Result is: 0 ✅
"" ?? "DEFAULT VALUE"; // Result is: "" ❓
Und mit diesem Wissen kann ich mein Codebeispiel auch wieder “vereinfachen” - und zwar so…
function doSomethingAmazing(firstValue) {
// Executes the right operand ("DEFAULT_VALUE")
// only if the left operand (firstValue) is nullish
let secondValue = firstValue ?? "DEFAULT_VALUE";
return secondValue;
}
doSomethingAmazing(1); // 1 ✅
doSomethingAmazing(42); // 42 ✅
doSomethingAmazing(null); // DEFAULT_VALUE ✅
doSomethingAmazing(/* No value means `undefined` as value */);
// DEFAULT_VALUE ✅
doSomethingAmazing(true); // true ✅
doSomethingAmazing(false); // false ✅
doSomethingAmazing(""); // "" ❓
Einen habe ich noch…
Bei meinen Beispielen mit dem Nullish coalescing operator wird Euch aufgefallen sein, dass der Aufruf meiner “vereinfachten” Funktionen mit einem leeren String (""
) nicht dazu führt das mir DEFAULT_VALUE
zurückgegeben wird.
Das ist für die Funktionsweise meines Beispiels nicht relevant, doch ich möchte Euch nicht verschweigen warum es dazu kommt.
Die Antwort liegt eigentlich klar vor uns: Der Nullish coalescing operator (??
) prüft ob ein Wert nullish ist, also null
oder undefined
ist. Und ein leerer String (""
) ist in JavaScript ein leerer String und damit weder null
noch undefined
- aber falsy 🤣
Ein weiteres Beispiel
Gehen wir noch einen Schritt weiter und wollen dieses mal tatsächlich mit bool‘schen Werten wie true
und false
arbeiten. Sagen wir, im Rahmen einer Konfiguration die genau dann einen Lebenszeichen von sich geben soll wenn wir online sind und voraussetzt dass wir (immer) online sind (per default):
function doSomethingAmazingWithAConfiguration({ online }) {
// We use the OR operator
let sendKeepAlive = online || true;
return sendKeepAlive;
}
// We say explicit that we're online
doSomethingAmazingWithAConfiguration({ online: true }); // true ✅
// We use the default-state
doSomethingAmazingWithAConfiguration({}); // true ✅
// We say explicit that we're offline ⚠️
doSomethingAmazingWithAConfiguration({ online: false }); // true ❌ 😮
An dieser Stelle des Textes habe ich jetzt mit dem “falschen” Rückgabewert des letzten Aufrufes der Funktion gerechnet, doch es ist nicht das was ich wollte.
Ich möchte das der Rückgabewert der Funktion mir false
liefert, wenn wir offline sind, also wenn wir im übergebenem Objekt den key online
auf false
setzen ({ online: false }
).
Das bekannte Problem
Mit dem gelernten macht dieses falsche Ergebnis meines Funktionsaufrufes Sinn. Denn online || true
hat mit dem letzten Aufruf folgende Werte: false || true
.
Und wenn die linke Seite des ODER-Operators false
liefert wird der Wert des Ausdrucks auf der rechten Seite genutzt (der Wert der linken Seite ist falsy) - in unserem Fall true
🤷.
Der Code funktioniert genau wie geschrieben, doch nicht wie erwartet.
Mögliche Lösungen
Für meine Funktion, die ein Konfigurationsobjekt erwartet, könnte ich mit Destructuring arbeiten und einen Defaultwert definieren:
function doSomethingAmazingWithAConfiguration({ online } = { online: false }) {
return online;
}
Oder ich nutze, statt eines Konfigurationsobjekts, ein boolean
und prüfe diese mit dem strict inequality operator (!==
):
function doSomethingAmazingWithAConfiguration({ online }) {
let sendKeepAlive = online !== false;
return sendKeepAlive;
}
Doch in diesem Artikel ist der Nullish coalescing operator der Star 🤩 und für meine Konfigurationsfunktion auch eine Lösung:
function doSomethingAmazingWithAConfiguration({ online }) {
// We use the Nullish coalescing operator
let sendKeepAlive = online ?? true;
return sendKeepAlive;
}
// We say explicit that we're online
doSomethingAmazingWithAConfiguration({ online: true }); // true ✅
// We use the default-state
doSomethingAmazingWithAConfiguration({}); // true ✅
// We say explicit that we're offline
doSomethingAmazingWithAConfiguration({ online: false }); // false ✅
Anmerkung
- Dieser Artikel ist am 29.03.2021 zuerst bei Dev.to erschienen.
- Das Hintergrundbild stammt von Science in HD auf Unsplash
Inhaltsverzeichnis
Um alle Neuigkeiten zu erfahren, abonniere hier unseren Newsletter!
Newsletter abonnierenThomas Scharke