web-dev-qa-db-fra.com

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

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 dans la spécification quelque part. Une recherche rapide sur Google ne m'a donné aucun résultat alors ici J'espère que quelqu'un pourra expliquer.

Je me souviens d'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/

Modifier Correction: un radix plus élevé renvoie des résultats différents, 32 renvoie 785077
Éditer 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) === 23 est une vraie instruction.

225
Robert

Il convertit null en chaîne "null" et en essayant de le convertir. Pour les radix 0 à 23, il n'y a aucun chiffre qu'il peut convertir, il renvoie donc NaN. À 24 ans, "n", la quatorzième 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
237

Mozilla nous dit :

la fonction parseInt convertit son premier argument en une chaîne , l’analyse et retourne 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 indique que la conversion en chaîne est effectuée en utilisant le ToString intégré, qui (selon 9.8) donne "null" (à ne pas confondre avec toString, ce qui donnerait "[object Window]"!).

Considérons donc 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 23 décimal.

Maintenant, l'analyse s'arrête une fois la décimale 23 retirée, car "u" n'est pas trouvé dans le système 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 NaN plutôt que 23: "n" N'est pas dans le système base-23.)

118

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:

  • Soit 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, soit Z soit S.
  • Si Z est vide, retournez NaN.
  • Soit mathInt la valeur mathématique entière 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 première partie 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 représentation toString de null. Nous devons regarder Table 13 — ToString Conversions Dans la section 9.8.0 pour ces informations:

enter image description here

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

Nous regardons ci-dessus 15.1.2.2 Et 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 u fait que les deux l sont ignorés (même s'ils font partie de la radix 23). Par conséquent, nous ne pouvons analyser que n, ce qui rend l'ensemble de l'instruction synonyme de parseInt('n', 23). :)

De toute façon, bonne question!

79
David Titarenco
parseInt( null, 24 ) === 23

Est équivalent à

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

ce qui équivaut à

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

Les chiffres pour 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 comme 15L analyser correctement, donc ce qui précède équivaut à

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

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

Q.E.D.

33
Mike Samuel

Je suppose que null est converti en chaîne "null". Donc n est en fait 23 dans 'base24' (idem dans 'base25' +), u n'est pas valide dans 'base24' donc le reste de la chaîne null sera ignoré. C'est pourquoi il génère 23 jusqu'à ce que u devienne valide dans 'base31'.

16
Floern

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".

7
fdaines