parseInt (null, 24) === 23… attendez quoi?

226

Très bien, donc je plaisantais avec parseInt pour voir comment il gère les valeurs non encore initialisées et je suis tombé sur ce joyau. Ce qui suit se produit pour tout radix 24 ou supérieur.

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

Je l'ai testé dans IE, Chrome et Firefox et ils alertent tous vrai, donc je pense que cela doit être quelque part dans les spécifications. Une recherche rapide sur Google ne m'a donné aucun résultat alors me voici, en espérant que quelqu'un puisse m'expliquer.

Je me souviens avoir écouté un discours de Crockford où il disait à typeof null === "object"cause d'une erreur qui faisait que Object et Null avaient un identifiant de type presque identique en mémoire ou quelque chose du genre, mais je ne trouve pas cette vidéo maintenant.

Essayez-le: http://jsfiddle.net/robert/txjwP/

Correction d' édition : un radix plus élevé renvoie des résultats différents, 32 renvoie 785077
Edit 2 De zzzzBov:[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745


tl; dr

Expliquez pourquoi parseInt(null, 24) === 23est une vraie déclaration.

Robert
la source
49
Comme c'est excentrique. JavaScript vous tient toujours sur vos gardes.
FishBasketGordo
1
point de données: alert(parseInt(null, 34) === 23)produitfalse
Stephen P
1
alert(parseInt(null,26)===23);produit également vrai?!?!
Petar Ivanov
6
[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745,[37...]:NaN
zzzzBov
1
Comme note supplémentaire, undefinedcar le premier paramètre renvoie des résultats étranges pour les années 30
zzzzBov

Réponses:

240

Il s'agit de convertir nullen chaîne "null"et d'essayer de le convertir. Pour les radix 0 à 23, il n'y a aucun chiffre qu'il peut convertir, il revient donc NaN. À 24 ans, "n"la 14e lettre est ajoutée au système numérique. Au 31, "u"la 21e lettre est ajoutée et la chaîne entière peut être décodée. À 37, il n'y a plus de jeu de chiffres valide pouvant être généré et NaN est retourné.

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
la source
3
@Tomalak: Qui a dit qu'il utilisait toString()?
Ignacio Vazquez-Abrams
3
@Ignacio. En fait, j'avais tort. Je ne savais pas que 37 faisait référence à un radix. Désolé pour ça.
Mike Samuel
3
@Robert, non, j'étais confus et je pensais qu'il réclamait autre chose que ce qu'il réclamait. C'est la bonne réponse. Toutes mes excuses.
Mike Samuel
19
Je pense toujours que cette réponse pourrait faire avec quelques références. Bien que ce soit tout à fait correct, ce n'est vraiment qu'une grande affirmation ...
Courses de légèreté en orbite
4
@Tomalak - vérifiez ma réponse pour toutes les références. Cette réponse est la bonne (et la première), donc je pense qu'elle devrait rester celle qui est acceptée. Bien que cela ne fasse jamais de mal d'expliquer ce qui se passe sous le capot;)
David Titarenco
118

Mozilla nous dit :

La fonction parseInt convertit son premier argument en chaîne , l'analyse et renvoie un entier ou NaN. Si ce n'est pas NaN, la valeur renvoyée sera la représentation entière décimale du premier argument pris comme un nombre dans le radix spécifié (base). Par exemple, un radix de 10 indique de convertir à partir d'un nombre décimal, 8 octal, 16 hexadécimal, etc. Pour les radices supérieurs à 10, les lettres de l'alphabet indiquent des chiffres supérieurs à 9. Par exemple, pour les nombres hexadécimaux (base 16), A à F sont utilisés.

Dans la spécification , 15.1.2.2/1 nous dit que la conversion en chaîne est effectuée en utilisant le intégré ToString, qui (selon 9.8) donne "null"(à ne pas confondre avec toString, ce qui donnerait "[object Window]"!).

Alors, réfléchissons parseInt("null", 24).

Bien sûr, ce n'est pas une chaîne numérique de base 24 dans son intégralité, mais "n" est: c'est décimal 23 .

Maintenant, l'analyse s'arrête une fois la décimale 23 retirée, car elle "u" n'est pas trouvée dans le système en base 24:

Si S contient un caractère qui n'est pas un chiffre radix-R, alors soit Z la sous-chaîne de S composée de tous les caractères avant le premier de ces caractères; sinon, que Z soit S. [15.1.2.2/11]

(Et c'est pourquoi parseInt(null, 23)(et les radices inférieurs) vous donne NaNplutôt que 23: "n"n'est pas dans le système base-23.)

Courses de légèreté en orbite
la source
2
Il s'agit d'un comportement très tragique de parseInt (je me demandais pourquoi il n'était pas conçu pour faire exception à la règle dans ces cas). Je préfère utiliser NUMBER () à la place quand je le peux.
Grijesh Chauhan
79

Ignacio Vazquez-Abrams est correct, mais permet de voir exactement comment cela fonctionne ...

De 15.1.2.2 parseInt (string , radix):

Lorsque la fonction parseInt est appelée, les étapes suivantes sont effectuées:

  • Laissez inputString être ToString (chaîne).
  • Soit S une sous-chaîne nouvellement créée de inputString composée du premier caractère qui n'est pas un StrWhiteSpaceChar et de tous les caractères qui suivent ce caractère. (En d'autres termes, supprimez le premier espace blanc.)
  • Soit signe 1.
  • Si S n'est pas vide et que le premier caractère de S est un signe moins -, soit signe soit -1.
  • Si S n'est pas vide et que le premier caractère de S est un signe plus + ou un signe moins -, supprimez le premier caractère de S.
  • Soit R = ToInt32 (radix).
  • Que stripPrefix soit vrai.
  • Si R ≠ 0, alors a. Si R <2 ou R> 36, retournez NaN. b. Si R ≠ 16, que stripPrefix soit faux.
  • Sinon, R = 0 a. Soit R = 10.
  • Si stripPrefix est vrai, alors a. Si la longueur de S est d'au moins 2 et que les deux premiers caractères de S sont «0x» ou «0X», supprimez les deux premiers caractères de S et laissez R = 16.
  • Si S contient un caractère qui n'est pas un chiffre radix-R, alors soit Z la sous-chaîne de S composée de tous les caractères avant le premier de ces caractères; sinon, que Z soit S.
  • Si Z est vide, retournez NaN.
  • Soit mathInt la valeur entière mathématique représentée par Z dans la notation radix-R, en utilisant les lettres AZ et az pour les chiffres de 10 à 35. (Cependant, si R est 10 et Z contient plus de 20 chiffres significatifs, chaque significatif le chiffre après le 20 peut être remplacé par un chiffre 0, au choix de l'implémentation; et si R n'est pas 2, 4, 8, 10, 16 ou 32, alors mathInt peut être une approximation dépendante de l'implémentation de l'entier mathématique valeur représentée par Z en notation radix-R.)
  • Soit number la valeur Number pour mathInt.
  • Signe de retour × numéro.

REMARQUE parseInt peut interpréter uniquement une portion de début de chaîne comme une valeur entière; il ignore tous les caractères qui ne peuvent pas être interprétés comme faisant partie de la notation d'un entier, et aucune indication n'est donnée que ces caractères ont été ignorés.

Il y a deux parties importantes ici. Je les ai mis en gras tous les deux. Donc, tout d'abord, nous devons découvrir quelle est la toStringreprésentation de null. Nous devons regarder Table 13 — ToString Conversionsdans la section 9.8.0 pour cette information:

entrez la description de l'image ici

Génial, alors maintenant nous savons que faire en toString(null)interne produit une 'null'chaîne. Très bien, mais comment gère-t-il exactement les chiffres (caractères) qui ne sont pas valides dans le radix fourni?

Nous regardons ci-dessus 15.1.2.2et nous voyons la remarque suivante:

Si S contient un caractère qui n'est pas un chiffre radix-R, alors soit Z la sous-chaîne de S composée de tous les caractères avant le premier de ces caractères; sinon, que Z soit S.

Cela signifie que nous traitons tous les chiffres AVANT le radix spécifié et ignorons tout le reste.

Fondamentalement, faire parseInt(null, 23)est la même chose que parseInt('null', 23). Le ufait que les deux lsoient ignorés (même s'ils font partie de la radix 23). Par conséquent, nous ne pouvons analyser que n, ce qui rend la déclaration entière synonyme de parseInt('n', 23). :)

De toute façon, bonne question!

David Titarenco
la source
33
parseInt( null, 24 ) === 23

Est équivalent à

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

ce qui équivaut à

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

Les chiffres de la base 24 sont 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, ..., n.

La spécification de langue dit

  1. Si S contient un caractère qui n'est pas un chiffre radix-R, alors soit Z la sous-chaîne de S composée de tous les caractères avant le premier de ces caractères; sinon, que Z soit S.

qui est la partie qui garantit que les littéraux entiers de style C aiment 15Lbien analyser, donc ce qui précède est équivalent à

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

"n" est la 23 e lettre de la liste de chiffres ci-dessus.

QED

Mike Samuel
la source
16

Je suppose qu'il nullest converti en chaîne "null". Donc, nest en fait 23dans «base24» (même dans «base25» +), un'est pas valide dans «base24», donc le reste de la chaîne nullsera ignoré. C'est pourquoi il sort 23jusqu'à ce uqu'il devienne valide dans 'base31'.

Floern
la source
7

parseInt utilise une représentation alphanumérique, puis en base-24 "n" est valide, mais "u" est un caractère invalide, puis parseInt analyse uniquement la valeur "n" ....

parseInt("n",24) -> 23

à titre d'exemple, essayez avec ceci:

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

Le résultat sera "3".

fdaines
la source