Benutzer:Orlok/Test/JavaScript/Operatoren/void

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

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




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.


Beispiel
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.


Beispiel
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.


Beispiel
// 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.


Hinweis:

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.


Beispiel
// 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.


Beispiel
// 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.


Beispiel
// 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.


Hinweis:

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.


Beispiel
// 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.


Hinweis:

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.


Beispiel
{
  // 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