Desenvolvimento

27 nov, 2018

Dominando condicionais JavaScript

Publicidade

Particularidades de condicionais em JavaScript que podem facilitar e muito o seu trabalho.

Uma das primeiras coisas que qualquer programador tem que aprender, são os operadores condicionais. Todos nós conhecemos os operadores if, else, else if ou o switch. Eles são uma poderosa ferramenta na tomada de decisão em suas aplicações.

Por exemplo, vamos lembrar da lenda do Rei Arthur. A Excalibur estava presa na pedra e somente o escolhido, Rei Arthur, poderia removê-la. Se a espada fosse programada em JavaScript como isso seria?

const knight = 'King Arthur';

if (knight === 'King Arthur') {
  // A espada é tua. Você é o cara!
  excalibur.remove();
} else {
  // A espada ri de seus esforços!
  excalibur.laugh();
}

Funciona muito bem desta maneira, mas percebi que eu tenho escrito cada vez menos if, else, switch e substituído por outros artifícios que o JavaScript nos proporciona. Vamos abordá-los neste artigo.

Expressions vs Statements

Parece uma simples questão de nomenclatura, mas não o é. Expressions e Statements são coisas bem diferentes. Vamos entendê-las.

Uma expression é qualquer comando que a engine do JavaScript pode avaliar para produzir um valor.

// Literal de um Array
[]

// Literal de um Objeto
{}

// Literal de uma Expressão Regular
/^\d+$/

// Operadores lógicos
(x && y)

// Operador ternário
(x ? y : z)

// Operação aritmética
(a + b) * z

// Atribuição
x = 100

// Função
(function x(y) {})

// Execução de função
x(100)

// Acesso à uma propriedade de um objeto
pizza.ingredients[0].name

Acima, temos alguns exemplos de expressions no JavaScript, temos literais, operadores lógicos, ternários, operações aritméticas, atribuições, execução de funções e até acesso à propriedades de objetos.

Já um statement é um comando que o JavaScript pode executar para fazer algo ou produzir algum side-effect. Por exemplo: condicionais, declarações de funções ou variáveis, throw, return, try/catch/finally.

Um condicional gera um booleano

Simples assim, um condicional sempre vai gerar um boleano, desde o mais simples teste até os condicionais mais complexos, o resultado será um booleano.

Mas essa é a parte mais fácil de entender. Para dominar melhor condicionais, temos que entender dois conceitos:

  • Os conceitos de truthy e falsy
  • Short-circuiting em condicionais.

Truthy X Falsy

Estranhas essas expressões, certo? Sempre aprendemos que um booleano só pode ser true ou false, então que diabos vem a ser truthy e falsy?

São valores que podem ser avaliados como verdadeiros ou falsos, apesar de não o serem, puramente. Quem já trabalhou com testes unitários provavelmente já teve contato com essas expressões e esses valores.

JavaScript considera falsy alguns valores que podem ser entendidos como vazios, ausentes ou não definidos. São avaliados como falsy os seguintes exemplos:

  • ``, '' , "" ou (strings vazias)
  • 0 ou -0 (número zero xD lembra que 0 é false né?)
  • null
  • undefined
  • NaN
  • false

Qualquer outro valor fora dessa lista é um valor considerado truthy e quando colocado “à prova” em um teste condicional valores falsy serão forçados para false e valores truthy serão forçados para true.

Existem algumas maneiras de converter um valor truthy ou falsy para booleano. A mais conhecida é usando a função Boolean().

(value) => {
  return Boolean(value);
}

Outra maneira que está em alta, é usando o operador ! (NOT). Quando usamos o operador !, ele nos dá o inverso de um valor booleano. Portanto, se usarmos !!, ele nos dá o inverso do inverso, ou seja, o valor certo. Vocês entenderam, né? Todo valor truthy será convertido para true, e todo valor falsy será convertido para false.

(value) => {
  return !!value;
}

Curto Circuito

Os operadores E (&&) e OU (||) precisam de duas expressões para avaliar, e avaliam estas expressões para produzir um valor.

Portanto, as expressões são evaluadas como true ou false, e:

  • O operador E só retornará true se, e somente se, as duas expressões forem true
  • O operador OU retornará true se ao menos uma das expressões forem true

Também devemos lembrar que o operador E tem precedência ao operador OU na hora da execução, o que pode mudar drasticamente o resultado de uma expressão, como vemos abaixo.

false && false || true // true
false && (false || true) // false

Portanto, em uma operação com o operador &&, por exemplo, se o primeiro é false, então o resultado será false, certo? Portanto, não há necessidade de executarmos a segunda expressão. Seria inútil, já que sabemos o resultado. O JavaScript, esperto que só ele, sabe disso e, portanto, tem uma funcionalidade conhecida como short-circuiting, que interrompe a execução quando o valor de uma operação já é conhecida.

Mas os operadores && e || nem sempre produzem valores true ou false. Na verdade, eles geralmente podem produzir qualquer valor. Veja exemplos disso baseando-nos no comportamento do short-circuiting:

  • Quando usamos o && e o valor do primeiro operando é truthy, ele executa o valor do segundo e retorna o mesmo. Porém, se o valor do primeiro operando é falsy, o valor do segundo nunca é analisado e o valor retornado é o valor falsy do primeiro operando.
const a = 'banana';
const b = 'abacate';
const c = null;

(a && b) === 'banana'; // false
(a && b) === 'abacate'; // true
(a && c) === 'banana'; // false
(c && a) === 'banana'; // false
(c && a) === null; // true;

Já quando usamos o || e o valor do primeiro operando é truthy, ele não executa o segundo operando. Somente retorna o valor do primeiro. Caso o primeiro operando seja falsy, o JavaScript irá executar o valor do segundo e retorná-lo.

const a = 'banana';
const b = 'abacate';
const c = null;

(a || b) === 'banana'; // true
(a || b) === 'abacate'; // false
(a || c) === 'banana'; // true
(c || a) === 'banana'; // true
(c || a) === null; // false;

Substituindo statements por expressions

Bom, mas só vale a pena conhecer essas particularidades se pudermos colocar em prática de alguma forma, certo? Com certeza! Vamos imaginar o caso do Rei Arthur, lá do começo deste artigo. Como podemos aplicar estes conceitos a ele usando short-circuiting?

const knight = 'King Arthur';

knigth === 'King Arthur' && excalibur.remove() || excalibur.laugh();

Menos código, né? Mas aqui coloco uma ressalva: usar expressões desta maneira pode fazer com que o código se torne mais difícil de ler por uma pessoa iniciante em JavaScript.

Em contrapartida, esse iniciante, quando conseguir entender o que está acontecendo ali, terá uma carga de conhecimento maior do que se ele só pegasse códigos usando if...else durante sua carreira.

Mais sobre

1. Operadores Ternários

Estes conceitos também podem ser utilizados usando-se operadores ternários. O exemplo acima usando ternário ficaria da seguinte maneira:

const knight = 'King Arthur';

knight === 'King Arthur' ? excalibur.remove() : excalibur.laugh();

2. Leis de De Morgan

Óbvio que se usamos lógica booleana aqui, as Leis de De Morgan se aplicam. Só vou colocar aqui para lembrar o que aprendemos nas aulas na faculdade e depois esquecemos. É legal conhecer o nome dos princípios que usamos e como eles funcionam.

Os exemplos abaixo são equivalentes entre si:

!A && !B == !(A || B)

!A || !B == !(A && B)

Conclusão

As técnicas e conceitos que eu apresentei aqui são muito legais pra conhecermos as particularidades e como funcionam as coisas no JavaScript.

Porém, temos de levar em conta a senioridade da equipe de desenvolvimento em que trabalhamos, tomar cuidado para não cometermos erros de interpretação de código e até mesmo na escrita, de que nossas expressões serão evaluadas como pretendemos.

Eu acho que vale a pena usar em algumas situações, mas eu sempre digo: uma tecnologia, uma técnica, uma solução, nunca serão definitivas. O papel de um bom programador e de um bom analista é saber quando e onde usar cada tipo de tecnologia.

Se quiserem saber mais sobre o assunto, vejam estes artigos aqui, que são interessantíssimos, nos quais eu me baseei:

Se quiserem bater mais um papo me coloco à disposição sempre:

Abraços!