Front End

6 jul, 2018

JS Experience 2018 – Nada é por acaso no JS

Publicidade

Ontem, dia 05/07/2018, quinta-feira, aconteceu no Hotel Renaissance a segunda edição do JS Experience, o maior evento de JavaScript da cidade de São Paulo. O evento – organizado pelo competente e experiente time do iMasters – contou com uma série de palestras incríveis e eu tive a felicidade de poder participar do quadro do 7Masters que ocorreu no evento.

Em resumo, o 7Masters é um evento que acontece todo mês, onde um tema é escolhido e sete profissionais que trabalham com esta tecnologia são convidados para compartilhar sua experiência em uma palestra de sete minutos. É isso mesmo, apenas sete minutos.

Eu fui o primeiro a me apresentar e o tema escolhido foi “Nada é por acaso, nem os tipos no JS”. Para quem quiser os slides, estão disponíveis aqui.

Tudo tem uma explicação

Como já estamos cansados de saber, o JavaScript é uma linguagem de tipagem dinâmica. Isso significa que a sua engine consegue interpretar qual é tipo do dado que estamos passando sem a necessidade de especificar. Por exemplo, podemos ter uma mesma variável valor que pode assumir diversos tipos:

var valor = 1; // numero
valor = "1"; // string
valor = {}; // objeto
valor = []; // array
valor = () => {}; // função

// ... etc

E com isso, conseguimos fazer vários tipos de operações, como: soma, subtração, concatenação, comparação, atribuição. Até aí, sem segredos, certo? O “problema” começa quando tentamos fazer operações com tipos diferentes:

1 + '2'; // '3'
[] + []; // ''
true - true; // 0
[] + {}; // "[object Object]"
[] == 0; // true
91 - "1"; // 90

Estes resultados inusitados acabam sendo surpresas (na maior parte das vezes desagradáveis). No entanto, ao contrário do ditado popular de que o JavaScript é maluco, há uma razão lógica para que tudo isso aconteça. Para entender o que acontece, é necessário entender quatro conceitos:

  • Precedência
  • Associatividade
  • Coerção
  • Semelhança e Igualdade

Um caso muito simples em que isso pode ser aplicado é no seguinte código:

var b = 3 < 2 < 1;
console.log(b); // ?

Você sabe qual é o resultado desta expressão? Se você acha que é false, tenho más notícias: o resultado é true.

Engraçado, né? Mas veja só como a explicação é simples: para realizar esta operação, o JavaScript segue uma tabela de precedência e associatividade (que pode ser encontrada no MDN através deste link). Vamos procurar pelo operador “menos que” (less than).

Tabela de precedência e associatividade

Note as colunas da tabela. A primeira indica o grau de precedência, ou seja, quanto maior o número, maior é a precedência (ex: a adição precede a igualdade, visto que sua precedência é 13, enquanto a da igualdade é 10). A terceira coluna indica a associatividade. Em termos simples, é a ordem em que deve ser realizada a operação no caso de duas de mesmo nível de precedência.

No nosso caso, temos dois operadores iguais, logo, precisamos realizar na ordem left-to-right (esquerda para direita). Com isso, temos:

false < 1

Nesta etapa, entra o conceito de coerção de tipos. Como temos dois tipos distintos nos dois lados da balança, o JavaScript faz com que ambos estejam em um plano comum. Neste caso, o false é convertido para número com a função Number().

Number(false) < 1

Depois disso, o resultado da operação é bem intuitivo:

Number(false) < 1
0 < 1
true

Fez sentido? O mesmo equivale para quando somamos uma string e um número, por exemplo:

'1' + 2;
'1' + String(2);
'1' + '2'; // 12

E assim por diante. Para facilitar a nossa vida, o MDN também possui uma tabela de comparações entre os tipos para que possamos ter uma referência (você pode acessar através deste link).

E acredite ou não, estes conceitos são o suficiente para entender como o resultado da expressão abaixo resulta na string “fail”:

(![]+[])[+[]]+(![]+[])[+!+[]]+([![]]+[][[]])[+!+[]+[+[]]]+(![]+[])[!+[]+!+[]] // “fail”

Se você quiser mais resultados engraçados, dê uma olhada no WTFJS.

Conclusão

Para evitar confusões no código, há algumas precauções que podemos tomar:

  • Consultem as tabelas de coerção e precedência
  • Use os métodos Number(), String() e Boolean() para ver os resultados das coerções
  • Use o === nas comparações para evitar confusões (evita a coerção automática dos tipos)
  • Utilize tecnologias como o TypeScript

Referências