Benutzer:Orlok/Test/JavaScript/Operatoren/void
Der unäre Operator void nimmt einen beliebigen Ausdruck entgegen, wertet ihn aus, und gibt dann unabhängig vom Ergebnis den primitiven Wert undefined zurück.
Syntax
void expression
Inhaltsverzeichnis
Anwendung des Operators
Der Operator void
wird als Präfix vor seinem Operanden notiert, bei dem es sich prinzipiell um einen beliebigen Ausdruck handeln kann. Bei der Ausführung des Codes wird der Ausdruck zwar ausgewertet und kann entsprechend Nebenwirkungen entfalten, der ermittelte Wert wird jedoch verworfen. Es wird grundsätzlich der Wert undefined
zurückgegeben.
let result = void 5;
console.log(result); // undefined
In dem Beispiel oben wird zunächst eine Variable deklariert und ihr dabei das Ergebnis der Anwendung von void
auf die Zahl Fünf zugewiesen. Wie erwartet wird der numerische Wert ignoriert und statt dessen der Wert undefined zurückgegeben, der schließlich in die Konsole geschrieben wird. Da es sich bei dem an void
übergebenen Ausdruck in diesem Fall lediglich um ein Zahlenliteral handelt, ist eine umschließende Klammer entbehrlich. Die Rangfolge der Operatoren ist jedoch grundsätzlich zu beachten und der Ausdruck wenn es nötig ist in eine Klammer zu fassen.
let result = void 2 + 5;
console.log(result); // NaN
Dieses Beispiel gleicht dem Letzten, mit dem augenfälligen Unterschied, dass hier statt einer einzelnen Zahl die Addition zweier Zahlen notiert ist. Es wird in diesem Fall jedoch nicht das Ergebnis der Addition an den Operator void
übergeben, denn da dieser stärker bindet als Plus, wird hier zunächst void auf die Zahl Zwei angewendet, was wie üblich undefied ergibt. Dieser Wert wird dann versucht zu dem Wert Fünf zu addieren, was nicht möglich ist, weshalb das Ergebnis dieser Addition und damit des gesamten Ausdrucks NaN
ist. Hier hätte man also eine Klammer setzen müssen.
Ersatz für die globale Variable undefined
Ein wesentlicher Verwendungszweck für den Operator void
besteht darin, auf den Wert undefined zugreifen zu können, ohne diesen über den Bezeichner undefined
referenzieren zu müssen. Bei undefined handelt es sich nämlich nicht um ein Literal, beziehungsweise um ein Schlüsselwort der Sprache, sondern lediglich um eine globale Variable, die durch lokale Variablen verschattet werden kann. Durch die Verwendung von void
wird also eine potentielle Fehlerquelle ausgeschlossen.
// Type -> Boolean
const isDefined = value => value !== void 0;
console.log(isDefined('defined')); // true
In diesem Beispiel wird eine Konstante deklariert und mit einem Funktionsausdruck initialisiert. Die Funktion nimmt einen beliebigen Wert entgegen und prüft, ob dieser definiert, also nicht undefined
ist. Dabei wird dem Operator void
einfach die Zahl Null übergeben, denn es kommt ohnehin nur auf den Rückgabewert an, also den Wert undefined. Schließlich wird die Funktion mit einem String als Argument aufgerufen und als Ergebnis der Wert true
in die Konsole geschrieben, da der übergebene Wert natürlich nicht undefined ist.
Die globale Variable undefined
kann zwar durch lokale Variablen gleichen Namens verschattet werden, jedoch ist es in modernen Implementierungen der Sprache nicht mehr möglich, der Variable im globalen Namensraum einen anderen Wert zuzuweisen. Das Risiko, versehentlich einen anderen Wert als undefined zu referenzieren, ist demnach zu vernachlässigen. Aus Gründen der besseren Lesbarkeit des Codes sollte also in solchen Fällen auf den Einsatz von void
verzichtet und der Wert wie vorgesehen über den Bezeichner undefined
referenziert werden.
Der Grund dafür, weshalb eine Prüfung wie in dem Beispiel oben oft sinnvoll ist, besteht darin, dass anders als bei einigen anderen Datentypen, der primitive Wert undefined
nicht implizit in ein Objekt konvertiert wird. Das heißt, wenn zum Beispiel versucht wird über einen Elementausdruck den Wert einer Objekteigenschaft zu verändern, die in diesem Ausdruck angegebene Referenz jedoch nicht zu einem Objekt sondern zu dem Wert undefined
aufgelöst wird, dann wird dadurch ein Typfehler erzeugt, der das Programm beendet. Das Gleiche gilt jedoch auch für den Datentyp Null, weshalb man einen Test wie in dem Beispiel oben in der Regel so schreiben möchte, dass auch der primitive Wert null
ausgeschlossen wird.
// Type -> Boolean
const isDefined = value => value != null;
console.log(isDefined(undefined), isDefined(null)); // false false
Anders als in dem ersten Beispiel wird hier nicht gegen das Ergebnis von void
getestet, sondern statt dessen mittels !=
eine typunsichere Prüfung auf Ungleichheit gegen den Wert null
durchgeführt. Da bei einem solchen Vergleich nur zwischen den Datentypen Null und Undefined konvertiert wird, ergibt die Prüfung oben nur dann true
, wenn der geprüfte Wert tatsächlich weder null
noch undefined
ist. Da dieser Zusammenhang jedoch nicht ohne Weiteres ersichtlich ist, kann es unter Umständen ratsam sein, durch typsichere Vergleiche mit !==
explizit gegen beide Werte zu testen.
Funktionen und void
In der Sprache C und einigen verwandten Programmiersprachen ist void
ein eigenständiger Datentyp, der eine ähnliche Bedeutung besitzt wie der Datentyp Undefined in JavaScript, nämlich dass an einer bestimmten Stelle kein spezifischer Wert vorgesehen ist. In solchen statisch typisierten Sprachen wird das Schlüsselwort void
zum Beispiel dafür verwendet, bei der Deklaration einer Funktion anzugeben, dass die Funktion keine Parameter erwartet oder keinen Wert zurückgibt.
// Void -> Void
void some_output (void) {
// for side effects only
printf("message");
}
JavaScript ist eine dynamisch typisierte Sprache und kennt keine Typangaben wie in diesem in C geschriebenen Beispiel. Weder muss noch kann der Datentyp des Rückgabewerts einer Funktion auf diese Weise festgelegt werden, und eine Beschränkung der Parameter der Funktion auf bestimmte Datentypen kann auf diese Art ebenfalls nicht erzwungen werden. Dennoch kann es unter Umständen auch in JavaScript sinnvoll sein, einer Funktionsdefinition das Schlüsselwort void
voranzustellen.
// Undefined -> Undefined
void function () {
// local function scope
var string = 'message';
console.log(string);
}();
In diesem Beispiel wird eine anonyme, also namenlose Funktion definiert und im Anschluss direkt aufgerufen, indem nach dem Anweisungsblock, der den Körper der Funktion umschließt, ein Paar runde Klammern notiert werden. Der implizite Rückgabewert der Funktion ist undefined
und dieser Wert wird an den Operator void
übergeben, mit der Folge, dass dies auch der Wert des gesamten Ausdrucks ist. Innerhalb der Funktion wird mit dem Schlüsselwort var
eine Variable deklariert, die nur dort sichtbar ist, nicht jedoch außerhalb der Funktion. Der Wert der Variable wird schließlich ausgegeben.
Ein solches Konstrukt wird als unmittelbar aufgerufener Funktionsausdruck (Immediately-invoked Function Expression) bezeichnet und dient dem Zweck Code auszuführen, ohne dem umgebenden Gültigkeitsbereich eine Bindung für einen Namen hinzuzufügen, sodass potentielle Namenskonflikte vermieden werden. Erreicht der Programmfluss diesen Ausdruck, dann wird der Code in der Funktion ausgeführt, das Ergebnis der Funktion verworfen, und mit der darauf folgenden Anweisung weitergemacht, ohne dass in diesem Kontext eine Funktion oder Variable deklariert wurde.
Der Grund dafür, dass der Funktionsdefinition void
vorangestellt wird ist, dass sonst versucht würde, die Definition als eine Deklaration zu interpretieren, was einen Syntaxfehler produzieren würde, da eine solche Funktionsdeklaration immer die Angabe eines Namens erfordert. Weil das Ziel hier gerade darin besteht, der jeweiligen lexikalischen Umgebung keinen weiteren Namen hinzuzufügen, muss also dafür gesorgt werden, dass die Definition der Funktion als Ausdruck statt als Deklaration interpretiert wird. Dies wird durch die Übergabe an den Operator void
erreicht.
// Undefined -> Undefined
(function () {
// do something
}());
Ein unmittelbar aufgerufener Funktionsausdruck muss allerdings nicht zwingend mittels void
konstruiert werden. Um zu errechen, dass die Funktion als Ausdruck interpretiert wird, kann die Definition auch anderen Operatoren übergeben werden. In der Praxis wird hierzu in aller Regel der Operator zur Gruppierung von Ausdrücken verwendet, die Definition der Funktion also einfach innerhalb runder Klammern notiert, so wie in dem Beispiel oben. Ob die Klammern zum Aufruf der Funktion dabei innerhalb oder außerhalb der Klammern für die Gruppierung notiert werden ist dabei nicht von Belang.
Außer void
und ()
werden gelegentlich auch andere Operatoren zu diesem Zweck genutzt, wie zum Beispiel die logische Negation. Obwohl grundsätzlich möglich, ist davon jedoch dringend abzuraten, da hierdurch die Lesbarkeit des Codes unnötig beeinträchtigt wird. Werden Klammern verwendet, dann ist als Zweck unmittelbar ersichtlich, dass die Funktion als Ausdruck ausgewertet werden soll. Wird der Operator void
verwendet, dann wird dokumentiert, dass die Funktionsdefinition als Ausdruck ausgewertet und das Ergebnis des Aufrufs der Funktion verworfen werden soll. Bei anderen Operatoren ist jedoch keine Verbindung zwischen der Semantik des Operators und dem beabsichtigten Zweck erkennbar, wodurch das Verständnis des Codes erschwert wird. Dies sollte vermieden werden.
Abschließend sei darauf hingewiesen, dass es heute durch die Weiterentwicklung von JavaScript nur noch selten Bedarf an unmittelbar aufgerufenen Funktionsausdrücken gibt, und dieser Anwendungsfall für den Operator void
mithin kaum noch relevant ist. Denn mit den Schlüsselwörtern let
und const
können heute Variablen und symbolische Konstanten deklariert werden, die anders als mit var
deklarierte Variablen nicht grundsätzlich im gesamten Ausführungskontext sichtbar sind.
{
// block scope
let name = 'value';
}
Wird also etwa wie in dem Beispiel oben innerhalb eines Anweisungsblocks mit let
eine Variable deklariert, dann ist diese nur innerhalb des Blocks sichtbar. Namenskonflikte mit Bezeichnern, die in der äußeren lexikalischen Umgebung gebunden sind, können hier anders als bei Variablen die mit var
deklariert wurden nicht auftreten. Da ein Anweisungsblock auch als einzelne Anweisung notiert werden kann, indem einfach ein paar geschweifte Klammern eingefügt werden, besteht keine Notwendigkeit mehr für die Kapselung des Codes im Körper einer Funktion.
Spezifikation
ECMAScript — The Void Operator