As melhores features do ES6, ES7 e ES8

JavaScript 18 de Out de 2017

Há cerca de 3 anos o Javascript resolveu crescer e começar a dar foco em suas novas atualizações liberando funcionalidades que outras linguagens mais maduras já haviam liberado.

Nesse post vou dar enfoque nas features que mais me chamaram atenção em todo processo e que me ajudam muito a tornar o processo de desenvolvimento mais produtivo e menos verboso.

Classes

A partir do ES6 é possível declararmos classes nativas com Javascript da seguinte forma:

class App {
  constructor() {
  }

  customMethod() {
    console.log('Hey');
  }
}

Além disso, podemos herdar outras classes utilizando o extends logo após o nome da classe:

class App extends AnotherClass {}

Outra funcionalidade bem legal é podermos utilizar métodos estáticos nas classes:

class App {
  static log() {
    console.log('Hey');
  }
}

App.log();

Constantes e variáveis de escopo

Se, assim como eu, você programa em Javascript há um tempo sabe que antigamente utilizámos var em todo canto, independente se a variável precisava se comportar dentro de um escopo determinado ou seu valor nunca mudasse.

A partir do ES6 temos acesso às constantes e variáveis de escopo.

Constantes são utilizadas quando seu valor não será reatribuído em nenhum momento. Ainda assim, as constantes podem sofrer mutação conforme no exemplo a seguir:

const a = 1;

a = 2; // Não funciona (Reatribuição)

const user = { nome: 'Diego' };

user.nome = 'Diego Fernandes'; // Funciona (Mutação)

Eu gosto muito de utilizar constantes toda vez que declaro uma variável que não sofrerá alterações (inclusive é uma boa prática fazer isso).

Variáveis de escopo são variáveis definidas pelo prefixo let que existirão apenas dentro do bloco associado à ela e não ficarão visíveis externamente, por exemplo:

function sum(a, b) {
  let c = a + b;
  console.log(c);
}

sum(1, 2); // 3
console.log(c); // undefined

Outro exemplo:

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

console.log(i); // undefined

Arrow functions

A Arrow Function é, se não a mais, uma das melhores features do ES6. Elas permitem que você escreva funções sem perder o escopo atual do this e isso é incrível, principalmente quando se trabalha com bibliotecas baseadas em classe como o React. Alguns exemplos de utilização da Arrow Funcion:

const numbers = [1, 2, 3, 4, 5, 6];

// Sem arrow function
const plusOneNumbers = numbers.map(function(n) {
  return n + 1;
});

Veja que sem Arrow Functions, precisamos declarar uma function dentro da iteração do map que percorre todos itens do array e os modifica somando 1 ao número. Vamos ver como esse exemplo ficaria com a nova especificação:

// Com arrow function (versão 1)
const plusOneNumbers = numbers.map((n) => {
  return n + 1;
});

Até aí tudo bem, só removemos o termo function e adicionamos os caracteres => mas nada mudou muito, né? O legal aqui é que se a função recebe apenas um parâmetro podemos omitir os parênteses por volta deles e, além disso, se a função possui apenas um retorno, podemos informar esse valor diretamente após o=>, vamos ver como fica:

// Com arrow function (versão 2)
const plusOneNumbers = numbers.map(n => n + 1);

Bem melhor, não é? Nosso código fica muito mais legível dessa forma!

Escopo

Como mencionei anteriormente, utilizando a variável this nas Arrow Functions retorna o escopo correto e não é necessário realizar o bind como era feito antigamente:

class App {
  log() {
    console.log('Funcionou!');
  }

  static init() {
    this.log();
  }
}

App.init(); // Erro :(

Nesse caso, como não estamos utilizando Arrow Functions no método da classe, o this está referenciando o escopo do método e não da classe. Se utilizarmos a nova especificação tudo irá funcionar.

class App {
  ...

  static init = () => {
    this.log();
  }
}

App.init(); // Funcionou!

Valor padrão no parâmetro

A partir do ES6 podemos definir valores padrão para os parâmetros de uma função da seguinte forma:

function sum(a = 1, b = 2) {
  console.log(a + b);
}

sum(4); // 6

Desestruturação

Acho que uma das features mais legais do ES6 é a possibilidade de desestruturar objetos e vetores. Vejamos como isso funciona:

const user = {
  nome: 'Diego',
  empresa: {
    nome: 'RocketSeat',
    site: 'www.rocketseat.com.br'
  }
};

let { nome } = user;

console.log(nome); // Diego

let { empresa: { site } } = user;

console.log(site); // www.rocketseat.com.br

Podemos utilizar essa sintaxe em funções inclusive:

function showName({ nome }) {
  console.log(nome);
}

const user = { nome: 'Diego' };

showName(user); // Diego

Veja que estou utilizando a sintaxe de chaves antes do =, sim!! Dessa forma conseguimos transformar as propriedades de um objeto em uma variável do Javascript apenas repassando o nome dessas propriedades dentro do objeto que antecede o símbolo de igual. Ah, isso funciona também para vetores:

const numbers = [1, 2, 3];

let [a, b, c] = numbers;

console.log(a); // 1
console.log(b); // 2
console.log(c); // 3

Operadores Rest e Spread

Os operadores Rest e Spread são formas de recuperarmos o conteúdo de objetos, vetores e parâmetros de funções de forma rápida. Sério, muita gente subestima a funcionalidade desses caras mas depois que aprendi a utilizá-los se tornaram uma mão na roda pra mim.

Operador Rest

const numbers = [1, 2, 3, 4, 5];

let [a, b, ...c] = numbers;

console.log(a); // 1
console.log(b); // 2
console.log(c); // [3, 4, 5]

Vejam que utilizando o ... consigo recuperar o “resto” dos números contidos no vetor e isso é magnífico.

Operador Spread

Possui a mesma sintaxe do Rest, mas é utilizado para “copiar” o conteúdo de um vetor ou objeto para outra estrutura de dados:

const user = {
  nome: 'Diego',
  usuario: 'diego3g',
  empresa: 'RocketSeat'
};

const newUser = { ...user, usuario: 'diegosf' };

console.log(newUser); 
// { nome: 'Diego', usuario: 'diegosf', empresa: 'RocketSeat' }

Repito novamente, não subestime o poder desses operadores, eles podem parecer um pouco chatos e estranhos, mas ajudam muito na produtividade.

Template Literals (variáveis em strings)

No ES6 é possível repassar variáveis para strings utilizando, ao invés de aspas simples ou duplas, apóstrofo e da seguinte forma:

const a = 3;

console.log(`O número é ${a}`);

Note que por volta da variável é necessário adicionar ${}.

Sintaxe curta de objetos

Muitas vezes nós definimos uma propriedade no objeto que possui o mesmo nome que seu valor, nesse caso, podemos resumir da seguinte forma:

const nome = 'Diego';

const user = {
  nome // Mesma coisa que nome: nome
};

console.log(user.nome); // Diego

ps.: Isso é muito legal!

Importação/exportação

Acho que a feature mais utilizada em qualquer projeto Javascript atualmente. A impotação e exportação de módulos permite a utilização de funções, variáveis e classes de um arquivo em outro, vamos entender como isso funciona:

// App.js

export function soma(a, b) {
  return a + b;
}

export const user = {
  nome: 'Diego',
  empresa: 'RocketSeat'
};

export default class App {
  static log() {
    console.log('Hey');
  }
}

Nesse primeiro arquivo estamos exportando uma função soma, um objeto user e uma classe App. Note que na última exportação estamos passando a palavra default e dessa forma estamos sinalizando que se não informamos a esse arquivo o quê estamos importando, por padrão ele deve retornar essa classe. Vamos ver como ficaria as importações:

// OutroArquivo.js

import App from 'App'; // Podemos omitir o .js aqui

App.log(); // Hey
soma(1, 2); // Erro: undefined function soma

Note que ele importou apenas a classe App e nada mais. Para importarmos variáveis, objetos ou classes que não estão com a notação default podemos utilizar a desestruturação aprendida anteriormente:

// OutroArquivo.js

import { soma, user } from 'App';

soma(1, 3); // 4
console.log(user.nome); // Diego

Outra forma de importar todas exportações sem a notação default de um arquivo é utilizando o wildcard *:

// OutroArquivo.js

import * as helpers from 'App';

helpers.soma(1, 3); // 4
console.log(helpers.user.nome); // Diego

Ainda podemos unir a importação do módulo default e das outras exportações em um único import:

import App, { soma, user } from 'App';

Async/await

Acho que a funcionalidade que mais me brilhou os olhos no ES8 foi a possibilidade de trabalhar com código assíncrono sem a antiga sintaxe de .then ou .catch. Vejamos como era feita uma requisição assíncrona antigamente:

function request() {
  Api.get('http://api.com')
    .then(response => { console.log(response) })
    .catch(err => { console.log(err) });
}

Agora com a nova sintaxe do async/await, podemos obter o mesmo resultado da seguinte forma:

async function request() {
  const response = await Api.get('http://api.com');
  console.log(response);
}

Note o uso da notação async junto à função e do prefixo await antes da função que retorna uma Promise. Ainda assim, se precisarmos ouvir o .catch podemos envolver nosso código em um try/catch:

async function request() {
  try {
    const response = await Api.get('http://api.com');
    console.log(response);
  } catch (err) {
    console.log(err);
  }
}

Você ainda está por aí?

Caramba, você leu tudo isso? Fico feliz. Eu me lembro da época que comecei a estudar as novas features da ECMAScript, era uma sensação igual daquele meme “Eu to feliz e puto”, ao mesmo tempo que é muito legal, é muita coisa e dificilmente eu conseguia encontrar conteúdo de qualidade para entender rapidamente.

Mas o que eu posso dizer, depois de aprender e dominar essas novas especificações, é que ajuda muito, cada funcionalidade acima me auxiliou a evoluir meu código e aumentar minha produtividade.

Se você gostou do post deixa um comentário aí sobre o que você já utiliza ou o que pretende utilizar pra trocarmos uma ideia 🤘🏻

Marcadores

Diego Fernandes

Programador full-stack, apaixonado pelas melhores tecnologias de desenvolvimento back-end, front-end e mobile, é co-fundador e CTO na Rocketseat.