Porque [ ' 1 ' , ' 7 ' , ' 11 ' ] .map (parseInt) retorna [1, NaN, 3] em Javascript

Eric Tong Segue 12 de junho · 5 min ler ???

Javascript é estranho. Não acredita em mim? Tente converter uma matriz de strings em inteiros usando map e parseInt. Abra seu console (F12 no Chrome), cole o seguinte e pressione Enter (ou execute a caneta abaixo).

 [1, 7, 11] map (parseInt); 

Em vez de nos dar uma matriz de inteiros [1, 7, 11] , acabamos com [1, NaN, 3] . O que? Para descobrir o que está acontecendo, primeiro teremos que falar sobre alguns conceitos de Javascript. Se você quiser um TLDR, incluí um breve resumo no final desta matéria.

Verdade e Falsidade

Aqui está uma declaração if-else simples em JavaScript:

 se for verdade) { 
// isso sempre roda
} outro {
// isso nunca roda
}

Nesse caso, a condição da instrução if-else é verdadeira, portanto, o bloco if é sempre executado e o bloco else é ignorado. Este é um exemplo trivial porque true é um booleano. E se colocarmos um não-booleano como condição?

 if ("olá mundo") { 
// isso vai rodar?
console.log ("Condição é verdade");
} outro {
// ou isto?
console.log ("Condição é falsa");
}

Tente executar este código no console do seu desenvolvedor (F12 no Chrome). Você deve descobrir que o bloco if é executado. Isso ocorre porque o objeto string "hello world" é verdadeiro.

Cada objeto Javascript ou é truthy ou Falsas. Quando colocados em um contexto booleano, como uma declaração if-else, os objetos são tratados como verdadeiros ou falsos com base em sua veracidade. Então, quais objetos são verdadeiros e quais são falsamente? Aqui está uma regra simples a seguir:

Todos os valores são verdadeiros, exceto: false , 0 , "" (string vazia), null , undefined e NaN .

Confusamente, isso significa que a string "false" , a string "0" , um objeto vazio {} e uma matriz vazia [] são todos verdadeiros. Você pode verificar isso passando um objeto para a função booleana (por exemplo, Boolean("0"); ).

Para nossos propósitos, basta lembrar que 0 é falsamente.

Radix

 0 1 2 3 4 5 6 7 8 9 10 

Quando contamos de zero a nove, temos símbolos diferentes para cada um dos números (0–9). No entanto, quando chegarmos a dez, precisamos de dois símbolos diferentes (1 e 0) para representar o número. Isso porque nosso sistema de contagem decimal tem uma raiz (ou base) de dez.

Radix é o menor número que só pode ser representado por mais de um símbolo. Diferentes sistemas de contagem têm diferentes radices e, portanto, os mesmos dígitos podem se referir a números diferentes em sistemas de contagem.

 HEXADECIMAL BINÁRIO DECIMAL 
RADIX = 10 RADIX = 2 RADIX = 16
0 0 0
1 1 1
2 10 2
3 11 3
4 100 4
5 101 5
6 110 6
7 111 7
8 1000 8
9 1001 9
10 1010 A
11 1011 B
12 1100 C
13 1101 D
14 1110 E
15 1111 F
16 10000 10
17 10001 11

Por exemplo, olhando para a tabela acima, vemos que os mesmos dígitos 11 podem significar diferentes números em diferentes sistemas de contagem. Se a raiz é 2, então ela se refere ao número 3. Se a raiz é 16, então ela se refere ao número 17.

Você deve ter notado que no nosso exemplo, parseInt retornou 3 quando a entrada é 11, o que corresponde à coluna Binária na tabela acima.

Argumentos de função

Funções em Javascript podem ser chamadas com qualquer número de argumentos, mesmo que não sejam iguais ao número de parâmetros da função declarada. Os argumentos ausentes são tratados como indefinidos e argumentos extras são ignorados (mas armazenados no objeto de argumentos do tipo array).

 função foo (x, y) { 
console.log (x);
console.log (y);
}
foo (1, 2); // registra 1, 2
foo (1); // log 1, indefinido
foo (1, 2, 3); // registra 1, 2

mapa()

Estamos quase lá!

Map é um método no protótipo Array que retorna uma nova matriz dos resultados da passagem de cada elemento da matriz original para uma função. Por exemplo, o código a seguir multiplica cada elemento na matriz por 3:

 função multiplyBy3 (x) { 
return x * 3;
}
const result = [1, 2, 3, 4, 5] .map (multiplplyBy3); console.log (resultado); // registra [3, 6, 9, 12, 15];

Agora, digamos que eu queira registrar cada elemento usando map() (sem instruções de retorno). Eu deveria ser capaz de passar o console.log como um argumento para map() … certo?

 [1, 2, 3, 4, 5] .map (console.log); 

Algo muito estranho está acontecendo. Em vez de registrar apenas o valor, cada chamada de console.log também registra o índice e a matriz completa.

 [1, 2, 3, 4, 5] .map (console.log); // O acima é equivalente a [1, 2, 3, 4, 5] .map ( 
(val, index, array) => console.log (val, index, array)
);
// e não equivalente a [1, 2, 3, 4, 5] .map (
val => console.log (val)
);

Quando uma função é passada para map() , para cada iteração, três argumentos são passados para a função : currentValue , currentIndex e a array completa. É por isso que três entradas são registradas para cada iteração.

Agora temos todas as peças que precisamos para resolver esse mistério.

Juntando tudo

ParseInt leva dois argumentos: string e radix . Se o radical fornecido for falso, por padrão, o radix é definido como 10.

 parseInt ('11 '); => 11 
parseInt ('11 ', 2); => 3
parseInt ('11 ', 16); => 17
parseInt ('11 ', indefinido); => 11 (a raiz é falsy)
parseInt ('11 ', 0); => 11 (a raiz é falsy)

Agora vamos executar nosso exemplo passo a passo.

 [1, 7, 11] map (parseInt); => [1, NaN, 3] // Primeira iteração: val = '1', índice = 0, array = ['1', '7', '11'] parseInt ('1', 0, ['1 ',' 7 ',' 11 ']); => 1 

Como 0 é falso, a raiz é configurada para o valor padrão 10. parseInt() usa apenas dois argumentos, portanto, o terceiro argumento ['1', '7', '11'] é ignorado. A string '1' na raiz 10 refere-se ao número 1.

 // Segunda iteração: val = '7', index = 1, array = ['1', '7', '11'] parseInt ('7', 1, ['1', '7', '11' ]); => NaN 

Em um sistema radix 1, o símbolo '7' não existe. Como na primeira iteração, o último argumento é ignorado. Então, parseInt() retorna NaN .

 // Terceira iteração: val = '11', index = 2, array = ['1', '7', '11'] parseInt ('11 ', 2, [' 1 ',' 7 ',' 11 ' ]); => 3 

Em um sistema radix 2 (binário), o símbolo '11' refere-se ao número 3. O último argumento é ignorado.

Resumo (TLDR)

['1', '7', '11'].map(parseInt) não funciona como esperado porque o map passa três argumentos para parseInt() em cada iteração. O segundo index argumentos é passado para parseInt como um parâmetro radix . Assim, cada string na matriz é analisada usando uma raiz diferente. '7' é analisado como radix 1, que é NaN , '11' é analisado como radix 2, que é 3. '1' é analisado como a raiz padrão 10, porque seu índice 0 é falso.

E assim, o código a seguir funcionará como pretendido:

 ['1', '7', '11']. Map (numStr => parseInt (numStr));