Olá Web Developers! Não importa a sua experiência com JavaScript: se já tiver estudado outra linguagem de programação, provavelmente já achou algum comportamento do JavaScript um pouco estranho. Então vamos conhecer e entender algumas partes estranhas da linguagem.
Mas antes já peço que você entenda que não existe comportamento errado em linguagem de programação. Cada linguagem é uma ferramenta criada com um propósito, e o criador tem a liberdade de escolher como cada funcionalidade vai se comportar. Falar mal do comportamento de uma linguagem seria o equivalente a tirar sarro de um cachorro por sua incapacidade de voar como os pássaros, não faz sentido.
Então, vamos conhecer comportamentos estranhos que muitas vezes são tratados como piada, mas que você verá que possui uma certa lógica por trás disso.
Curso JavaScript Básico
Conhecer o curso0.1 + 0.2 é diferente de 0.3
Essa é uma piada clássica com JavaScript que na verdade é uma certa injustiça. Tente fazer essa soma e terá o seguinte resultado:
0.1 + 0.2
// > 0.30000000000000004
Tudo no computador são 0 e 1. Então os computadores possuem certa dificuldade em trabalhar com números que possuem casas decimais, pois eles trabalham bem apenas com números inteiros. Números decimais são aproximações, o que pode causar imprecisão em certos cálculos.
Eu digo que é uma injustiça falar disso apenas em relação ao JavaScript, pois todas as linguagens que utilizam cálculos com pontos flutuantes possuem esse problema. É algo tão conhecido que você pode conferir o site http://0.30000000000000004.com/, que lista esta conta em várias linguagens de programação.
Não é à toa que existem bibliotecas nas mais variadas linguagens para quem precisa trabalhar com cálculos muito precisos ou números muito grandes (ou muito pequenos).
Soma e Subtração entre números e strings
Um bom código não misturaria outros tipos de dados em soma e subtração além de números, até porque daria erro. Mas nosso amigo JavaScript
é super de boa, ele não liga. E então algumas pessoas podem achar estranho um exemplo bem simples:
"3" - 1
//> 2
Se deu certo, será que podemos fazer isso com a adição?
"3" + 1
//> "31"
No primeiro exemplo tivemos um resultado esperado, o número 2. Mas no segundo tivemos a string
“31” ao invés do número 4.
Isso ocorre porque o operador -
só serve para subtração. Então a string
“3” é convertida automaticamente para o number
3, nos permitindo ter o resultado correto da subtração.
Porém, o operador +
serve tanto para adição quanto para concatenar strings. No segundo exemplo, por termos a string
“3”, o número 1 é convertido para string
e ocorre a concatenação entre os valores ao invés de uma adição. Se a prioridade do operador +
fosse adição, seria impossível concatenar números a uma string
, como em:
const dia = 2;
"Hoje é dia " + dia;
Essas conversões automáticas são causadas pelo JavaScript ser uma linguagem de tipagem fraca. Aqui no blog temos um post que explica qual a diferença entre a tipagem forte e fraca.
A comparação de null com 0
Só faz sentido comparar se um valor é maior que outro se ambos forem valores numéricos, mas este é um outro exemplo comum e uma dúvida que já foi feita por alguns alunos aqui da TreinaWeb. Veja as seguintes comparações entre null
e 0
.
null > 0; //> false
null < 0; //> false
null == 0; //> false
null >= 0; //> true
null <= 0; //> true
Se algo não é maior, menor e nem igual que algo, como pode ser maior igual e menor igual? Acontece que cada uma das expressões são validadas de forma diferente seguindo as regras de comparação da linguagem:
Seguindo as regras de comparação relacional, nas comparações de >
e <
o null
é convertido para +0 e 0 continua sendo 0. Como +0 é igual a 0, as comparações >
e <
serão falsas.
Já na comparação com ==
, seguindo o algoritmo de comparação de igualdade descrito em 7.2.13 Abstract Equality Comparison
, como nenhuma das condições é atendida, o valor padrão retornado é false
.
Já as comparações >=
e <=
acabam funcionando da seguinte maneira: “Se null < 0
anteriormente resultou em false, então null >= 0
é true, pois se algo não é menor, só pode ser maior ou igual”. Até que faz sentido, não é mesmo?
Curso JavaScript Avançado
Conhecer o cursoErro ao ordenar números com .sort()
Já vi palestrantes fazerem piada e tirarem gargalhadas da plateia por conta do seguinte comportamento ao tentar ordenar um Array
de números:
[10, 1, 12, 2, 3, 25].sort()
//> [ 1, 10, 12, 2, 25, 3 ]
Acredito que se esse palestrante tivesse pelo menos estudado como o .sort()
funciona de verdade, teria evitado tratar isso como algo errado. Que bom que a plateia dele também não trabalhava com JavaScript, ou ele teria passado vergonha no palco.
O comportamento padrão do .sort()
é ordenar strings
. Então, todos os nossos números são convertidos para string
, o que explica o "10"
vir antes de "2"
, já que 1 vem antes de 2. Acontece que a conversão é feita apenas durante a comparação, por isso que os números no resultado continuam sendo do tipo number
. Posso te provar com um exemplo bem simples:
['[u', {nome: 'treinaweb'}, '[a'].sort()
//> [ '[a', {nome: 'treinaweb'}, '[u' ]
O objeto {nome: 'treinaweb'}
é convertido em string
pelo método .toString()
, resultando em "[object Object]"
. Por isso que "[a"
ficou antes e "[u"
ficou depois, mas note que o Array
ordenado manteve nosso objeto ao invés de deixá-lo convertido em string
.
Se você quiser ordenar coisas que não são strings, o jeito certo é passar uma função para .sort()
e indicar as regras para ele saber qual elemento deve vir antes e qual deve vir depois. Assim você pode ordenar números ou até mesmo objetos por um ou mais campos, como nome e idade.
[10, 1, 12, 2, 3, 25].sort((a, b) => a - b)
//> [ 1, 2, 3, 10, 12, 25 ]
Mais comparações com 0
Você já deve ter visto o seguinte meme sobre JavaScript baseado no desenho Bob Esponja, com Patrick e o Homem Raio no episódio O Homem-Sereia e o Mexilhãozinho III
:
[caption id=“attachment_13592” align=“alignnone” width=“1452”]
Basicamente:
0 == "0"
//> true
0 == []
//> true
"0" == []
//> false
Aqui é mais um caso em que há comparação entre dados de tipos diferentes. Quando fazemos comparações com ===
, se os tipos forem diferentes o JavaScript já vai retornar que é falso. Comparações com ==
que utilizam dados de tipos diferentes fazem que o JavaScript converta um dos dados para poder fazer a comparação de seus valores. Por isso que é uma boa prática sempre utilizar ===
, evitando comportamentos inesperados. Mas já que é algo recorrente nas redes sociais, vamos entender os motivos:
0 == “0”
Como mostrado nas regras da linguagem ao fazer comparações de igualdade (Abstract Equality Comparison):
- If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
Esse ToNumber
é uma operação com as regras descritas na seção 7.1.3 ToNumber na especificação da linguagem.
Então na comparação entre 0 e “0”, por termos um número e uma string, o JavaScript vai converter o “0” em um número. Como 0 é igual a 0, o resultado é true
.
0 == []
Na comparação entre 0 e [] seguimos outra regra. [] é considerado um objeto.
- If Type(x) is either String, Number, or Symbol and Type(y) is Object, return the result of the comparison x == ToPrimitive(y).
Esse ToPrimitive
é uma operação com as regras descritas na seção 7.1.1 ToPrimitive na especificação da linguagem.
Então na comparação entre 0 e [], por termos um número e um objeto, o JavaScript vai pegar o valor primitivo de []. Você pode fazer isso executando [].toString()
, que retornará uma string
vazia. E faz sentido se pensarmos que uma string
é um array
de caracteres.
O JavaScript possui 8 valores considerados falsos. Qualquer coisa diferente disso é considerado verdadeiro:
- 0 (número zero)
- -0 (zero negativo)
- 0n (0 no formato BigInt, seguindo a mesma regra do Number 0)
- “” (strings vazias)
- null
- undefined
- NaN
Como 0
e ""
são valores falsos, a comparação entre 0 e [] (que é convertido em string vazia) é true
, pois false
é igual a false
.
“0” == []
Aqui seguimos a mesma regra do item anterior. Temos a comparação de uma string
e de um objeto. "0"
, por ser uma string
, já é um tipo primitivo, então continua intacto. E como vimos anteriormente, []
é convertido numa string
vazia.
Conversões feitas, então basta fazer a comparação: "0" == ""
. Seguindo a primeira regras das comparações, quando temos dois tipos iguais, a comparação é feita do mesmo jeito que o operador ===
.
- If Type(x) is the same as Type(y), then a. Return the result of performing Strict Equality Comparison x === y.
Portanto, é simples de entender que uma string
“0” é diferente de uma string
vazia, certo? Por isso que o resultado dessa comparação é false
.
E então, o que achou desta lista? Conhecia a razão de algum comportamento? Comente e diga se você gostaria de uma parte 2.