Comment contourner le comportement octal parseInt de JavaScript?

283

Essayez d'exécuter ce qui suit en JavaScript:

parseInt('01'); //equals 1
parseInt('02'); //equals 2
parseInt('03'); //equals 3
parseInt('04'); //equals 4
parseInt('05'); //equals 5
parseInt('06'); //equals 6
parseInt('07'); //equals 7
parseInt('08'); //equals 0 !!
parseInt('09'); //equals 0 !!

Je viens d'apprendre à la dure que JavaScript pense que le zéro de tête indique un entier octal , et puisqu'il n'y a pas "8"ou "9"en base-8, la fonction renvoie zéro. Qu'on le veuille ou non, c'est par conception .

Quelles sont les solutions de contournement?

Remarque: Par souci d'exhaustivité, je suis sur le point de publier une solution, mais c'est une solution que je déteste, alors veuillez publier d'autres / meilleures réponses.


Mettre à jour:

La 5e édition de la norme JavaScript ( ECMA-262 ) introduit un changement de rupture qui élimine ce comportement. Mozilla a une bonne rédaction .

Portman
la source
21
Étape 1) Rendez-vous service et incluez toujours le radix comme mentionné dans les réponses précédentes ainsi que dans le livre de Doug. Étape 2) Si vous voulez vraiment apprendre JavaScript, procurez-vous un exemplaire du livre de Doug. C'est inestimable. Mon livre préféré jusqu'à présent. Voici un examen fyi: realtech.burningbird.net/learning-javascript/basics/…
fuentesjr
8
Dans les navigateurs compatibles ECMAScript 5e édition, comme Internet Explorer 9, le paramètre de base est par défaut 10(décimal), sauf si le nombre à analyser est préfixé 0x, par exemple 0xFF, dans ce cas, le paramètre de base par défaut est 16. Espérons qu'un jour, ce problème sera un lointain souvenir.
Andy E
2
Et juste +'08' === 8? Vrai! Peut-être avez-vous vraiment besoin parseIntde votre vrai code, mais pas de ce qui précède.
kojiro
1
a) ce n'est pas un bug, corrige le titre b)Number('08')
OnTheFly
4
@portman: "la 5ème édition ... introduit un changement de rupture qui élimine ce comportement" Il vaut probablement la peine de souligner que même dans la 3ème édition (il y a 13 ans), les implémentations étaient "encouragées" à ne pas le faire: "Quand radix est 0ou undefinedet le numéro de la chaîne commence par un 0chiffre non suivi d'un xou X, alors l'implémentation peut, à sa discrétion, interpréter le nombre comme étant octal ou comme décimal. Les implémentations sont encouragées à interpréter les nombres dans ce cas comme étant décimaux. " ( mon accent)
TJ Crowder

Réponses:

329

Il s'agit d'un gotcha Javascript commun avec une solution simple:

Spécifiez simplement la base , ou «radix», comme ceci:

parseInt('08',10); // 8

Vous pouvez également utiliser Numéro :

Number('08'); // 8
Paolo Bergantino
la source
57
Numéro . Glorieux.
Portman
10
Nombre a besoin de guillemets autour du 08. Il suffit également d'être averti, Numéro ('08 .123 ') produira 8.123 comme sortie. Si vous voulez vraiment un entier, n'utilisez pas Number (ou ne faites pas correspondre le motif à votre entrée pour garantir uniquement les entiers).
Jason S
3
Numéro (08); me donne 8 dans Firefox et IE.
Paolo Bergantino
3
cela ne fait pas partie de la norme ECMAscript. Je teste JSDB qui utilise Spidermonkey 1.7 (= moteur Firefox JS), et se plaint que "08 n'est pas une constante octale ECMA-262 légale"
Jason S
5
Utilisez toujours '08' entre guillemets. 08 ne répond pas à la norme ECMA-262 et n'est pas garanti pour réussir sans avertissements et / ou erreurs et / ou un comportement spécifié particulier.
Jason S
43

Si vous savez que votre valeur sera dans la plage d'entiers 32 bits signée, alors ~~xfera la bonne chose dans tous les scénarios.

~~"08" === 8
~~"foobar" === 0
~~(1.99) === 1
~~(-1.99)  === -1

Si vous recherchez binaire not ( ~), la spécification nécessite une conversion "ToInt32" pour l'argument qui effectue la conversion évidente en Int32 et est spécifié pour contraindre les NaNvaleurs à zéro.

Oui, c'est incroyablement hackish mais c'est tellement pratique ...

Karl Guertin
la source
2
Pourquoi deux opérations quand un binaire ou suffirait?
Oleg V. Volkov
@Oleg, pourquoi suffirait-on?
Sebastian
3
@SebastianGodelet, | 0.
Oleg V. Volkov
@Grodriguez, et quand 0 dans | 0 est une chaîne?
Oleg V. Volkov
Toute cette question concerne les alternatives à parseInt pour convertir des chaînes en entiers. Ainsi: toujours.
Grodriguez
24

Dans la documentation de parseInt , utilisez l'argument radix facultatif pour spécifier la base-10:

parseInt('08', 10); //equals 8
parseInt('09', 10); //equals 9

Cela me semble pédant, déroutant et verbeux (vraiment, un argument supplémentaire dans chaque analyse individuelle?), Donc j'espère qu'il y a une meilleure façon.

Portman
la source
6
si vous n'aimez pas la verbosité, créez simplement votre propre fonction qui appelle la fonction intégrée et remplit les arguments que vous gardez toujours constants.
Jason S
3
Riiiiiiight, parce que ce n'est pas comme si chaque personne sur StackOverflow vous reprocherait d'écrire la fonction parseIntB10. L'écriture de votre propre fonction wrapper est une idée terrible à cet effet.
Stefan Kendall
2
@iftrue: Je pense que vous avez manqué mon point. Personnellement, cela ne me dérange pas de faire une analyse syntaxique (someString, 10) partout pour m'assurer de forcer la base 10. L'OP ne semble pas aimer cette approche, j'ai donc suggéré une alternative, que je n'utiliserais pas personnellement, mais peut-être qu'elle correspond à la sienne Besoins. (c'est apparemment la pensée derrière JQuery: le rendre pratique en ajoutant une complexité supplémentaire. Je n'utilise pas JQuery mais beaucoup de gens le trouvent utile.)
Jason S
4
En fait, il est vraiment important d'encapsuler parseIntpour une utilisation pratique dans les expressions fonctionnelles, comme mapdans ["7","4","09","5"].map(parseInt); Mappassera l' index de l'élément dans le tableau comme deuxième argument, qui sera interprété parseIntcomme la base à moins que vous ne l'utilisiez.
Plynx
11
function parseDecimal(s) { return parseInt(s, 10); }

edit: créer votre propre fonction, pour faire ce que vous voulez vraiment, n'est qu'une option si vous n'aimez pas ajouter tout le temps ", 10" à l'appel parseInt (). Il a l'inconvénient d'être une fonction non standard: plus pratique pour vous si vous l'utilisez beaucoup, mais peut-être plus déroutant pour les autres.

Jason S
la source
10

Spécifiez la base:

var number = parseInt(s, 10);
RichieHindle
la source
Wow vous les gars êtes rapides. J'ai même eu ma réponse dans le presse-papiers. Êtes-vous branché directement sur Internet, style néo?
Portman
1
Pourquoi le diable n'est-il pas par défaut à la base 10
Tom Gullen
2
Peut-être parce qu'elle n'est pas principalement conçue comme une fonction de conversion String-> Number, mais comme une fonction qui lit un Number à partir d'une chaîne de nombres b de base.
artistoex
2
il est par défaut à la base 10 mais les zéros de tête sont un moyen répandu d'indiquer octal.
Nathan
7

Serait-il très vilain de remplacer parseInt par une version qui prend la décimale si elle n'a pas de second paramètre? (note - non testé)

parseIntImpl = parseInt
parseInt = function(str, base){return parseIntImpl(str, base ? base : 10)}
Andrew Duffy
la source
2
oui, ce serait vilain - cela briserait un autre code qui reposait sur le comportement standard.
jes5199
4
C'est vrai, mais il n'y en a pas beaucoup. Ce n'est pas non plus un comportement standard - le support octal est facultatif.
Andrew Duffy
2
Mais vous supprimez également le support hexadécimal qui n'est pas facultatif, n'est-ce pas? Cela devrait toujours être vrai parseInt("0xFFFFFF") === 16777215:, mais avec votre hack méchant en place, cela ne fonctionne plusparseInt("0xFFFFFF") === 0
Timo
6

Vous pouvez également, au lieu d'utiliser parseFloat ou parseInt, utiliser l'opérateur unaire ( + ).

+"01"
// => 1

+"02"
// => 2

+"03"
// => 3

+"04"
// => 4

+"05"
// => 5

+"06"
// => 6

+"07"
// => 7

+"08"
// => 8

+"09"
// => 9

et pour faire bonne mesure

+"09.09"
// => 9.09

Lien MDN

L'opérateur unaire plus précède son opérande et évalue son opérande mais tente de le convertir en nombre, s'il ne l'est pas déjà. Bien que la négation unaire (-) puisse également convertir des non-nombres, unaire plus est le moyen le plus rapide et préféré de convertir quelque chose en nombre , car il n'effectue aucune autre opération sur le nombre.

SoEzPz
la source
5

Que diriez-vous pour décimal:

('09'-0) === 9  // true

('009'-0) === 9 // true
Gordon K
la source
4

Si vous avez déjà fait beaucoup de codage avec parseInt et que vous ne voulez pas ajouter ", 10" à tout, vous pouvez simplement remplacer la fonction pour faire de la base 10 la valeur par défaut:

window._oldParseInt = window.parseInt;
window.parseInt = function(str, rad) {
    if (! rad) {
        return _oldParseInt(str, 10);
    }
    return _oldParseInt(str, rad);
};

Cela peut dérouter un lecteur ultérieur, donc faire une fonction parseInt10 () pourrait être plus explicite. Personnellement, je préfère utiliser une fonction simple que d'avoir à ajouter ", 10" tout le temps - crée simplement plus de possibilités d'erreurs.

ingrédient_15939
la source
0

Ce problème ne peut pas être répliqué dans les derniers Chrome ni Firefox (2019).

Andrija
la source