JavaScript/Operatoren/typeof

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Unter Verwendung des Operators typeof kann der Datentyp eines Wertes ermittelt werden. Er wird dem Operanden vorangestellt, bei dem es sich um einen beliebigen Ausdruck handeln kann. Abhängig vom Ergebnis der Auswertung des Operanden wird ein String zurückgegeben, der den Bezeichner des entsprechenden Datentyps enthält.


Syntax

typeof expression


Datentypen[Bearbeiten]

Es gibt insgesamt sieben verschiedene Datentypen, nämlich Undefined, Null, Boolean, String, Symbol, Number und Object. Dabei werden die sechs erstgenannten auch als primitive Datentypen bezeichnet, wohingegen man bei Object von einem abstrakten Datentyp spricht. Das bedeutet, dass zum Beispiel Funktionen oder auch Arrays grundsätzlich vom Datentyp Object sind, ebenso wie jeder andere Wert, der sich nicht einem der primitiven Datentypen zuordnen lässt.


In diesem Zusammenhang ist nun zu beachten, dass die definierten Datentypen der Sprache durch die möglichen Rückgabewerte des Operators typeof nicht eins zu eins abgebildet werden. Das heißt, in bestimmten Fällen entspricht der von typeof ermittelte Typ nicht dem eigentlichen Datentyp des geprüften Wertes. Dieses Verhalten ist teilweise erwünscht und beabsichtigt, teilweise aber auch auf Fehler im Design der Sprache zurückzuführen.

Verwendung[Bearbeiten]

Daten werden bei Bedarf automatisch in den jeweils benötigten Typ umgewandelt, so dies grundsätzlich möglich ist. Es ist jedoch aufgrund der dynamischen Typisierung der Sprache, bei der die Typbestimmung erst zur Laufzeit des Programms erfolgt, nicht selten erforderlich, einen Wert eigenhändig auf seinen Typ hin zu überprüfen, um schwerwiegende Fehler im Programmablauf zu vermeiden. Dabei kann der Operator typeof von Nutzen sein.


Beispiel
function assign (object) {
  if (typeof object === 'object') { // null ?
    object.name = 'value';
  }
}

assign( );


Die in diesem Beispiel definierte Funktion erwartet als Parameter ein Objekt, dem eine Eigenschaft zugewiesen werden soll. Beim Aufruf der Funktion wird jedoch kein Argument übergeben, sodass der Funktionsparameter mit dem primitiven Wert undefined initialisiert wird. Da in diesem Fall keine implizite Typumwandlung erfolgt, würde hier durch den Versuch der Eigenschaftszuweisung ein Type Error produziert, welcher unbehandelt das Programm zum Absturz bringen würde.


Um nun zu verhindern, dass durch den fehlerhaften Funktionsaufruf eine entsprechende Ausnahme geworfen wird, ist die Eigenschaftszuweisung hier in den Anweisungsblock eines Conditional Statements geschrieben. Das heißt, sie wird abhängig gemacht vom Ergebnis der Überprüfung des Parameters mittels typeof und nur dann ausgeführt, wenn es sich bei dessen Wert um ein Objekt handelt. Oder um das, was der Operator typeof für ein Objekt hält.


Hinweis

Die von typeof in Form von Strings zurückgegebenen Typbezeichner sind durchgängig klein geschrieben. Wenn also wie hier in diesem Beispiel geprüft wird, so ist der als Vergleichswert notierte Name des Typs ebenfalls zwingend klein zu schreiben, denn unabhängig davon ob die entsprechenden Werte auf Gleichheit oder auf Identität hin untersucht werden, ergibt der Vergleich von Strings nur dann true, wenn auf beiden Seiten des Ausdrucks tatsächlich dieselben Zeichen notiert sind.


Jedenfalls ist eine Überprüfung wie in dem gegebenen Beispiel tatsächlich nicht empfehlenswert, denn aufgrund eines Designfehlers der Sprache würde die bedingte Anweisung hier auch dann ausgeführt, wenn kein Objekt sondern der primitive Wert null an die Funktion übergeben wurde. Denn der Operator typeof gibt auch bei der Prüfung des Wertes null die Zeichenkette object zurück und nicht etwa null, wie es dem eigentlichen Datentyp entspräche.

Besonderheiten[Bearbeiten]

Wie gesehen entspricht der von typeof zurückgegebene Typ nicht immer dem tatsächlichen Datentyp des untersuchten Wertes. Darum sind im Folgenden diejenigen Fälle aufgeführt, bei denen das Ergebnis der Überprüfung mittels typeof nicht den oben aufgelisteten Datentypen entspricht, ebenso wie Fälle, bei denen die Zuordnung zu einem Typ beziehungsweise der Rückgabewert von typeof im Allgemeinen nicht ohne weiteres ersichtlich ist.


Bereits festgestellt haben wir, dass für den primitiven Wert null fälschlicherweise der Typ Object identifiziert wird und nicht der eigentlich korrekte Typ Null. Dabei handelt es sich tatsächlich um einen Fehler in der Sprache, der jedoch nicht behoben werden kann, da sonst viele bestehende Programme nicht mehr funktionieren würden.


Information

Die Ursache für die falsche Interpretation des Wertes null durch den Operator typeof liegt in der Implementierung der ersten Version der Sprache begründet. Damals wurden die verschiedenen Werte als 32 Bit Einheiten gespeichert, die sich aus dem eigentlichen Wert und einer zwischen ein und drei Bit langen Markierung zur Identifizierung des Typs zusammensetzten. Werte vom Typ Null wurden jedoch nicht auf diese Weise repräsentiert, sondern durch einen sogenannten Nullzeiger, also einen Verweis auf eine bestimmte Speicheradresse, die explizit für diesen Wert reserviert ist. Bei der für typeof ausgeführten internen Prüfung wurden nun die von dieser Adresse gelesenen Bits aufgrund identischer Werte fälschlicherweise als Markierung für den Typ Objekt interpretiert.


Anders als bei dem Wert null ist die vom eigentlichen Datentyp abweichende Interpretation durch typeof bei Funktionen beabsichtigt. Zwar sind Funktionen grundsätzlich vom Typ Object, jedoch ist aufgrund ihrer besonderen Semantik eine separate Typangabe gerechtfertigt. Entsprechend wird, wenn es sich bei dem durch typeof zu prüfenden Operanden um ein Funktionsobjekt handelt, die Zeichenkette function zurückgegeben.


Beispiel
function test (value) {
  return value;
}

console.info(typeof test); // function


Aus dem gleichen Grund wie Funktionen hätten eigentlich auch Arrays eine Sonderbehandlung durch typeof verdient, zumal diesen exotischen Objekten ebenfalls eine herausragende Rolle innerhalb der Sprache zukommt. Wer hier nun allerdings erwartet hat, eine entsprechende Prüfung mittels typeof würde array ergeben, wird enttäuscht.


Beispiel
var array = ['liberté', 'égalité', 'fraternité'];

console.info(typeof array); // object


Wie in diesem Beispiel gezeigt, ergibt die Überprüfung eines Arrays mittels typeof den Wert object, was zwar formal korrekt ist, aber nicht unbedingt immer hilfreich. Wenn es also erforderlich ist einen Wert daraufhin zu überprüfen, ob es sich bei ihm um ein Array handelt, dann ist der Operator typeof definitiv nicht das Mittel der Wahl. Statt dessen kann eine solche Prüfung unter Verwendung der Methode isArray des Konstruktors Array erfolgen, wobei allerdings zu berücksichtigen ist, dass der von dieser Methode ausgeführte Test nur für normale Arrays funktioniert, für andere Indexed Collections wie beispielsweise Typed Arrays aber nicht zu gebrauchen ist.


Beispiel
const check = value => console.log(Array.isArray(value));

check(new Array(9)); // true

check(new Float32Array(16)); // false

(function ( ) {
  check(arguments); // false
}( ));


Ein weiterer Fall, bei dem die Zuordnung zu einem bestimmten Typ nicht gerade offensichtlich ist, ist der Wert NaN, denn obwohl dessen Bezeichner für Not a Number steht, handelt es sich hierbei dennoch um einen Wert vom Datentyp Number. Gleiches gilt im Übrigen natürlich auch für den Wert Infinity, weshalb in beiden Fällen der String number zurückgegeben wird.


Beispiel
console.info(typeof NaN); // number

console.info(typeof Infinity); // number


Dem Wert NaN wohnt indes eine besondere Tücke inne, insofern er der einzige Wert ist, der nicht mit sich selbst gleich ist. Zwar handelt es sich dabei um einen Falsy Value, weshalb er leicht auszuschließen ist, aber zur Prüfung ob es sich bei einem Wert um NaN handelt, muss die Methode isNaN bemüht werden.


Beispiel
(function (value) {
  console.log(value == NaN); // false
  console.log(isNaN(value)); // true
}(NaN));


In diesem Beispiel wird für einen unmittelbar aufgerufenen Funktionsausdruck ein Parameter deklariert und mit dem Wert NaN befüllt. Obwohl in der Folge nicht einmal auf Identität (Strict Equality) sondern nur auf Gleichheit (Lenient Equality) getestet wird, ergibt die erste Prüfung dennoch false. Erst mittels isNaN wird bestätigt, dass es sich bei dem Wert des Parameters um NaN handelt.


Beispiel
const result = typeof undeclared;

console.log(result); // undefined


Handelt es sich bei dem auszuwertenden Ausdruck um eine unauflösbare Referenz (Unresolvable Reference), sprich, gibt es innerhalb der Kette der Gültigkeitsbereiche (Scope Chain) keinen Bezeichner mit dem gesuchten Namen, dann wird von typeof standardmäßig der Wert undefined zurückgegeben und keine Ausnahme geworfen.


Beispiel
console.log(typeof state); // Reference Error

let state = true;


Anders sieht es hingegen aus, wenn eine Variable oder Konstante geprüft wird, die sich in der sogenannten Temporal Dead Zone befindet, die also zum Zeitpunkt der Prüfung im Code noch nicht deklariert wurde. In diesem Fall wird immer ein Fehler produziert, da es sich nicht um eine unauflösbare Referenz handelt, sondern die Variable oder Konstante lediglich noch nicht initialisiert wurde und ein Zugriff somit unmöglich ist.

Anmerkungen[Bearbeiten]

Bei der Verwendung von typeof muss freilich wie immer die Operatorenreihenfolge berücksichtigt werden, da es sonst zu unerwarteten Ergebnissen und daraus resultierend zu Programmfehlern kommen kann.


Beispiel
var type = typeof 3 + 5;

console.log(type === 'number'); // false

console.log(type); // number5


Der Operator typeof hat eine höhere Präzedenz als der Plusoperator, weshalb hier zunächst der Datentyp der Zahl 3 bestimmt wird, was entsprechend den String number ergibt. Ist nun einer der Operanden links oder rechts des Pluszeichens ein String, dann wird der jeweils andere Operand implizit in einen String umgewandelt und das Ergebnis des Ausdrucks ist die Konkatenation der beiden Zeichenketten. In diesem Beispiel hätten die Addition der Werte 3 und 5 also in eine Klammer gefasst werden müssen.

Weblinks[Bearbeiten]