parseInt (nulo, 24) === 23 ... espera, o que?

226

Tudo bem, então eu estava brincando com o parseInt para ver como ele lida com valores ainda não inicializados e me deparei com essa gema. O seguinte acontece para qualquer raiz 24 ou superior.

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

Eu testei no IE, Chrome e Firefox e todos eles alertam como verdadeiros, então acho que deve estar na especificação em algum lugar. Uma rápida pesquisa no Google não me deu nenhum resultado, então aqui estou, esperando que alguém possa explicar.

Lembro-me de ouvir um discurso de Crockford em que ele estava dizendo typeof null === "object"por causa de uma supervisão que fazia com que Object e Null tivessem um identificador de tipo quase idêntico na memória ou algo nesse sentido, mas não consigo encontrar esse vídeo agora.

Experimente: http://jsfiddle.net/robert/txjwP/

Correção de edição : um radical mais alto retorna resultados diferentes, 32 retorna 785077
Edit 2 From zzzzBov:[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745


tl; dr

Explique por que parseInt(null, 24) === 23é uma afirmação verdadeira.

Robert
fonte
49
Que peculiar. O JavaScript sempre o mantém alerta.
FishBasketGordo
1
ponto de dados: alert(parseInt(null, 34) === 23)produzidofalse
Stephen P
1
alert(parseInt(null,26)===23);também produz true?!?!
Petar Ivanov
6
[24...30]:23, 31:714695, 32:785077, 33:859935, 34:939407, 35:1023631, 36:1112745,[37...]:NaN
zzzzBov
1
Como uma nota adicional, undefinedcomo o primeiro parâmetro retorna resultados estranhos para os anos 30
zzzzBov

Respostas:

240

Está convertendo nullpara a string "null"e tentando convertê-la. Para radixes de 0 a 23, não há números que possam ser convertidos e, portanto, ele retorna NaN. Aos 24, "n"a 14ª letra é adicionada ao sistema numérico. Em 31, "u"a 21ª letra é adicionada e a cadeia inteira pode ser decodificada. Aos 37 anos, não há mais nenhum conjunto de números válido que possa ser gerado e o NaN é retornado.

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
fonte
3
@Tomalak: Quem disse que está usando toString()?
Ignacio Vazquez-Abrams
3
@Ignacio. Na verdade, eu estava errado. Eu não sabia que 37 estava se referindo a um radical. Me desculpe por isso.
Mike Samuel
3
@ Robert, não, eu estava confuso e pensei que ele estava reivindicando algo diferente do que ele estava reivindicando. É a resposta certa. Desculpas por toda parte.
Mike Samuel
19
Ainda acho que essa resposta poderia ter algumas referências. Embora seja inteiramente correto, é realmente apenas uma grande afirmação ...
Leveza raças em órbita
4
@Tomalak - verifique minha resposta para todas as referências. Esta resposta é a correta (e a primeira), então acho que deve continuar sendo a aceita. Embora nunca é demais para explicar o que se passa sob o capô;)
David Titarenco
118

Mozilla nos diz :

A função parseInt converte seu primeiro argumento em uma string , analisa-o e retorna um número inteiro ou NaN. Se não NaN, o valor retornado será a representação inteira decimal do primeiro argumento obtido como um número no radical especificado (base). Por exemplo, uma raiz de 10 indica para converter de um número decimal, 8 octal, 16 hexadecimal e assim por diante. Para radias acima de 10, as letras do alfabeto indicam números maiores que 9. Por exemplo, para números hexadecimais (base 16), de A a F são usados.

Na especificação , 15.1.2.2/1 nos diz que a conversão em string é realizada usando o built-in ToString, que (conforme 9.8) produz "null"(não deve ser confundido com o toStringque produziria "[object Window]"!).

Então, vamos considerar parseInt("null", 24).

Obviamente, essa não é uma string numérica de base 24 por inteiro, mas "n" é: é 23 decimal .

Agora, a análise é interrompida depois que o decimal 23 é retirado, porque "u" não é encontrado no sistema base-24:

Se S contiver algum caractere que não seja um dígito radix-R, seja Z a substring de S que consiste em todos os caracteres antes do primeiro caractere; caso contrário, seja Z S. [15.1.2.2/11]

(E é por isso que parseInt(null, 23)(e radices mais baixos) fornecem a você, em NaNvez de 23: "n"não está no sistema base-23.)

Raças de leveza em órbita
fonte
2
Esse é um comportamento muito trágico do parseInt (eu estava pensando por que ele não foi projetado para ser exceção nesse caso). Eu preferiria usar NUMBER () quando possível.
Grijesh Chauhan 22/03
79

Ignacio Vazquez-Abrams está correto, mas vamos ver exatamente como ele funciona ...

De 15.1.2.2 parseInt (string , radix):

Quando a função parseInt é chamada, são executadas as seguintes etapas:

  • Deixe inputString ser ToString (string).
  • Seja S uma substring recém-criada de inputString que consiste no primeiro caractere que não é um StrWhiteSpaceChar e em todos os caracteres após esse caractere. (Em outras palavras, remova o espaço em branco à esquerda.)
  • Let sign seja 1.
  • Se S não estiver vazio e o primeiro caractere de S for um sinal de menos -, deixe o sinal ser -1.
  • Se S não estiver vazio e o primeiro caractere de S for um sinal de mais + ou um sinal de menos -, remova o primeiro caractere de S.
  • Seja R = ToInt32 (raiz).
  • Seja stripPrefix verdadeiro.
  • Se R ≠ 0, então a. Se R <2 ou R> 36, retorne NaN. b. Se R 16, deixe stripPrefix ser falso.
  • Senão, R = 0 a. Seja R = 10.
  • Se stripPrefix for true, então a. Se o comprimento de S for pelo menos 2 e os dois primeiros caracteres de S forem "0x" ou "0X", remova os dois primeiros caracteres de S e deixe R = 16.
  • Se S contiver algum caractere que não seja um dígito radix-R, seja Z a substring de S que consiste em todos os caracteres antes do primeiro caractere; caso contrário, seja Z S.
  • Se Z estiver vazio, retorne NaN.
  • Seja mathInt o valor inteiro matemático que é representado por Z na notação radix-R, usando as letras AZ e az para dígitos com valores de 10 a 35. (No entanto, se R for 10 e Z contiver mais de 20 dígitos significativos, todos os significantes dígito após o dia 20 pode ser substituído por um dígito 0, na opção da implementação; e se R não for 2, 4, 8, 10, 16 ou 32, então mathInt pode ser uma aproximação dependente da implementação do número inteiro matemático valor que é representado por Z na notação radix-R.)
  • Seja number o valor numérico para mathInt.
  • Sinal de retorno × número.

NOTA parseInt pode interpretar apenas uma parte inicial da string como um valor inteiro; ele ignora qualquer caractere que não possa ser interpretado como parte da notação de um número inteiro e nenhuma indicação é dada de que esses caracteres foram ignorados.

Existem duas partes importantes aqui. Eu coloquei os dois em negrito. Então, primeiro de tudo, temos que descobrir qual é a toStringrepresentação de null. Precisamos examinar Table 13 — ToString Conversionsna seção 9.8.0 essas informações:

insira a descrição da imagem aqui

Ótimo, agora sabemos que fazer toString(null)internamente produz uma 'null'string. Ótimo, mas como exatamente ele lida com dígitos (caracteres) que não são válidos dentro da raiz fornecida?

Observamos acima 15.1.2.2e vemos a seguinte observação:

Se S contiver algum caractere que não seja um dígito radix-R, seja Z a substring de S que consiste em todos os caracteres antes do primeiro caractere; caso contrário, seja Z S.

Isso significa que lidamos com todos os dígitos ANTES do radical especificado e ignoramos todo o resto.

Basicamente, fazer parseInt(null, 23)é a mesma coisa que parseInt('null', 23). Isso ufaz com que os dois lsejam ignorados (mesmo que sejam parte da base 23). Portanto, só podemos analisar n, tornando a declaração inteira sinônimo de parseInt('n', 23). :)

De qualquer maneira, ótima pergunta!

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

É equivalente a

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

que é equivalente a

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

Os dígitos da base 24 são 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f, ..., n.

A especificação do idioma diz

  1. Se S contiver algum caractere que não seja um dígito radix-R, seja Z a substring de S que consiste em todos os caracteres antes do primeiro caractere; caso contrário, seja Z S.

que é a parte que garante que literais inteiros no estilo C 15Lanalisem corretamente, portanto, o acima é equivalente a

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

"n" é a 23ª letra da lista de dígitos acima.

QED

Mike Samuel
fonte
16

Eu acho que nullé convertido em uma string "null". Então, na nverdade, está 23em 'base24' (o mesmo em 'base25' +), ué inválido em 'base24', portanto o restante da string nullserá ignorado. É por isso que ele produz 23até que use torne válido em 'base31'.

Floern
fonte
7

parseInt usa representação alfanumérica; em seguida, na base-24 "n" é válido, mas "u" é um caractere inválido, então parseInt analisa apenas o valor "n" ....

parseInt("n",24) -> 23

como exemplo, tente com isso:

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

O resultado será "3".

fdaines
fonte