parseInt (null, 24) === 23… warte, was?

226

Okay, also habe ich mit parseInt herumgespielt, um zu sehen, wie es mit Werten umgeht, die noch nicht initialisiert wurden, und bin auf dieses Juwel gestoßen. Das Folgende passiert für jeden Radix 24 oder höher.

parseInt(null, 24) === 23 // evaluates to true

Ich habe es in IE, Chrome und Firefox getestet und alle alarmieren true, daher denke ich, dass es irgendwo in der Spezifikation enthalten sein muss. Eine schnelle Google-Suche hat mir keine Ergebnisse gebracht, also bin ich hier und hoffe, dass jemand es erklären kann.

Ich erinnere mich an eine Crockford-Rede, in der er typeof null === "object"wegen eines Versehens sagte, dass Object und Null eine nahezu identische Typkennung im Speicher haben oder so ähnlich, aber ich kann das Video jetzt nicht finden.

Probieren Sie es aus: http://jsfiddle.net/robert/txjwP/

Korrektur bearbeiten : Ein höherer Radix gibt unterschiedliche Ergebnisse zurück, 32 gibt 785077 zurück.
Bearbeiten 2 Von zzzzBov:[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745


tl; dr

Erklären Sie, warum dies parseInt(null, 24) === 23eine wahre Aussage ist.

Robert
quelle
49
Wie schrullig. JavaScript hält Sie immer auf Trab.
FishBasketGordo
1
Datenpunkt: alert(parseInt(null, 34) === 23)produziertfalse
Stephen P
1
alert(parseInt(null,26)===23);produziert auch wahr?!?!
Petar Ivanov
6
[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745,[37...]:NaN
zzzzBov
1
Als zusätzliche Anmerkung, undefinedda der erste Parameter ungerade Ergebnisse für die
30er Jahre zurückgibt

Antworten:

240

Es wird nullin den String "null"konvertiert und versucht, ihn zu konvertieren. Für die Radixe 0 bis 23 gibt es keine Ziffern, die konvertiert werden können, daher wird zurückgegeben NaN. Bei 24 wird "n"der 14. Buchstabe zum Zahlensystem hinzugefügt. Bei 31 wird "u"der 21. Buchstabe hinzugefügt und die gesamte Zeichenfolge kann dekodiert werden. Ab 37 kann kein gültiger Zahlensatz mehr generiert werden und NaN wird zurückgegeben.

js> parseInt(null, 36)
1112745

>>> reduce(lambda x, y: x * 36 + y, [(string.digits + string.lowercase).index(x) for x in 'null'])
1112745
Ignacio Vazquez-Abrams
quelle
3
@Tomalak: Wer sagt, dass es verwendet toString()?
Ignacio Vazquez-Abrams
3
@ Ignacio. Eigentlich habe ich mich geirrt. Ich wusste nicht, dass 37 sich auf einen Radix bezog. Das tut mir leid.
Mike Samuel
3
@ Robert, nein, ich war verwirrt und dachte, er würde etwas anderes behaupten als das, was er behauptete. Es ist die richtige Antwort. Entschuldigung überall.
Mike Samuel
19
Ich denke immer noch, dass diese Antwort einige Referenzen vertragen könnte. Obwohl es völlig richtig ist, ist es wirklich nur eine große Behauptung ...
Leichtigkeitsrennen im Orbit
4
@Tomalak - überprüfe meine Antwort für alle Referenzen. Diese Antwort ist die richtige (und erste), daher denke ich, dass sie die akzeptierte bleiben sollte. Obwohl es nie weh tut zu erklären, was unter der Haube vor sich geht;)
David Titarenco
118

Mozilla sagt uns :

Die Funktion parseInt konvertiert ihr erstes Argument in eine Zeichenfolge , analysiert es und gibt eine Ganzzahl oder NaN zurück. Wenn nicht NaN, ist der zurückgegebene Wert die dezimale Ganzzahldarstellung des ersten Arguments, das als Zahl im angegebenen Radix (Basis) verwendet wird. Ein Radix von 10 gibt beispielsweise an, dass eine Dezimalzahl von 8 Oktal, 16 Hexadezimal usw. konvertiert werden soll. Bei Radices über 10 geben die Buchstaben des Alphabets Ziffern größer als 9 an. Bei Hexadezimalzahlen (Basis 16) werden beispielsweise A bis F verwendet.

In der Spezifikation sagt uns 15.1.2.2/1, dass die Konvertierung in einen String mit dem eingebauten System durchgeführt wird ToString, das (gemäß 9.8) ergibt "null"(nicht zu verwechseln mit dem toString, was ergeben würde "[object Window]"!).

Also, lasst uns überlegen parseInt("null", 24).

Natürlich ist dies keine vollständige numerische Zeichenfolge der Basis 24, aber "n" ist: Es ist eine Dezimalzahl 23 .

Jetzt wird die Analyse beendet, nachdem die Dezimalstelle 23 herausgezogen wurde, da sie im Basis-24-System "u" nicht gefunden wird:

Wenn S ein Zeichen enthält, das keine Radix-R-Ziffer ist, dann sei Z die Teilzeichenfolge von S, die aus allen Zeichen vor dem ersten solchen Zeichen besteht. andernfalls sei Z S. [15.1.2.2/11]

(Und aus diesem Grund parseInt(null, 23)(und mit niedrigeren Radices) erhalten Sie NaNeher als 23: "n"befindet sich nicht im Basis-23-System.)

Leichtigkeitsrennen im Orbit
quelle
2
Dies ist ein sehr tragisches Verhalten von parseInt (ich dachte mir, warum es in diesem Fall keine Ausnahme war). Ich würde lieber NUMBER () verwenden, wenn ich kann.
Grijesh Chauhan
79

Ignacio Vazquez-Abrams ist richtig, aber lassen Sie uns genau sehen, wie es funktioniert ...

Von 15.1.2.2 parseInt (string , radix):

Wenn die Funktion parseInt aufgerufen wird, werden die folgenden Schritte ausgeführt:

  • Sei inputString ToString (string).
  • Sei S eine neu erstellte Teilzeichenfolge von inputString, die aus dem ersten Zeichen, das kein StrWhiteSpaceChar ist, und allen Zeichen besteht, die diesem Zeichen folgen. (Mit anderen Worten, entfernen Sie den führenden Leerraum.)
  • Das Zeichen sei 1.
  • Wenn S nicht leer ist und das erste Zeichen von S ein Minuszeichen - ist, sei das Vorzeichen −1.
  • Wenn S nicht leer ist und das erste Zeichen von S ein Pluszeichen + oder ein Minuszeichen - ist, entfernen Sie das erste Zeichen aus S.
  • Sei R = ToInt32 (Radix).
  • Lassen Sie stripPrefix wahr sein.
  • Wenn R ≠ 0 ist, dann a. Wenn R 2 oder R 36, dann NaN zurückgeben. b. Wenn R ≠ 16 ist, sei stripPrefix falsch.
  • Sonst ist R = 0 a. Sei R = 10.
  • Wenn stripPrefix wahr ist, dann a. Wenn die Länge von S mindestens 2 beträgt und die ersten beiden Zeichen von S entweder "0x" oder "0X" sind, entfernen Sie die ersten beiden Zeichen aus S und lassen Sie R = 16.
  • Wenn S ein Zeichen enthält, das keine Radix-R-Ziffer ist, dann sei Z die Teilzeichenfolge von S, die aus allen Zeichen vor dem ersten solchen Zeichen besteht. andernfalls sei Z S.
  • Wenn Z leer ist, geben Sie NaN zurück.
  • Sei mathInt der mathematische ganzzahlige Wert, der durch Z in Radix-R-Notation dargestellt wird, wobei die Buchstaben AZ und az für Ziffern mit den Werten 10 bis 35 verwendet werden. (Wenn R jedoch 10 ist und Z mehr als 20 signifikante Ziffern enthält, jede signifikante Eine Ziffer nach der 20. kann nach Wahl der Implementierung durch eine 0-Ziffer ersetzt werden. Wenn R nicht 2, 4, 8, 10, 16 oder 32 ist, kann mathInt eine implementierungsabhängige Annäherung an die mathematische Ganzzahl sein Wert, der durch Z in Radix-R-Notation dargestellt wird.)
  • Die Zahl sei der Zahlenwert für mathInt.
  • Rückgabezeichen × Nummer.

HINWEIS parseInt interpretiert möglicherweise nur einen führenden Teil der Zeichenfolge als ganzzahligen Wert. Zeichen, die nicht als Teil der Notation einer Ganzzahl interpretiert werden können, werden ignoriert, und es wird kein Hinweis darauf gegeben, dass solche Zeichen ignoriert wurden.

Hier gibt es zwei wichtige Teile. Ich habe beide gewagt. Also müssen wir zuerst herausfinden, was die toStringDarstellung von nullist. Wir müssen Table 13 — ToString Conversionsin Abschnitt 9.8.0 nach diesen Informationen suchen :

Geben Sie hier die Bildbeschreibung ein

Großartig, jetzt wissen wir also, dass toString(null)internes Handeln eine 'null'Zeichenfolge ergibt . Großartig, aber wie genau werden Ziffern (Zeichen) behandelt, die innerhalb des angegebenen Radix nicht gültig sind?

Wir schauen nach oben 15.1.2.2und sehen die folgende Bemerkung:

Wenn S ein Zeichen enthält, das keine Radix-R-Ziffer ist, dann sei Z die Teilzeichenfolge von S, die aus allen Zeichen vor dem ersten solchen Zeichen besteht. andernfalls sei Z S.

Das bedeutet, dass wir alle Ziffern VOR dem angegebenen Radix behandeln und alles andere ignorieren.

Im Grunde ist das Tun parseInt(null, 23)dasselbe wie parseInt('null', 23). Dies ubewirkt, dass die beiden lignoriert werden (obwohl sie Teil des Radix 23 sind). Daher können wir nur analysieren n, was die gesamte Aussage synonym macht parseInt('n', 23). :) :)

Wie auch immer, tolle Frage!

David Titarenco
quelle
33
parseInt( null, 24 ) === 23

Ist äquivalent zu

parseInt( String(null), 24 ) === 23

das ist äquivalent zu

parseInt( "null", 24 ) === 23

Die Ziffern für die Basis 24 sind 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, ..., n.

Die Sprachspezifikation sagt

  1. Wenn S ein Zeichen enthält, das keine Radix-R-Ziffer ist, dann sei Z die Teilzeichenfolge von S, die aus allen Zeichen vor dem ersten solchen Zeichen besteht. andernfalls sei Z S.

Dies ist der Teil, der sicherstellt, dass Ganzzahlliterale im C-Stil wie 15Lrichtig analysiert werden

parseInt( "n", 24 ) === 23

"n" ist der 23. Buchstabe der obigen Ziffernliste.

QED

Mike Samuel
quelle
16

Ich denke, nullwird in eine Zeichenfolge konvertiert "null". Ist also ntatsächlich 23in 'base24' (dasselbe in 'base25' +), uist in 'base24' ungültig, so dass der Rest der Zeichenfolge nullignoriert wird. Aus diesem Grund wird ausgegeben, 23bis ues in 'base31' gültig wird.

Floern
quelle
7

parseInt verwendet eine alphanumerische Darstellung, dann ist in Basis-24 "n" gültig, aber "u" ist ein ungültiges Zeichen, dann analysiert parseInt nur den Wert "n" ....

parseInt("n",24) -> 23

Versuchen Sie als Beispiel Folgendes:

alert(parseInt("3x", 24))

Das Ergebnis ist "3".

fdaines
quelle