Como trabalho em torno do comportamento ocular analítico do JavaScript?

282

Tente executar o seguinte em 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 !!

Acabei de aprender da maneira mais difícil que o JavaScript pensa que o zero inicial indica um número inteiro octal e, como não existe "8"ou "9"na base 8, a função retorna zero. Goste ou não, isso ocorre por design .

Quais são as soluções alternativas?

Nota: Por uma questão de integridade, estou prestes a postar uma solução, mas é uma solução que eu odeio, portanto, publique outras / melhores respostas.


Atualizar:

A 5ª edição do padrão JavaScript ( ECMA-262 ) apresenta uma alteração de última hora que elimina esse comportamento. Mozilla tem uma boa redação .

Portman
fonte
21
Etapa 1) Faça um favor a si mesmo e sempre inclua a raiz conforme mencionado nas respostas anteriores, bem como no livro de Doug. Etapa 2) Se você está interessado em aprender JavaScript, obtenha uma cópia do livro de Doug. É inestimável. Meu livro favorito até agora. Aqui está uma revisão fyi: realtech.burningbird.net/learning-javascript/basics/…
fuentesjr
8
Nos navegadores compatíveis com o ECMAScript 5th Edition, como o Internet Explorer 9, o parâmetro base é padronizado como 10(decimal), a menos que o número a ser analisado seja prefixado 0x, por exemplo 0xFF, nesse caso, o parâmetro base é padronizado como 16. Felizmente, um dia, esse problema será uma memória distante.
Andy E
2
Que tal apenas +'08' === 8? Verdade! Talvez você realmente precise parseIntdo seu código real, mas não do acima.
Kojiro
1
a) este não é um erro, corrigir o título b)Number('08')
OnTheFly
4
@portman: "a 5ª edição ... introduz uma mudança radical que elimina esse comportamento" Provavelmente vale ressaltar que, mesmo na 3ª edição (13 anos atrás), as implementações foram "encorajadas" a não fazê-lo: "Quando radix é 0ou undefinede o número da string começa com um 0dígito não seguido por um xou X, então a implementação pode, a seu critério, interpretar o número como octal ou decimal. As implementações são incentivadas a interpretar números nesse caso como decimais. " ( minha ênfase)
TJ Crowder

Respostas:

328

Esta é uma pegadinha Javascript comum com uma solução simples:

Basta especificar a base , ou 'radix', da seguinte forma:

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

Você também pode usar o Number :

Number('08'); // 8
Paolo Bergantino
fonte
57
Número . Glorioso.
Portman
10
O número precisa de aspas em torno do 08. Também basta ser avisado, Number ('08 .123 ') produzirá 8.123 como sua saída. Se você realmente deseja um número inteiro, não use Number (ou faça a correspondência padrão de sua entrada para garantir apenas números inteiros).
11139 Jason S
3
Número (08); me dá 8 no Firefox e IE.
1211 Paolo Bergantino
3
não faz parte do padrão ECMAscript. Estou testando em JSDB que usa Aranha 1,7 (= motor Firefox JS), e queixa-se "08 não é um ECMA-262 constante octal legal"
Jason S
5
Ainda assim, use '08' entre aspas. 08 não atende ao padrão ECMA-262 e não tem garantia de êxito sem avisos e / ou erros e / ou um comportamento específico especificado.
11339 Jason S
43

Se você souber que seu valor estará no intervalo inteiro assinado de 32 bits, ~~xfará a coisa correta em todos os cenários.

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

Se você procurar o binário not ( ~), a especificação exigirá uma conversão "ToInt32" para o argumento que faz a conversão óbvia para um Int32 e é especificado para coagir NaNvalores a zero.

Sim, isso é incrivelmente brincalhão, mas é tão conveniente ...

Karl Guertin
fonte
2
Por que duas operações quando uma binária ou seria suficiente?
Oleg V. Volkov
@ Oleg, por que basta?
21313 Sebastian
3
@SebastianGodelet, | 0.
Oleg V. Volkov
@Grodriguez, e quando 0 em | 0 é uma string?
Oleg V. Volkov
Esta questão toda é sobre alternativas ao parseInt para converter seqüências de caracteres em números inteiros. Assim: sempre.
Grodriguez 25/10
24

Na documentação parseInt , use o argumento radix opcional para especificar a base-10:

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

Isso me parece pedante, confuso e detalhado (realmente, um argumento extra em cada análise)? Por isso, espero que exista um caminho melhor.

Portman
fonte
6
se você não gosta da verbosidade, basta criar sua própria função que chama a função interna e preenche os argumentos que você sempre mantém constantes.
11339 Jason S
3
É claro, porque não é como se todas as pessoas no StackOverflow o repreendessem por escrever a função parseIntB10. Escrever sua própria função de wrapper é uma péssima idéia para esse propósito.
237 Stefan Kendall
2
@ iftrue: Eu acho que você perdeu o meu ponto. Pessoalmente, não me importo de fazer a análise (someString, 10) em todos os lugares para garantir que eu force a base 10. O OP parece não gostar dessa abordagem, então sugeri uma alternativa, que eu não usaria pessoalmente, mas talvez ela atenda às suas necessidades. necessidades. (isto é, aparentemente, o pensamento por trás JQuery: torná-lo conveniente adicionando complexidade extra Não uso JQuery, mas muitas pessoas acham que é útil..)
Jason S
4
Na verdade, é realmente importante agrupar parseIntpara uso conveniente em expressões funcionais, como mapem ["7","4","09","5"].map(parseInt); Mappassará o índice do elemento na matriz como o segundo argumento, que será interpretado parseIntcomo a base, a menos que você o agrupe.
Plynx
11
function parseDecimal(s) { return parseInt(s, 10); }

edit: criar sua própria função, para fazer o que você realmente deseja, é apenas uma opção se você não gosta de adicionar o ", 10" o tempo todo à chamada parseInt (). Tem a desvantagem de ser uma função fora do padrão: mais conveniente para você, se você a usa muito, mas talvez mais confusa para os outros.

Jason S
fonte
10

Especifique a base:

var number = parseInt(s, 10);
RichieHindle
fonte
Uau, vocês são rápidos. Eu até tive minha resposta na área de transferência. Você está conectado diretamente à Internet, no estilo neo?
Portman
1
Por que o diabo não está na base 10
Tom Gullen
2
Talvez porque não seja principalmente uma função de conversão String-> Number, mas uma função que lê um Número de uma String de número b base.
Artistoex
2
o padrão é a base 10, mas os zeros à esquerda são uma maneira difundida de indicar octal.
Nathan
7

Seria muito impertinente substituir parseInt por uma versão que assume decimal se não tiver um segundo parâmetro? (nota - não testado)

parseIntImpl = parseInt
parseInt = function(str, base){return parseIntImpl(str, base ? base : 10)}
Andrew Duffy
fonte
2
Sim, isso seria desobediente - quebraria outro código que se baseava no comportamento padrão.
jes5199
4
Isso é verdade, mas não há muito disso. Também não é um comportamento padrão - o suporte octal é opcional.
Andrew Duffy
2
Mas você também está descartando o suporte hexadecimal que não é opcional, é? Este deve sempre ser verdadeiro: parseInt("0xFFFFFF") === 16777215, mas com o seu corte impertinente no lugar ele não funciona maisparseInt("0xFFFFFF") === 0
Timo
6

Você também pode, em vez de usar parseFloat ou parseInt, usar o operador unário ( + ).

+"01"
// => 1

+"02"
// => 2

+"03"
// => 3

+"04"
// => 4

+"05"
// => 5

+"06"
// => 6

+"07"
// => 7

+"08"
// => 8

+"09"
// => 9

e para uma boa medida

+"09.09"
// => 9.09

Link MDN

O operador unary plus precede seu operando e avalia seu operando, mas tenta convertê-lo em um número, se já não estiver. Embora a negação unária (-) também possa converter não-números, o unary plus é a maneira mais rápida e preferida de converter algo em um número , porque não realiza nenhuma outra operação no número.

SoEzPz
fonte
5

Que tal isso para decimal:

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

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

Se você já fez um monte de codificação com parseInt e não deseja adicionar ", 10" a tudo, basta substituir a função para tornar a base 10 o padrão:

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

Isso pode confundir um leitor posterior, portanto, criar uma função parseInt10 () pode ser mais auto-explicativa. Pessoalmente, prefiro usar uma função simples do que adicionar ", 10" o tempo todo - apenas cria mais oportunidades para erros.

ingrediente_15939
fonte
0

Esse problema não pode ser replicado no Chrome nem no Firefox (2019) mais recentes.

Andrija
fonte