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

de.front #002 O que acontece no algoritmo de igualdade do JS?

tags satisfies, NaN, css variables, strict equals, loosely equals

Opa! boas vindas a segunda edição do de.front! hoje vamos entender um pouco sobre uma função relativamente nova do typescript, o por que que o NaN é uma tipagem diferenciada, variáveis css e mais a fundo sobre como é o funcionamento dos algoritmos de igualdade.

<aside>

TS satisfies

Você já precisou montar um objeto e passar como argumento de uma funçao? já fez isso no typescript e percebeu como é chato? nessas situações normalmente precisamos "forçar" uma tipagem para que ele aceite como verdade:

const user = {
  content: undefined,
  name: "Jorge"
} as PrintContentProps // irá retornar erro no objeto como um todo, imagine aqui um objeto de vários e vários itens para entender qual o erro que está retornando

printContent(user)

function printContent(param: PrintContentProps) {
  console.log(param.content)
}

interface PrintContentProps {
  content: string;
  nome: string
}

Agora mudando para o satisfies:

const user = {
  content: undefined, // será identificado somente aqui como um problema para que você faça o ajuste!
  name: "Jorge"
} satisfies PrintContentProps

printContent(user)

function printContent(param: PrintContentProps) {
  console.log(param.content)
}

interface PrintContentProps {
  content: string;
  nome: string
}

E ai, você pode me perguntar, por que devo trocar? bem, eu consideraria a ideia por que ele te dá essa visão mais clara do que está dando erro, podendo ver antes mesmo de colocar como um argumento na função, além disso, quando usamos o as estamos informando que aquela é a tipagem, e dependendo da situação pode ocorrer de o programa aceitar que é aquele valor realmente é o correto, gerando problemas futuros 🐛

JS NaN

Sabia que valores NaN (Not a Number) não são iguais? pois é!

NaN === NaN // false

Tá mas por que isso acontece?

Na especificação do javascript, valores NaN são considerados diferentes dele mesmo em qualquer aspecto, por que veja, a string computador e array ['teclado'] não são iguais em praticamente nada, porém os dois não são números, logo os dois são NaNs e já que existe essa certa igualdade eles poderiam ser considerados iguais de maneira errada

Ok, entendi, mas como que eu lido com isso no meu dia a dia?

Aqui a gente pode utilizar uma função que foi disponibilizada para nós que é o isNaN, com um pequeno detalhe que posso entrar mais a fundo em outro momento, dê preferencia a função que vem do construtor Number, dessa maneira:

Number.isNaN('computador') == Number.isNaN(['teclado']) // como o resultado é um boolean doss dois lado, eu posso garantir que os dois realmente não são números e gerar um grau de igualdade por conta disso!

CSS variables

Se você já teve problema e valores dentro do CSS e também já não conhece essa funcionalidade dele, podemos atribuir valores que nem fazemos com o javascript, quer dizer, mais ou menos por que precisa de uns detalhes a mais hehe

:root {
  --var-1: #FFF;
}

div {
  background-color: var(--var-1);
}

Você só precisa adicionar a iniciação dela, normalmente no pseudo elemento root utilizando a nomenclatura de dois hifens no inicio seguido do seu nome e para consumir esse conteúdo, é usar a função var() com essa variável, o mais legal é que ela vai aparecer exatamente assim no dev tools dando maior visibilidade do que está acontecendo!

<main>

Agora para o assunto principal / polêmico que é o uso das igualdades!

Acredito que praticamente todos nós somos ensinados desde sempre que o javascript tem o loose equals == e o strict equals === e que se você usar o loose equals o seu programa entrará em combustão espontânea, que tudo que usamos será convertido para valores impossíveis e que devemos ter raiva das pessoas que usam essa expressão, e a única solução é trazer mais um sinal para que tudo se resolva de maneira perfeita, onde todos os problemas são resolvidos e nós devs fronts podemos viver felizes para sempre com nossas escolhas. Quero te mostrar um pouco mais por de baixo dos panos qual que é a diferença entre esses dois, e pasmem, como podemos também utilizar das funcionalidades que cada um nós dá, sim, vou te dar munição para poder usar um caractere a menos no seu programa!

Ok ok, agora entrando no conteúdo real, precisamos entender que, cada um tem seu algoritmo, ou conjunto de instruções, que vão definir como os valores de cada lado serão utilizados para chegar em um valor boolean de comparação final, termos um conceito mental de como funciona vai nós ajudar a entender quando utilizar cada um, já que são ferramentas que está à nosso dispor, então vou passar abaixo como são, começando pelo strict:

Para efeito dos exemplos estarei nomeando os valores comparados como esquerda e direita para definir as duas opções que temos na comparação

algoritmo do strict equal ou ===

  • Se tipo do esquerda for diferente do direita retorna false
  • Se tipo de esquerda for número então retorna comparação numérica entre esquerda e direita
  • Retorna comparador de mesmo mesmo valor que não seja numérica.

bastante simples né? aqui devemos ter cuidado somente com a comparação de NaNs como foi comentado anteriormente e comparação de objetos que podem ser iguais mas não tem a mesma referencia (que pode ser outro tópico futuro se você quiser).

algoritmo do loosely equal ou ==

  • Se tipo esquerda for igual ao tipo direita retorna algoritmo de validação strict
  • Se esquerda for null e direita for undefined retorna true
  • Se esquerda for undefined e direita for null retorna true
  • Se esquerda for number e direita for string, converte a string para number e reinicia o algoritmo
  • Se esquerda for string e direita for number, converte a string para number e reinicia o algoritmo
  • Se esquerda for bigint e direita for string, converte a string para bigint
    • Se a conversão resultar em undefined retorna false
    • Reinicia o algoritmo
  • Se esquerda for string e direita for bigint reinicia o algoritmo porém trocando as posições de esquerda e direita
  • Se esquerda for boolean converte ele para number e reinicia o algoritmo
  • Se direita for boolean converte ele para number e reinicia o algoritmo
  • Se esquerda for qualquer um entre string, number, bigint ou Symbol e direita for um object converte direita para tipo primitivo e reinicia o algoritmo
  • Se esquerda for um object e direita for qualquer um entre string, number, bigint ou Symbol converte esquerda para tipo primitivo e reinicia o algoritmo
  • Se esquerda for bigint e direita for number ou o inverso, então:
    • esquerda OU direita for infinity retorna false
    • esquerda e direita tem o mesmo valor e é finita, retorna true
  • retorna false

Ufa! viu que é um algoritmo muito mais extenso? imagino que sim kk

Por ter tantas regras, faz com que tenhamos situações onde caso seu mapa mental não esteja alinhado com ele, gera um problemão de entendimento, porém se você entende bem isso aqui, facilita muito na hora de decidir e utilizar algumas dessas funcionalidades, lê novamente esse algoritmo e anota alguns pontos interessantes sobre ele.

Fez?

Bora falar desses pontos e ver se estamos alinhados:

Todos os caminhos levam a number

Com várias regras que o algoritmo tem, ele sempre tem a tendencia de tentar fazer o valor chegar em números, percebe? nos momentos em que ele reinicia o algoritmo, ele está convertendo de alguma maneira esse valor para o mais próximo de number que seja possível, assim é algo que você pode ficar em mente, em talvez como otimizar isso!

Infinitas não forma

Valores infinitos só são iguais com eles mesmos, então se um deles não for, o algoritmo nem se dá mais o trabalho

null e undefined são iguais???

Pois é! nesse algoritmo, os dois valores são considerados iguais, então poderíamos utilizar isso de alguma forma...

Calma, o que é aquela primeira instrução?

Simmm, eu propositalmente falei logo pelo strict equal por conta disso, logo na primeira instrução desse algoritmo, se os tipos forem iguais ele retorna a regra de strict!! então, se você já sabe quais são os tipos dos dois lados da igualdade, que espero que você sempre saiba kk, podemos utilizar do loose para entrar rapidamente na regra do strict, assim permitindo a mesma lógica no final das contas!

E ai viu mais algo interessante? me fala sobre!

Como usar isso ao nosso favor?

Como primeiro exemplo, eu comentei ali sobre o null e undefined e podemos fazer isso aqui:

Ao invés de:

if(content !== undefined && content !== null) {
  return "conteúdo"
}

Podemos fazer:

if(content != null) {
  return "conteúdo"
}

Legal né? já nós dá opções mais simples de utilizar

Já para o caso de você precisar utilizar o typeof por exemplo:

if(typeof content == 'string') // já que as opções são sempre strings com o nome de cada um, não precisamos do = extra

Outro interessante é, não sei se você sabe, mas quando usamos um array vazio para fazer comparação, ele é considerado true e para isso precisamos utilizar o length para checar o comprimento e estar tudo certo:

if([]) // true

if([].length) // false

Mas podemos utilizar da habilidade do algoritmo de transformar tudo em number para chegar em algo que funcione para nós:

if([] != 0) // normalmente o array seria um valor truthy mas como comparamos com um número, esse obrigatoriamente é convertido, assim chegando no false!

// para comparação, utilizando o strict equal isso aqui seria true:
if([] !== 0) // tipos são diferentes? nem vou me dar o trabalho, é true!

Conclusão

Tentei fazer aqui uma defesa por essa parte do javascript que é tão marginalizada, e tem sim seus vários casos de uso, mas precisa ter também esse mapa mental ou algoritmo na cabeça quando for fazer suas validações, como um pesquisa sua, sabe quando aparecer aquelas regras consideradas malucas que gostam de tirar onda conosco, pega ela e passa por esse algoritmo para entender e passar esse conhecimento adiante!

Bônus

Lembra quando falei lá em cima sobre o isNaN e como devemos dar preferência a função que vem do construtor Number?

É por que a versão global do isNaN utiliza da mesma lógica do algoritmo do loosely equals, forçando sempre a conversão para number que é algo que pode ocasionar algumas lógicas meio confusas!

Então o pessoal lá nós deu o Number.isNaN que não faz nenhum tipo de conversão, assim ficando mais simples e com casos de uso mais claros evitando bugs por falta de conhecimento.

e aí? se você chegou até aqui, obrigado! fiquei muito feliz com as interações que tive na última postagens e quero continuar trazendo esse conteúdo para vocês para entrarmos cada vez mais no mundo do front end juntos!

me manda um feedback! gostou do conteúdo? quer ver mais? falei besteira? me avisa!

é nozes!

Carregando publicação patrocinada...