An den Tagen, an denen ich mich für ganz besonders lustig halte, verfasse ich manchmal Tweets wie diesen hier:

Wenn Tweets mit Witzen ein bisschen die Runde machen, wird der Tweet-Autor immer zum Empfänger zahlreicher Witz-Erklärungen. Da in diesem Fall aber keine der angebotenen Erklärungen etwas mit der Wahrheit hinter parseFloat() und parseInt() zu tun hatten, fühle ich mich genötigt eine kleine Erklärung des behandelten Phänomens nachzuschieben.

Wie parseFloat() funktioniert

Anders als man vielleicht zunächst glauben würde, haben parseFloat() und parseInt() in ihrer Funktionsweise unter der Haube nur wenig gemein. Die ECMAScript-Spezifikationen beschreiben die Funktion parseFloat() wie folgt:

The parseFloat function produces a Number value dictated by interpretation of the contents of the string argument as a decimal literal.

Der konkrete Algorithmus für die Interpretation des an parseFloat() übergebenen Strings sieht vor, dass, nachdem er um Whitespace am Anfang bereinigt wurde, der String von links nach rechts nach einem StrDecimalLiteral durchsucht wird. Nach dem ersten Zeichen, das nicht in die Definition von StrDecimalLiteral passt, wird die Suche beendet; so ergibt parseFloat(' 42.23€') die Number 42.23. Ein StrDecimalLiteral ist nichts weiter als ein bestimmter Brocken String-Syntax, bestehend aus einem StrUnsignedDecimalLiteral und einem optionalen Vorzeichen. Und da die Definition von StrUnsignedDecimalLiteral neben den Zahlen 0 bis 9, Kommata und Exponenten auch den String Infinity beinhaltet, produziert parseFloat('Infinity') die Number Infinity.

Wie parseInt() funktioniert

Die Kurzbeschreibung von parseInt() liest sich wie folgt:

The parseInt function produces an integer value dictated by interpretation of the contents of the string argument according to the specified radix.

Der Knackpunkt ist „according to the specified radix“. Grundsätzlich macht parseInt() das gleiche wie parseFloat(), nämlich einen String, der mit Zahlzeichen beginnt, ab der ersten Nicht-Zahl (im Falle von parseFloat() auch ab einem eventuellen Komma) abzuschneiden. Aber parseInt() ist in der Lage, Zahlen mit jeder Basis zwischen 2 und 36 zu verarbeiten, wozu ein zweiter Parameter angegeben werden kann. Das Ergebnis von parseInt('FF', 16) ist also 255. Wird der zweite Parameter nicht explizit angegeben, verwendet parseInt() seit ECMAScript 5 immer 10 (falls der zu parsende Input-String nicht gerade mit 0x beginnt). Beim Aufruf von parseInt("Infinity") sucht die Funktion also nach Ziffern von 0 bis 9 am Anfang des Strings (da ja Baisis 10 verwendet wird), findet keine, kann aus dem Eingabestring keine Zahl extrahieren und gibt gezwungenermaßen NaN zurück.

Ein Infinity-kompatibles parseInt()

Ein parseInt(), das mit Infinity klar kommt, ist einfach zu bauen, wenn man bereit ist sich auf die Basis 10 festzulegen. Das Problem besteht einzig darin, den Sonderfall Infinity abzufangen, bevor das eigentliche parseInt() auf den Plan tritt. Die hierfür scheinbar prädestinierte Funktion isFinite() würde allerdings auch für Eingaben wie "1a" mit false antworten – schließlich ist der String "1a" keine endliche (oder sonst irgendeine) Zahl. Deshalb sollte man den Eingabewert erst mittels parseFloat() in eine JavaScript-Number inklusive Infinity verwandeln, Unendlichkeit ausfiltern und schlussendlich das Ergebnis von parseFloat() in parseInt() hineinfüttern:

function parseIntInfinity(input){
  var number = parseFloat(input);
  if(!isFinite(number)){
    return number;
  }
  return parseInt(number, 10);
}

parseIntInfinity("1"); // > 1
parseIntInfinity("1F"); // > 1
parseIntInfinity("F1"); // > NaN
parseIntInfinity("Infinity"); // > Infinity
parseIntInfinity("-InfinityXXX"); // > -Infinity

Mit einer anderen Basis als 10 würde eine solche Funktion schon etwas schwieriger zu bauen. Je nach Basis könnte der String "Infinity" für eine ganze Reihe von Werten stehen, beginnend bei 1461559270678 (Basis 36) bis runter zu 18 (Basis 19, wo nach dem I abgeschnitten wird). Wie auch immer man Unendlichkeit repräsentieren möchte: mit demString "Infinity" geht es nicht, wenn parseInt() mitspielen soll.