JavaScript #2 - ECMAScript 6

Publicado por Andrey Luiz no dia dev

ES6

Este é o segundo post da série que irá falar sobre JavaScript. No post anterior eu apresentei um contexto histórico do surgimento da linguagem e como ela evoluiu até agora.

Nesse post abordaremos a versão 6 da ECMAScript dando alguns exemplos do que mudou e o que chegou de novo. Se você está em dúvida sobre o histórico da versão 6, não deixe de ler o post anterior da série.

Por que a versão 6?

Quando comecei a escrever a série eu tinha a intenção de abordar a versão 5 da linguagem, que é a versão mais usada atualmente. Porém esse cenário está mudando. Existem inúmeros projetos novos que estão sendo feitos usando ES 6. Além disso, eu apresentei um seminário sobre essa versão e gostei muito da participação dos presentes.

Então resolvi mudar o escopo e abordar a versão 6 para que essa série sirva de referência e para que tenha um valor didático mais de acordo com os dias de hoje e os que estão por vir.

De onde vem a ECMAScript 6?

Se você leu o post anterior da série provavelmente sabe que a ES 6 deriva indiretamente da versão 4. A ES 4 foi abandonada em Julho de 2008 porque trazia um pacote de features enorme. Isso tornaria a aceitação da nova versão um pouco difícil, visto que a mudança seria muito disruptiva. A sintaxe mudou, as funções mudaram, as classes chegaram, enfim. Tudo o que JavaScript não tinha chegou já na versão 4.

O TC-39 decidiu abandonar a ES 4, criar um patch para a versão 3 que fizesse mais que a 3, mas menos que a 4 e dividir o enorme pacote de features em duas versões, que viriam a se tornar as versões 6 e 7. Essas novas features foram propostas já em 2008, porém só ficaram disponíveis em Junho de 2015, quanto o TC-39 aprovou a ECMAScript 6.

O que há de novo?

A lista de novas features é enorme. Por isso vou citar apenas as mais importantes e mais esperadas.

Classes e herança

As classes, juntamente com a herança, vieram para trazer o conceito de orientação a objeto mais ao pé da definição da linguagem. Essa definição foi herdada puramente do Java.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Animal {
  constructor(name) {
    this.name = name;
  }
  walk() {
    console.log(`${this.name} is walking.`);
  }
}

class Dog extends Animal {
  bark() {
    console.log(`${this.name} says 'Woof'!`);
  }
}

var dog = new Dog('Lorkar');
dog.walk();
// > Lorkar is walking.
dog.bark();
// > Lorkar says 'Woof'!

Parâmetros default

Foi herdada do Python e do Perl e fazia uma enorme falta na linguagem.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Dog extends Animal {
  bark(msg='Woof') {
    console.log(`${this.name} says '${msg}'!`);
  }
}

var dog = new Dog('Lorkar');
dog.walk();
// > Lorkar is walking.
dog.bark();
// > Lorkar says 'Woof'!
dog.bark('Ouch');
// > Lorkar says 'Ouch'!

Geradores (generators)

Os geradores (generators) são como funções que pausam enquanto retornam um valor que pode ter sido previamente processado. Se o generator for chamado novamente, ele resume a execução de onde parou, preservando todo o escopo.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function* range(start, end, step) {
    while (start < end) {
        yield start;
        start += step;
    }
}

for (let i of range(0, 10, 2)) {
    console.log(i);
}

// > 0
// > 2
// > 4
// > 6
// > 8

Promises

Já presentes em Frameworks como o Angular, as promises vem nativas na ES 6.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Dog extends Animal {
  bark() {
    return new Promise(function (req, rej) {
      setTimeout(req, 1000);
    });
  }
}

function barkHandler() {
  console.log('Woof!');
}

var dog = new Dog('Lorkar');

dog.bark().then(barkHandler);
// > Woof! after 1 sec

Template literals

Funcionalidade clássica herdada do Perl e do Python.

1
2
3
4
5
class Dog extends Animal {
  bark(msg='Woof') {
    console.log(`${this.name} says ${msg}!`);
  }
}

Arrow functions

A redução sintática das funções anônimas. Além do tamanho, elas possuem outra característica muito interessante. Elas preservam o escopo do objeto onde estão declaradas. As funções normais do JavaScript tem escopo próprio e por isso o this aponta para a própria função e não para o objeto.

1
2
3
4
5
6
7
let square = x => x * x;
let add = (a, b) => a + b;
let pi = () => 3.1415;

console.log(square(5));
console.log(add(3, 4));
console.log(pi());

Escopo de bloco

A ES 5 é famosa por não prover escopo às variáveis em blocos. Somente dentro de funções isso acontecia. Isso fazia com que o programador tivesse que inventar formas de contornar os problemas que isso causava. Declarando as variáveis com let, o escopo é preservado no bloco onde a variável é declarada, não importando se é um if, um for ou uma função. Se declaradas com var, as variáveis permanecem com o comportamento antigo.

1
2
3
4
5
6
7
8
9
10
11
12
13
var x = 0;

for (let i = 0; i < 10; i++) {
    x += 10;
}

try {
    console.log(i);
} catch(e) {
    console.log(
        'i does not exist here!'
    );
}

Atribuição desestruturada

Açúcar sintático para extração de atributos internos a um Array ou Objeto.

1
2
3
4
let [one, two] = [1, 2];
let {three, four} = {three: 3, four:  4};

console.log(one, two, three, four);

Iteradores

Semelhante ao in já usado em objetos, mas para uso em Arrays, Sets e Maps.

1
2
3
4
5
6
7
8
let arr = [1, 2, 3, 4, 5];
let sum = 0;

for (let v of arr) {
  sum += v;
}

console.log('1 + 2 + 3 + 4 + 5 =', sum);

Spread Operators

Açúcar sintático que estrutura ou desestrutura arrays para facilitar a passagem de parâmetros e evitar o uso do arguments em funções, por exemplo. O React vai ainda mais longe e possibilita que essa feature seja usada para desestruturar objetos.

1
2
3
4
5
6
7
function add(a, b) {
  return a + b;
}

let nums = [5, 4];

console.log(add(...nums));

Ainda não está totalmente suportado

A definição oficial do TC-39 a respeito da ES 6 foi feita em Junho de 2015. Por isso os navegadores atuais em sua maioria não suportam totalmente a ES 6. O Google Chrome 53 possui uma compatibilidade de 97%. Portanto é possível brincar com ES 6 no console do Chrome sem se preocupar com algo que não seja suportado.

Existe uma tabela de compatibilidade bastante confiável que mantém atualizadas as especificações da ES 6, mas não só dessa versão, como da 7 entre outras. Observe que o Google Chrome, o Microsoft Edge (sim, o Edge!) e o Node 6.5 são as aplicações com maior compatibilidade.

Tabela de Compatibilidade ECMAScript 6

Como posso usar ES 6 hoje?

A resposta é: Babel!

O Babel é um compilador JavaScript. Em algumas fontes é citada a palavra “transpiler”. Essa palavra não tem uma tradução clara para o português, mas poderiamos chamar de transpilador, ou transcritor. Então podemos reformular que o Babel transcreve código JavaScript. Esse debate se deu entre o Leonardo Fin e eu, afim de encontrar uma palavra que se adequasse à tradução.

Transcrevendo o seu código ES 6 com o Babel, você terá como resultado um código ES 5. Essa é a melhor forma de usar a ES 6 hoje sem se preocupar com o ambiente onde seu código está rodando. Em ambientes isolados, e garantindo que você rode num compilador que suporte ES 6, o Babel não é necessário. Um exemplo disso é o Node 6.x.

Como fica o código ES 6 transcrito para ES 5?

Aqui vão alguns exemplos do que o Babel faz para simular em ES 5 o comportamento das funcionalidades da ES 6. São dois exemplos simples, apenas para mostrar que o comportamento é totalmente reproduzível com a versão 5.

Repare que todos os códigos transcritos usam o modo estrito. O modo estrito é habilitado por default na ES 6.

Escopo de bloco

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// ES 6
var x = 0;

for (let i = 0; i < 10; i++) {
    x += 10;
}

try {
  console.log(i);
} catch(e) {
  console.log(
      'i não existe aqui!'
  );
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Transcrito para ES 5
'use strict';

var x = 0;

for (var _i = 0; _i < 10; _i++) {
  x += 10;
}

try {
  console.log(i);
} catch (e) {
  console.log('i não existe aqui!');
}

Arrow functions

1
2
3
4
5
6
7
8
// ES 6
let square = x => x * x;
let add = (a, b) => a + b;
let pi = () => 3.1415;

console.log(square(5));
console.log(add(3, 4));
console.log(pi());
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//Transcrito para ES 5
"use strict";

var square = function square(x) {
  return x * x;
};
var add = function add(a, b) {
  return a + b;
};
var pi = function pi() {
  return 3.1415;
};

console.log(square(5));
console.log(add(3, 4));
console.log(pi());

Se tiver curiosidade sobre como ficam outros códigos transcritos pelo Babel, experimente pegar o código de um dos exemplos acima e reproduzir no Try it out do Babel.

O Babel usa presets para fazer a transcrição. Para transcrever código em ES 6, o Babel usa o preset es2015. Para ES 7 e React, por exemplo, pode-se usar os presets stage-2 e react. Isso torna o Babel bastante extensível, de forma que é possível rodar JavaScript nas últimas especificações.

Coisas fáceis de se fazer com ES 6 que eram horríveis de se fazer com ES 5

Aqui vão dois exemplos de coisas simples de se fazer com ES 6, que rendiam um verdadeiro drible na linguagem (e consequentemente bastante código) com ES 5.

Classes

Claro, o melhor exemplo! Classes não existem em ES 5. Para simular esse comportamento, declarava-se uma função imediata (uma closure auto-executável) para servir de inicializador e então era declarada uma função construtora que recebia funções e atributos no prototype. Veja o exemplo em ES 5:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// ES 5
// Declarando

var Animal = (function () {

  function Animal(name) {
      // Função construtura
      this.name = name;
  }

  Animal.prototype.walk = function walk() {
      console.log(this.name + ' is walking!');
  }

  return Animal; // Esse é o animal do closure. Não o de fora.


}()) // <= Auto executável

// Usando

var animal = new Animal('Lorkar');
animal.walk(); // > 'Lorkar is walking!'

O equivalente em ES 6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// ES 6
// Declarando

class Animal {
  constructor(name) {
      this.name = name;
  }
  walk() {
      console.log(`${this.name} is walking!`);
  }
}

// Usando

let animal = new Animal('Lorkar');
animal.walk(); // > 'Lorkar is walking!'

Perceba que a forma de usar é exatamente igual. Mas a declaração é assustadoramente diferente. E além disso conseguimos tirar proveito de um template literal na frase da função walk. A ES 6 veio com as classes para realmente facilitar as coisas.

Não vou expor aqui um exemplo com herança pois o código em ES 5 é muito extenso. Uma verdadeira salada de código para fazer algo que em ES 6 se faz com um class Animal extends MyObject.

Escopo de bloco

Em ES 5 as variáveis não possuem escopo de bloco. A única coisa que dá escopo às variáveis são as funções. Por isso, era comum ver blocos for englobados por uma função imediata para proteger o escopo da variável. Veja o exemplo em ES 5.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// ES 5
for (var i = 0; i < 10; i++) {
  console.log(i);
}

// > 0
// > ...
// > 9

console.log(i); // > 9

(function () {
  for (var i = 0; i < 10; i++) {
      console.log(i);
  }
}())

// > 0
// > ...
// > 9

console.log(i); // > Uncaught ReferenceError: i is not defined

O equivalente em ES 6:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// ES 6
for (var i = 0; i < 10; i++) {
  console.log(i);
}

// > 0
// > ...
// > 9

console.log(i); // > 9


for (let i = 0; i < 10; i++) {
  console.log(i);
}

// > 0
// > ...
// > 9

console.log(i); // > Uncaught ReferenceError: i is not defined

Perceba o uso do let. Se declaradas as variáveis com let, o escopo será preservado dentro do bloco {}.

E na RD?

Na Resultados Digitais ainda predomina a ES 5, pois os projetos front-end não possuem um framework ou biblioteca (além das leves, como jQuery).

Recentemente foi levantada uma discussão sobre a adoção de um framework ou biblioteca. Dessa discussão, o Ember foi escolhido para que alguns projetos fossem feitos. Esse framework, em sua segunda versão, utiliza a ES 6 por padrão. Por causa disso, o ambiente de desenvolvimento é todo baseado no Node.js. Ferramentas citadas nesse post são usadas no processo. O Babel é um exemplo.

Estamos tentando cada vez mais adotar o uso da ES 6 para aumentar a confiança e facilitar a manutenção. Além disso, por ser uma versão mais bem trabalhada do JavaScript, ganha-se na redução de padrões de uso do código (boilerplate) e no uso excessivo de design patterns para contrapor as limitações da linguagem.

Conclusão

A ES 6 veio para arrumar a casa na questão de sintaxe e de features. O que era um pesadelo de se fazer em ES 5 se tornou simples de se fazer em ES 6, como visto nos dois exemplos acima.

Além disso, as classes finalmente chegaram e deram ainda mais poder à linguagem, sem lançar mão das funções, que ganharam as arrow functions para deixá-las ainda mais poderosas.

Gostou da ES 6? Já desenvolve usando a nova tecnologia? Conte-nos suas experiências nos comentários.

Andrey Luiz

Andrey Luiz

Full Stack Developer

Comentários