Executando verificação de segurança...
7

EXPLICANDO TODAS AS PECULIARIDADES NO JAVASCRIPT - Luciano655dev

Este post foi originalmente feito por mim, em inglês, no LeetCode, fique a vontade para ver o post original!
Caso a tradução esteja ruim, acabei fazendo-a as pressas com o Google Tradutor e não revisei de forma extremamente detalhada.
Mesmo assim, espero que goste!

image
Source: Fireship

Você já deve ter visto essa imagem circulando pela internet, mas nunca soube realmente por que cada coisa aconteceu.
JavaScript tem alguns comportamentos interessantes e às vezes contra-intuitivos devido à sua coerção de tipo e regras de conversão automática. Vamos examinar cada afirmação:

- typeof NaN = "number": NaN (Not a Number)

No JavaScript, o typeof NaN retorna "number" deriva do IEEE 754 floating-point standard, que define NaN como um "quiet NaN" para representar undefined ou valores irrepresentáveis​resultantes de certas operações aritméticas.
Embora a categorização de NaN como "number" pareça ser contra-intuitivo, ele se alinha com a representação do JavaScript de NaN como um valor especial que indica cálculos numéricos problemáticos.
A escolha do design da linguagem de rotular NaN como um "número" facilita a identificação de resultados numéricos inesperados ou errôneos, apesar da propriedade única de NaN de não ser igual a nenhum outro valor, incluindo ele mesmo.

- 9999999999999999 = 100000000000000000

A razão para isso reside no fato de que o JavaScript usa uma representação de ponto flutuante para números, aderindo ao IEEE 754 standard.
Este padrão impõe limitações na precisão dos valores numéricos, particularmente quando se trata de “números inteiros” muito grandes. Os números na instrução excedem o limite de precisão da representação de ponto flutuante do JavaScript, levando a uma perda de precisão durante a atribuição.
Como resultado, os dois valores não são considerados iguais no contexto da representação numérica do JavaScript e a comparação é avaliada como false. Ele destaca a importância de compreender as limitações da aritmética de ponto flutuante ao trabalhar com grandes números em JavaScript.

- (0.5+0.1 == 0.6) = true || (0.1+0.2 == 0.3) = false

No JavaScript, as expressões 0,5 + 0,1 == 0,6 e 0,1 + 0,2 == 0,3 produzem resultados diferentes devido às limitações inerentes à representação de frações decimais na aritmética binária de ponto flutuante.
Enquanto a primeira expressão é avaliada como true porque os valores envolvidos 0,5, 0,1 e 0,6 não sofrem erros de precisão significativos em suas representações binárias, a segunda expressão é avaliada como false. No segundo caso, a adição de 0,1 e 0,2 resulta em um valor que não é exatamente igual a 0,3 devido a erros de arredondamento na representação binária.
Esta discrepância sublinha a cautela necessária ao realizar comparações diretas de igualdade com números de ponto flutuante. Muitas vezes é recomendado o uso de técnicas como comparações épsilon, onde uma pequena margem de erro (épsilon) é considerada, para explicar a imprecisão inerente da aritmética de ponto flutuante ao lidar com verificações de igualdade envolvendo frações decimais.

- Math.max() = -Infinity || Math.min() = Infinity

No JavaScript, as expressões Math.max() e Math.min() assumem valores específicos como convenção.
Quando Math.max() é chamado sem nenhum argumento, ele retorna infinito negativo (-Infinity). Por outro lado, quando Math.min() é chamado sem argumentos, ele retorna infinito positivo (Infinity).
Este comportamento foi projetado para garantir que qualquer comparação subsequente com números reais produzirá o valor máximo ou mínimo, respectivamente. Ao comparar valores numéricos reais usando essas funções, o resultado será o máximo ou o mínimo dos valores fornecidos, substituindo os valores infinitos padrão. Esta abordagem permite que os desenvolvedores encontrem os valores máximos ou mínimos em um conjunto de números, e os valores padrão iniciais indicam que qualquer número real na comparação subsequente será maior que -Infinity e menor que Infinity, garantindo uma comparação precisa resultado.

- []+[] = ""

No JavaScript, a expressão [] + [] é avaliada como uma string vazia "" devido à maneira como o operador + se comporta quando aplicado a arrays.
O operador + está sobrecarregado em JavaScript para realizar adição e concatenação de strings. Quando usado com arrays, ele aciona o método padrão .toString() para cada array, convertendo os arrays em strings e então concatena essas representações de strings.
Para matrizes vazias, a representação de string é uma string vazia. Consequentemente, [] + [] resulta na concatenação de duas strings vazias, produzindo uma string vazia como resultado final. Esse comportamento pode parecer contra-intuitivo, mas segue as regras de coerção de tipo do JavaScript e o tratamento específico de conversões de array em string no contexto do operador +.

- []+{} = "[object Object]"

Em JavaScript, a expressão [] + {} resulta na string "[object Object]" devido à coerção de tipo implícita e ao comportamento de concatenação de string do operador + também.
Quando o operador + encontra um array e um objeto neste contexto, ele converte o array em uma string usando seu método .toString(), que resulta em uma string vazia.
Da mesma forma, o objeto é convertido em sua representação de string, que é a string "[object Object]" baseada no método padrão .toString() para objetos.
The + operator then concatenates these two string representations, leading to the final result of "[object Object]". This behavior is a consequence of JavaScript's automatic type conversion rules and the specific ways in which arrays and objects are coerced to strings in the context of the + operator.

- { }+[] = 0

No JavaScript, a expressão { }+[] é avaliada como 0 devido à maneira como a linguagem interpreta a combinação de um objeto literal e um array com o operador +.
A chave de abertura {} é inicialmente interpretada como uma instrução de bloco vazia em vez de um objeto literal porque não é precedida por nenhuma atribuição ou rótulo. Como resultado, ele não cria um novo objeto, mas é tratado como um bloco de código isolado.
O +[] subsequente é então interpretado como o operador unário de adição aplicado a um array vazio, resultando no valor numérico 0. Portanto, a expressão geral torna-se 0 + 0, produzindo o resultado final de 0. Esse comportamento destaca a importância de compreender as regras do JavaScript para análise de instruções e coerção de tipos, pois isso pode levar a resultados inesperados.

- (true+true+true === 3) = true || true-true = 0

No JavaScript, as expressões true + true + true === 3 avaliando como true e true - true equivalendo a 0 demonstram a coerção automática de tipo que ocorre em certas operações.
Na primeira expressão, o operador + é usado para adição, e o JavaScript força os valores booleanos a valores numéricos (1 neste caso). Portanto, true + true + true resulta em 3, e a verificação de igualdade com === em relação ao valor numérico 3 retorna true. Da mesma forma, na segunda expressão, a operação de subtração envolve a coerção de valores booleanos em números, resultando em 1 - 1 e produzindo 0.
Esses comportamentos exemplificam como o JavaScript realiza conversões de tipo para facilitar operações aritméticas, mesmo quando se trabalha com valores booleanos. Os desenvolvedores devem estar atentos a essas conversões implícitas para evitar consequências indesejadas em seu código.

- (true == 1) = true || (true === 1) = false

No JavaScript, as expressões true == 1 avaliadas como true e true === 1 resultando em false ilustram a distinção entre igualdade frouxa == e igualdade estrita ===.
O operador de igualdade flexível tenta a coerção de tipo, convertendo os operandos em um tipo comum antes de fazer a comparação. Na primeira expressão, true é forçado ao valor numérico 1, e a comparação se torna 1 == 1, que é true. Por outro lado, o operador de igualdade estrita não executa coerção de tipo e exige que o valor e o tipo correspondam. Na segunda expressão, true e 1 são de tipos diferentes (booleano e número, respectivamente), então true === 1 é avaliado como false.
Essas nuances destacam a importância de escolher o operador de igualdade apropriado com base no comportamento desejado e na compreensão das implicações da coerção de tipo em comparações de JavaScript.

- (!+[]+[]+![]).length = 9

A expressão (!+[]+[]+![]).length em JavaScript é avaliada como 9 devido a uma sequência de operações de coerção de tipo e concatenação.
Dividindo, !+[] primeiro converte o array vazio [] em um booleano (![] is true) e depois em um número (+true is 1). Consequentemente, a expressão torna-se "1" + [] + true. Aqui, o operador + é sobrecarregado para concatenação de strings, então o array vazio é convertido em uma string vazia, resultando na string "1" + "" + "true". A string concatenada final é "1true," e seu comprimento é 9.
A expressão, aparentemente complexa, ressalta as regras de coerção de tipo do JavaScript e a importância de compreender como diferentes operadores se comportam em sequência.

- 9+"1" = "91" || 91-"1" = 90

No JavaScript, as expressões 9 + "1" e 91 - "1" mostram as regras automáticas de conversão de tipo da linguagem.
Na primeira expressão, o operador de adição encontra um valor numérico 9 e uma string "1". JavaScript executa coerção implícita de tipo, convertendo o valor numérico em uma string e, em seguida, concatena as duas strings, resultando na string "91".
Por outro lado, na segunda expressão, o operador de subtração é usado com um valor numérico 91 e uma string "1". JavaScript reconhece a intenção de realizar aritmética e força a string a um valor numérico antes de subtrair, levando ao resultado numérico de 90.
Esses exemplos ilustram como o JavaScript ajusta dinamicamente os tipos de dados durante as operações, às vezes levando a resultados inesperados, enfatizando a importância de compreender a coerção de tipos na linguagem.

- ([]==0) = true

Em JavaScript, a expressão [] == 0 é avaliada como verdadeira devido às regras implícitas de coerção de tipo da linguagem no contexto de comparações == de igualdade frouxa.
Ao comparar uma matriz (como []) com um valor numérico 0 usando o operador de igualdade flexível, o JavaScript tenta converter ambos os operandos em um tipo comum. Nesse caso, o array vazio é forçado a um valor primitivo, resultando em uma string vazia "".
Posteriormente, uma string vazia é forçada ao valor numérico 0. Como resultado, a expressão torna-se efetivamente 0 == 0, e a verificação de igualdade flexível é avaliada como true. É importante observar que o uso de igualdade estrita === geralmente é recomendado para evitar possíveis armadilhas associadas à coerção implícita de tipos em comparações de JavaScript.

Espero que tenha gostado do post!

Mihas Redes-sociais:

Twitter: @Luciano655dev

GitHub: @Luciano655dev

Discord: @Luciano655 (Luciano655#7898)

Website: luciano655dev.netlify.app

Qualquer coisa, eu estou disponível! Fique a vontate para mandar qualquer mensagem ou fazer qualquer comentário!
4

Só um detalhe: os 3 primeiros itens não são exclusividade do JavaScript. Eles são todos consequências do padrão IEEE 754, e afetam outras linguagens que usam este padrão (como por exemplo C, Java, C#, etc).

No caso do NaN, apesar do nome que pode confundir ("not a number", mas é um "valor númerico"), é um valor válido e definido pela norma. Inclusive, o fato dele não ser igual a nenhum outro valor (nem ele mesmo) é totalmente arbitrário, mas existe um raciocínio por trás, explicado em mais detalhes aqui.

Sobre o segundo caso, ver a explicação aqui.

E sobre o terceiro caso, é o motivo pelo qual números de ponto flutuante não são o tipo mais adequado para operações financeiras (um erro infelizmente comum). Sobre isso, ver mais detalhes aqui, aqui, aqui, aqui e aqui.


Quanto aos outros casos, eles podem ser explicados pelas regras malucas de coerção automática. Basicamente, cada operador (como o +, ==, etc) verifica o tipo dos seus operandos, e dependendo do caso, converte-os para alguma outra coisa antes de fazer a operação. Para saber as regras exatas, sugiro consultar a especificação oficial da linguagem.

O problema, claro, é que isso gera esses resultados absurdos. O lado bom é que também gerou um dos melhores vídeos sobre a linguagem :-)