Benutzer:Orlok/Test/JavaScript/Datentypen/Undefined

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Der primitive Datentyp Undefined besitzt nur einen einzigen Wert, nämlich undefined, der regelmäßig dann Verwendung findet, wenn ein anderer Wert nicht zur Verfügung steht. Wird also zum Beispiel eine Variable deklariert, dabei jedoch kein Wert zugewiesen, dann wird der Bezeichner der Variable automatisch mit dem Wert undefined verknüpft.




Semantik[Bearbeiten]

Der Datentyp Undefined repräsentiert allgemein die Abwesenheit eines Wertes. So werden zum Beispiel Variablen, denen bei ihrer Deklaration kein Wert zugewiesen wurde, automatisch mit dem Wert undefined initialisiert.


Beispiel
let variable;

variable; // undefined


Wenn versucht wird auf eine Objekteigenschaft zuzugreifen, jedoch weder das Objekt selbst noch einer seiner Prototypen über die gesuchte Eigenschaft verfügt, dann ist das Ergebnis dieser Operation ebenfalls der Wert undefined.


Beispiel
const object = {};

object.property; // undefined


Wird bei einer Funktionsdefinition keine Rückgabeanweisung notiert, dann wird beim Aufruf der Funktion automatisch der Wert undefined zurückgegeben. Das Gleiche gilt für den Fall, dass zwar eine Rückgabeanweisung notiert, dabei jedoch kein Wert für die Rückgabe bestimmt wurde.


Beispiel
function noop () {}

noop(); // undefined


Auch wenn wie im folgenden Beispiel eine Funktion mit weniger Argumenten aufgerufen wird als Parameter deklariert wurden, kommt Undefined zum Einsatz. In diesem Fall werden die Parameter für die kein Wert übergeben wurde mit dem Wert undefined initialisiert.


Beispiel
const identity = value => value;

identity(); // undefined


Der Datentyp Undefined kann also als eine Art Platzhalter verstanden werden. Das heißt, wenn an einer Stelle an der ein Wert erwartet wird kein Wert zur Verfügung steht, dann wird regelmäßig der Wert undefined eingesetzt.

Abgrenzung zum Datentyp Null[Bearbeiten]

Der Datentyp Undefined besitzt keine weitere Bedeutung außer der, für die Nichtverfügbarkeit eines Wertes zu stehen. Damit unterscheidet er sich von dem Datentyp Null, der für die beabsichtigte Abwesenheit eines Wertes steht.


Beispiel
function append(list, value = null) {

    // Skip operation if value is absent

    if (value != null) {
        list.push(value);
    }

    return list;
}


Soll also etwa wie in dem Beispiel oben zum Ausdruck gebracht werden, dass ein Parameter optional ist, dann würde man in Ermangelung eines passenderen Wertes den Standardwert null zuweisen. Dadurch wird kenntlich gemacht, dass an dieser Stelle entweder ein Wert oder kein Wert vorkommen kann, die mögliche Abwesenheit eines Wertes also eingeplant und sie nicht auf einen Fehler im Programm zurückzuführen ist.


Syntax[Bearbeiten]

Auf den einzigen Wert des Datentyps Undefined kann standardmäßig über den Bezeichner undefined zugegriffen werden. Ein solcher Zugriff kann wie in dem folgenden Codebeispiel erfolgen.


Beispiel
let number = 42;

number = undefined;


Hier wird zunächst eine Variable deklariert und mit einem Wert initialisiert. Dieser wird im Anschluss durch die Zuweisung des Wertes undefined überschrieben, der über den gleichnamigen Bezeichner referenziert wird.

Kein Schlüsselwort der Sprache[Bearbeiten]

Bei der Referenzierung über den Bezeichner undefined ist zu beachten, dass dieser kein Schlüsselwort der Sprache ist, sondern nur der Name einer globalen Variable. Das bedeutet, dass der Bezeichner prinzipiell bei einem Zuweisungsausdruck auf der linken Seite stehen kann, ohne dass dies in jedem Fall zu einem Fehler führt. Die globale Variable mit dem Namen undefined kann durch eine lokale Variable gleichen Namens verschattet werden.


Beispiel
{
  let undefined = 'defined';

  undefined; // 'defined'
}


undefined; // undefined


In diesem Beispiel wird zunächst durch den in geschweifte Klammern gefassten Anweisungsblock ein neuer Scope für Variablen erzeugt. Darin wird nun eine lokale Variable mit dem Bezeichner undefined deklariert, wobei ein String als Wert zugewiesen wird. In diesem Gültigkeitsbereich ist der Bezeichner jetzt nicht mehr mit dem Wert undefined verknüpft, sondern mit dem zugewiesenen String. Die globale Variable undefined besitzt weiterhin denselben Wert, wird jedoch innerhalb des Anweisungsblocks durch die lokale Variable verdeckt.


Hinweis

Dass es sich bei undefined nicht um ein reserviertes Wort handelt, kann als Fehler im Design der Sprache angesehen werden. Es ist dringend davon abzuraten, diesen Bezeichner für eigene Zwecke zu verwenden, denn später hinzugefügter Code verlässt sich unter Umständen darauf, dass der Bezeichner wie vorgesehen mit dem Wert undefined verknüpft ist. Wird eine entsprechende Referenz zu einem anderen Wert aufgelöst, kann dies zu Fehlern im Programm führen.

Schreibgeschützt im globalen Scope[Bearbeiten]

Es ist allerdings zu beachten, dass während früher auch die globale Variable mit dem Namen undefined durch den Benutzer überschrieben werden konnte, dies in aktuellen Implementierungen der Sprache nicht mehr möglich ist. Modernere Standards sehen vor, dass die Eigenschaft undefined des globalen Objektes, die Teil der globalen lexikalischen Umgebung ist, weder beschreibbar noch konfigurierbar ist, also durch ein Programm nicht verändert werden kann.


Beispiel
// global scope

undefined = 'defined';

undefined; // undefined


Der Versuch der globalen Variable undefined einen anderen Wert zuzuweisen bleibt dementsprechend wirkungslos, sprich, die Zuweisung wird im normalen Ausführungsmodus einfach ignoriert. Wird das Programm hingegen im Strict Mode ausgeführt, dann erzeugt die Zuweisung einen TypeError. Man kann sich in modernen Ausführungsumgebungen also darauf verlassen, dass die Variable undefined wenigstens im globalen Namensraum nicht überschrieben wurde.


Typprüfung[Bearbeiten]

Anders als bei einigen anderen Datentypen gibt es für Undefined keinen gleichnamigen Konstruktor. Die Konvertierung in ein Objekt und nachfolgende Prüfung der Abstammung scheidet als Mittel der Typbestimmung dem zur Folge aus. Es gibt aber mehrere Möglichkeiten, auf den Wert undefined zu testen.


Beispiel
// Check whether a given value is not undefined

const isDefined = value => typeof value !== 'undefined';


// Usage

isDefined(undefined); // false


Eine Helferfunktion für die Prüfung auf den Wert undefined die den Operator typeof verwendet, könnte wie in dem Beispiel oben aussehen, wobei sinnigerweise geprüft wird, ob der übergebene Wert nicht vom Typ Undefined ist, da man in der Regel eher an dieser Information interessiert ist.

Verwendung des Operators typeof[Bearbeiten]

Die Frage, ob ein bestimmter Wert vom Typ Undefined ist, kann also mit dem Operator typeof beantwortet werden, der die Zeichenkette undefined zurückgibt, wenn der als Operand notierte Ausdruck zu diesem Wert aufgelöst wird.


Beispiel
let value = undefined;

typeof value; // 'undefined'


In diesem Beispiel wird zunächst eine Variable deklariert und explizit mit dem Wert undefined initialisiert. Eine Referenz auf die Variable wird danach dem Operator typeof übergeben. Bei der Auswertung des Operanden wird die Referenz erfolgreich aufgelöst und der Typ korrekt bestimmt.


Hinweis

Wenn die Referenz nicht hätte aufgelöst werden können, da innerhalb der Scope Chain keine Bindung für den angegebenen Bezeichner existiert, wäre das Ergebnis der Operation übrigens dasselbe gewesen. Statt einen ReferenceError zu erzeugen, wird bei Verwendung von typeof in einem solchen Fall die Zeichenkette undefined zurückgegeben.

Direkter Vergleich mit der Variable undefined[Bearbeiten]

Eine weitere Möglichkeit zur Typprüfung besteht natürlich im direkten Vergleich mit dem einzigen Wert des Typs Undefined, der über den Bezeichner undefined referenziert werden kann.


Beispiel
let variable = undefined;

variable === undefined; // true


Wie bereits gesehen, handelt es sich bei undefined jedoch um eine globale Variable, die durch gleichnamige lokale Variablen verschattet werden kann. Bei einer Prüfung wie in dem Beispiel oben besteht also das grundsätzliche Risiko, dass eine lokale Variable mit einem anderen Wert als undefined referenziert und das Ergebnis der Prüfung verfälscht wird.


Hinweis

Es ist darüber hinaus zu beachten, dass eine Prüfung wie in dem Beispiel oben zu einem Fehler führt, wenn die in dem Vergleichsausdruck verwendete Referenz nicht aufgelöst werden kann. Dass bei der Verwendung von typeof in einem solchen Fall kein Fehler geworfen wird, stellt eine Ausnahme dar, nicht die Regel. Diesen Umstand gilt es zu berücksichtigen bei der Entscheidung, welche Methode zur Prüfung des Typs verwendet werden soll.

Verwendung des Operators void[Bearbeiten]

Statt den Wert undefined über die globale Variable gleichen Namens zu referenzieren, kann eine Vergleichsprüfung auch unter Verwendung des Operators void erfolgen. Dieser Operator nimmt einen beliebigen Ausdruck entgegen, wertet ihn aus, und gibt dann unabhängig vom Ergebnis den Wert undefined zurück.


Beispiel
const variable = undefined;

variable === void 0; // true


Wird wie in diesem Beispiel geprüft, kann das Risiko ausgeschlossen werden, unbeabsichtigt einen anderen Wert als undefined zu referenzieren. Da void für jeden Ausdruck undefinded zurückgibt und an der Auswertung des Ausdrucks kein Interesse besteht, genügt es an dieser Stelle die Zahl Null zu notieren.


Typkonvertierung[Bearbeiten]

Wenn kein typsicherer Vergleich durchgeführt wird, also mittels == oder != auf Gleichheit oder Ungleichheit getestet wird, dann wird bei unterschiedlichen Datentypen der Operanden unter Umständen implizit eine Typkonvertierung durchgeführt, um die Werte miteinander vergleichen zu können. Dies trifft auf den Wert undefined jedoch nur eingeschränkt zu. In Bezug auf Undefined gilt, dass wenn einer der Operanden vom diesem Typ ist, Gleichheit nur dann angenommen wird, wenn der andere Operand entweder ebenfalls vom Typ Undefined ist, oder aber vom Typ Null.


Beispiel
// Type -> Boolean

const isDefined = value => value != null;


console.log(isDefined(undefined)); // false


Wenn es also darum geht herauszufinden, ob ein Wert weder undefined noch null ist, kann die Prüfung entsprechend wie in dem Beispiel oben erfolgen. Da nur diese beiden Werte sich gleichen, genügt es gegen einen der Werte zu testen, um beide Fälle abzudecken. Aus Gründen der besseren Lesbarkeit des Codes kann es aber dennoch sinnvoll sein, mit einem typsicheren Vergleich, also mittels === beziehungsweise !==, explizit gegen beide Werte zu testen.


Beispiel
const types = [Boolean, Number, String];


// call type constructors

types.forEach(Type => console.log(Type(undefined)); // false NaN undefined


Grundsätzlich kann der Wert undefined in drei andere Datentypen konvertiert werden, nämlich Boolean, Number und String, was manuell durch den Aufruf der gleichnamigen Funktionen wie in dem Beispiel oben bewerkstelligt werden kann. Da es sich bei undefined um einen sogenannten falsy value handelt, ergibt die Konvertierung in den Typ Boolean den Wert false. Die Konvertierung in einen Wert vom Typ Number ergibt NaN, und die Umwandlung in einen Wert vom Typ String ergibt die Zeichenkette undefined. Die Konvertierung in andere Datentypen ist nicht vorgesehen.


Beispiel
// Object -> String

const assign = object => object.property = 'value';


console.log(assign(undefined)); // Type Error


Hierbei ist insbesondere zu beachten, dass undefined anders als einige andere Datentypen nicht automatisch in ein Objekt konvertiert wird. Das bedeutet, wenn wie in dem Beispiel oben über einen Elementausdruck lesend oder schreibend auf eine Objekteigenschaft zugegriffen wird, dabei jedoch statt eines Objektes der primitive Wert undefined referenziert wird, dann führt dies unweigerlich zu einem Typfehler. Sofern also nicht mit Gewissheit davon ausgegangen werden kann, dass in einer solchen Situation ein Objekt referenziert wird, ist es ratsam vor dem Zugriff eine entsprechende Prüfung vorzunehmen.


Variablen und Undefined[Bearbeiten]

Variablen, denen bei ihrer Deklaration kein Wert zugewiesen wurde, werden standardmäßig mit dem Wert undefined initialisiert. Das bedeutet, wann immer eine Variable deklariert wird, unabhängig davon, ob es sich um eine globale oder um eine lokale Variable handelt, oder ob dies unter Verwendung des Schlüsselwortes var oder mittels let geschieht, ist ihr Wert solange undefined, bis dieser durch die Zuweisung eines anderen Wertes überschrieben wird.


Beispiel
let variable;

console.log(variable); // undefined


In dem Beispiel oben wird mittels let eine Variable deklariert ohne dabei einen Wert zuzuweisen. Wie die darauf folgende Ausgabe in der Konsole bestätigt, wurde die Variable implizit mit dem Wert undefined initialisiert. Hierbei ist zu erwähnen, dass die Bindung an den Wert undefined, anders als bei der Verwendung des Schlüsselwortes var, erst zum Zeitpunkt der Deklaration erfolgt. Variablen die auf diese Weise deklariert werden, können nicht vor ihrer Deklaration referenziert werden.


Hinweis

Die implizite Zuweisung des Wertes undefined betrifft übrigens tatsächlich nur Variablen, nicht jedoch symbolische Konstanten, welche unter Verwendung des Schlüsselwortes const deklariert werden können. Der naheliegende Grund dafür ist, dass Konstanten während ihrer Lebenszeit grundsätzlich nur an einen einzigen Wert gebunden sein können, sie also nicht überschrieben werden dürfen. Daraus folgt, dass einer Konstante bei ihrer Deklaration grundsätzlich ein Wert zugewiesen werden muss, und dementsprechend kein Raum für eine implizite Zuweisung bleibt.


Anders als Variablen die mit let deklariert wurden, können mittels var deklarierte Variablen bereits vor ihrer Deklaration referenziert werden, da die Deklaration implizit an den Anfang des mit dem jeweiligen Ausführungskontext verknüpften Codes gezogen wird. Mithin erfolgt die Bindung des Wertes undefined an den Bezeichner der Variable in diesem Fall bereits bei ihrer Erzeugung, und nicht erst zum Zeitpunkt ihrer Deklaration im Quelltext. Dies kann zu überraschenden Ergebnissen führen, und es begünstigt das Schreiben von schlecht lesbarem und potentiell fehlerträchtigem Code. Dies ist einer der Gründe, weshalb von der Verwendung des Schlüsselwortes var für die Deklaration von Variablen im Allgemeinen abzuraten ist.


Beispiel
var number = 5;


// Undefined -> Undefined

function test () {

  console.log(number);

  var number = 10;
}


In diesem Beispiel wird zunächst eine globale Variable mit dem Namen number angelegt und dabei ein Wert zugewiesen. Danach wird eine Funktion deklariert, deren erste Anweisung darin besteht, den Wert der Variable number in die Konsole zu schreiben. Innerhalb der Funktion wird schließlich noch eine lokale Variable gleichen Namens deklariert, der ebenfalls ein Wert zugewiesen wird. Die Frage ist nun, welcher Wert wird in die Konsole geschrieben, wenn die Funktion aufgerufen wird?


Beispiel
test();

// undefined


Die vielleicht nicht unmittelbar einleuchtende Antwort ist weder Fünf noch Zehn, sondern undefined, denn referenziert wird hier die lokale Variable number, die bereits beim Eintritt in die Funktion und vor ihrer Deklaration im Quelltext mit diesem Wert initialisiert wurde. Im Sinne von gut lesbarem und robustem Code sei jedenfalls dringend empfohlen, niemals Variablen vor ihrer Deklaration zu referenzieren, auch wenn dies wie gesehen möglich ist!


Undefined im Zusammenhang mit Funktionen[Bearbeiten]

In JavaScript muss die Anzahl der Argumente mit denen eine Funktion aufgerufen wird nicht zwingend mit der Anzahl der formalen Parameter der Funktion übereinstimmen. Wird eine Funktion mit weniger Argumenten aufgerufen als bei der Definition der Funktion angegeben wurden, dann wird dementsprechend keine Ausnahme geworfen, sondern die Parameter für die kein Argument übergeben wurde werden einfach mit dem Wert undefined initialisiert.


Beispiel
// Type -> Undefined

const log = value => console.log(value);


log(); // undefined


In dem Beispiel oben wird eine Funktion mit einem Parameter definiert und einer Konstante als Wert zugewiesen. Die einzige Anweisung der Funktion besteht darin, den Wert des Parameters in die Konsole zu schreiben. Bei dem anschließenden Aufruf der Funktion wird jedoch kein Argument übergeben, was dazu führt, dass undefined in der Konsole ausgegeben wird, da der formale Parameter der Funktion an diesen Wert gebunden wurde.


Hinweis

Es kann übrigens einen Unterschied machen, ob kein Argument übergeben wurde, oder ob eine Funktion explizit mit dem Wert undefined aufgerufen wurde. Wird etwa eine zweistellige Funktion mit nur einem Wert aufgerufen, dann wird der erste Parameter mit diesem Wert initialisiert und der zweite implizit mit undefined. Wird an erster Stelle hingegen undefined übergeben und an zweiter Stelle ein anderer Wert, dann wird dies bei der Zuweisung berücksichtigt und beiden Parametern die übergebenen Werte zugewiesen.


Der Wert undefined ist darüber hinaus der Standardwert, der von Funktionen zurückgegeben wird. Das heißt, in dem Fall, dass bei der Definition einer Funktion kein Rückgabewert angegeben wurde, also entweder gar keine return Anweisung oder nach dem Schlüsselwort return kein Ausdruck notiert wurde, dann wird grundsätzlich undefined zurückgegeben.


Beispiel
// Undefined -> Undefined

function doNothing () {

  // no return statement
}


console.log(doNothing()); // undefined


In diesem Beispiel wird ein Funktion deklariert, die keine Parameter erwartet und die in ihrem Körper keinerlei Anweisungen enthält. Diese Funktion wird nun aufgerufen und ihr Rückgabewert in die Konsole geschrieben, bei dem es sich wie erwartet um den Wert undefined handelt. Wann immer also eine Funktion keine Rückgabeanweisung enthält, wird implizit nach der letzten Anweisung ein return undefined eingefügt.


Objekte und Undefined[Bearbeiten]

Wird auf eine Objekteigenschaft zugegriffen, dann wird intern zunächst geprüft, ob das Objekt über eine eigene Eigenschaft mit dem angegebenen Namen verfügt. Ist dies nicht der Fall, dann wird die Kette der Prototypen des Objektes nach der Eigenschaft durchsucht. Findet sich die gesuchte Eigenschaft weder auf dem angesprochenen Objekt noch auf einem seiner Prototypen, so wird standardmäßig der Wert undefined zurückgegeben.


Beispiel
const object = {};

console.log(object.property); // undefined


In dem Beispiel oben wird zunächst in Literalschreibweise ein planes Objekt ohne eigene Eigenschaften erzeugt und einer Konstante als Wert zugewiesen. Danach wird versucht auf die Eigenschaft mit dem Namen property zuzugreifen. Da nun weder das referenzierte Objekt noch dessen Prototyp Object.prototype über eine Eigenschaft dieses Namens verfügen, wird schließlich undefined in die Konsole geschrieben. Eine solche Eigenschaft ist für das Objekt also nicht definiert.


Spezifikation[Bearbeiten]

ECMAScript — The Undefined Type