Sim, outro post sobre ECMAScript 2015 ou ES6

William Oliveira
Tech@Grupo ZAP
Published in
11 min readJan 12, 2017

--

Você já deve ter lido, assistido uma palestra ou ouvido um podcast sobre ECMAScript 2015, o ES6. ES6 já não é mais novidade. Ao contrário nós já deveríamos conhecer pelo menos o mínimo das mudanças no nosso querido JavaScript.

Nesse post não vou falar sobre o TC39 ou sobre a história do ECMAScript, se você quiser ler sobre isso da uma olhada aqui e aqui. Nesse texto vou tentar passar algumas coisas legais que aprendi sobre a linguagem nos últimos meses e fazer um compilado do que lí.

Você vai conseguir pegar boas referências aqui e de quebra pode conhecer algumas pessoas legais para seguir no Twitter e acompanhar suas atualizações para saber mais ainda sobre ES6 ou sobre as próximas versões do JS.

Se você quiser saber mais sobre o suporte a ES6 por parte dos navegadores, da uma olhada nesse link. Já se utiliza JavaScript no servidor, veja nesse outro link o suporte dado pelo Nodejs.

Nesse post vou falar sobre:

  • Declaração de valores, escopo e hoisting
  • Uma nova maneira de extrair dados de arrays e objetos
  • Trabalhando com Strings
  • Template literals
  • Multi-line strings
  • Funções
  • Múltiplos retornos em uma função
  • Generators e Iterators
  • Orientação a Objetos
  • Classes
  • Object Methods
  • Getters e Setters
  • Dados privados
  • Herança
  • Parâmetros Padrão
  • Um novo for: for.. of
  • Rest operator
  • Spread operator
  • Promises
  • Módulos
  • Criando seu módulo
  • Importando um módulo

E você pode testar os exemplos de código direto no seu navegador. — Eu acho que vai dar certo se você estiver usando o Google Chrome na versão mais atual.

Declaração de valores, escopo e hoisting

Algo que incomoda muito algumas pessoas que vem de outras linguagens sobre JavaScript é o escopo e o hoisting, mas isso mudou um pouco na nova versão da linguagem utilizando a declaração let e o escopo por blocos.

Acompanhe o exemplo de uma declaração de um iterador i em um for.

for utilizando var

Ao declararmos a variável i com var essa variável é hasteada e pula para fora do bloco onde foi declarada, utilizada e deveria ficar, o for. Acho que ninguém quer que isso aconteça.

Agora o mesmo exemplo utilizando let:

for utilizando let

Com o let, a variável i fica somente onde deveria estar e se você tentar acessar fora do bloco o JavaScript te retorna um erro informando que i não foi definida.

Outra forma de declarar os dados em sua aplicação é utilizando o const, sendo que essa declaração serve para (nada mais, nada menos que) valores constantes em sua aplicação.

Exemplo do uso de const:

Valores constantes não podem ser alterados

Só que nem toda constante em ES6 é constante. Se você declarar um objeto como const, ainda poderá alterar suas propriedades:

Utilizando const com objetos

Como deve ser claro pra você, utilize let em valores que podem ser alterados durante a execução do seu programa e const para valores constantes como o valor de PI, módulos importados, valores de configuração, etc.

Se você precisar que o objeto e suas propriedades sejam sempre o mesmo valor, então deve utilizar o Object.freeze():

Uma nova maneira de extrair dados de arrays e objetos

Com o uso de destructuring assignment é possível extrair valores de arrays e objetos de uma maneira simples.

Em ES5 podemos fazer o seguinte para extrair o primeiro e o terceiro valor de um array:

Extraindo dados de arrays com ES5

Utilizando destructuring pela atribuição básica de variáveis podemos fazer algo como:

Destructuring via atribuição básica de variáveis

Onde declaramos as três constantes recebendo os três primeiros valores de myArray.

Fazendo o mesmo via atribuição separada da declaração:

atribuição via atribuição separada da declaração

Onde declaramos as variáveis a e b antes de passar os valores vindos do array.

Poderíamos ter feito assim também:

Destructuring via atribuição separada da declaração utilizando um array declarado

Isso é interessante, pois nos possibilita trocar os valores dos arrays de maneira simples:

Alternando valores de arrays com destructuring

E com objetos:

Destructuring com objetos

Não se preocupe com a expressão “`Crazy people? ${crazy}`” agora, vou explicar sobre isso mais pra baixo.

Podemos ainda passar default values (que também vou explicar mais abaixo), tanto nos arrays quanto nos objetos, e caso algum valor não seja atribuído ele assume o valor padrão da seguinte maneira:

Destructuring com default parameters

Onde a recebeu o valor extraído (3) e b recebeu o valor padrão (2).

O Matheus Lima da um ótimo exemplo de como isso seria útil no import de libs em seu artigo sobre ES6:

Ao invés de fazer isso:

Exemplo do Matheus Lima sobre imports com destructuring

Poderia ser feito isso aqui:

Exemplo do Matheus Lima sobre imports com destructuring

Trabalhando com Strings

Algumas funcionalidades foram adicionadas ao ES6 para melhorar nosso trabalho com strings.

Template Literals

Essa foi a funcionalidade que eu achei mais linda para strings.

No ES5 o trabalho para concatenar variáveis a strings era o seguinte:

Concatenando strings e variáveis em ES5

Agora, com a funcionalidade template literals ou template strings, podemos fazer assim:

Concatenando strings e variáveis em ES6

E também poderia ser feito da seguinte maneira:

Concatenando strings e variáveis em ES6

Mas atente-se ao detalhe do uso de acento grave (`texto`) para a declaração da string e o cifrão junto com chaves (${}) com o valor que será substituído não o uso de aspas (“texto”) ou aspas simples (‘texto’).

Outra coisa interessante é que podemos passar expressões dentro dessas strings e elas serão executadas:

Executando expressões dentro das strings em ES6

Multi-line strings

Outra funcionalidade muito legal para trabalhar com strings foi o multi-line strings. Agora, ao invés de fazer isso aqui:

Um poema em ES5 via \n e +

Ou:

Um poema em ES5 via \n

Onde temos que utiliza a declaração de uma nova linha com \n, para criar strings de várias linhas (como o criação de templates ou esse poema) você pode fazer:

Um poema escrito com multi-line strings

Utilizando o acento grave, novamente, você pode escrever um texto, pular uma linha e continuar o texto sem o \n!

Funções

Não tem como falar de JavaScript sem citar funções e não tem como falar de ES6 sem comentar sobre as alterações que vieram nesse setor.

As Arrow Functions vieram para deixar o JavaScript mais bonito e resolver aquele probleminha com o this.

Em linguagens Orientadas a Objetos o this referencia a instância da classe. Já no JavaScript o this é definido na hora em que um objeto invoca uma função. O this é determinado pelo contexto de invocação da função.

O melhor artigo que eu já li sobre o this em JavaScript foi esse aqui do Tárcio Zemel. Se você nunca usou um .bind(), da uma olhada no artigo e depois volte aqui, pois vai clarear sua mente sobre o assunto.

Com as Arrow Functions o this referencia corretamente a “instância da classe”, como nesse exemplo retirado do artigo do Matheus:

O this lexico de arrow functions

Fora o acerto com o this, essa sintaxe para a declaração de funções é bem mais bonita:

Sintaxe de arrow functions

Onde definimos a função sum que recebe dois parâmetros (x, y) e retorna o valor de x + y.

A mesma função pode ser escrita em uma linha omitindo os parênteses e o return:

Arrow functions em uma linha

E caso você só utilize um parâmetro ainda pode omitir os parênteses que o envolvem:

Arrow functions omitindo os parênteses

Isso também deixa mais bonito o uso de callbacks não nomeados:

Exemplo um pouco maior do uso de arrow functions

E se eu quisesse utilizar uma Arrow Function em uma IIFE, seria possível?

Sim!

IIFE e arrow functions

Múltiplos retornos em uma função

Utilizando destructuring assignment podemos ter um return com duas saídas.

Normalmente podemos retornar um objeto ou um array e extrair os dados dessas estruturas, porém agora podemos utilizar o recurso de destructuring para extrair os dados com menos linhas de código.

Em ES5, podemos fazer o seguinte:

Múltiplos retornos em uma função em ES5

Onde criamos uma função que retorna um objeto e disso extraímos os valores para as novas variáveis.

Agora, o mesmo cenário utilizando ES6:

Múltiplos retornos em uma função em ES6 com destructuring

Generators e Iterators

Comum em outras linguagens de programação, agora o JavaScript também tem os Generators e Iterators.

Utilizando Generator você pode interromper o fluxo de uma função e voltar a execução dela depois partindo de onde parou. Tudo o que estiver acontecendo internamente ficará salvo para quando você voltar a execução.

Quando chamamos uma função “geradora” nós não a executamos de imediato. Um objeto iterator é retornado dessa função e esse objeto possui um método next() executando a função até a primeira expressão yield, que recolhe o valor que deve ser retornado como valor do iterator ou, utilizando yield*, delega outra função geradora.

Um exemplo de generator function:

A função geradora é definida com function* nomeDaFuncao() {}.

Agora se tentarmos executar essa função, olha o que acontece:

Se executarmos myGenerator.next() teremos como retorno o nosso objeto iterator com os valores:

Onde done é false até que não tenham mais valores a serem iterados e value é o valor atual no iterador que, no exemplo acima, é zero.

E se continuarmos executando next, continuaremos a receber o valor do próximo index.

Atente-se que para retornar o valor de value basta executar next().value.

No caso que exemplifiquei agora estamos utilizando um while(true), então esse index nunca vai retornar done: true, pois os itens não acabam.

Veja outro exemplo, agora com final:

Orientação a Objetos

No ES6 foram implementadas novas funcionalidades para Orientação a Objetos incluindo classes, constructors, getters e setters, extends e super.

Classes

Em ES5 conseguimos utilizar algo que simula a classes com Funções Construtoras:

Classe em ES5

Já em ES6 podemos fazer:

Classe em ES6

Onde class é a keyword que indica a classe que será declarada, constructor é o inicializador da instância quando executada, e criamos o método run utilizando a nova sintaxe de object methods.

Object Methods

Em ES5, para declarar métodos de um objeto poderíamos fazer:

Métodos de objetos em ES5

Em ES6 não precisamos usar function, podemos fazer o seguinte:

Object Methods em ES6

Getters e Setters

-Se você é Javeiro deve usar muito isso aqui.

Em ES6 temos uma nova sintaxe para criação de getters e setters nas Classes utilizando as keywords get e set. veja como fica a classe Car com getters e setters:

Getters e Setters em ES6

E utilizamos assim:

Utilizando o exemplo de getters e setters

Onde utilizei o setter set color para adicionar a cor da minha brasilia.

Dados privados

Com o uso das classes você deve se perguntar como manter dados públicos e dados privados em um objeto.

Como quase tudo em JavaScript, existe mais de uma maneira de se fazer isso! Você pode se aprofundar mais no assunto lendo esse capítulo do livro Exploring ES6 e escolhendo a melhor abordagem com base nos prós e contras de cada uma de acordo com a sua necessidade.

Por enquanto eu não encontrei em nenhum lugar sobre um padrão para os utilizar ou uma convenção.

Herança

Para trabalhar com herança em ES6 foram implementadas as keywords extends e super e podemos utilizá-las da seguinte maneira se continuarmos seguindo o exemplo da classe Car:

Herança em ES6

Com ES5 seria necessário utilizar o método call do objeto pai para fazer o mesmo:

Herança em ES5

Parâmetros Padrão

Em ES5 declaramos parâmetros padrão para nossas funções da seguinte maneira:

Parâmetros padrão em ES5

Onde dizemos que a variável x recebe o valor x recebido via parâmetro pela função example(x, y) ou a string “The x is the x” e o mesmo esquema para y.

Agora, em ES6, podemos fazer:

Parâmetros padrão em ES6

Um novo for: for.. Of

Agora, além de termos os métodos naturais for e forEach, temos um novo método para iterar sobre arrays o for… of. Podemos utiliza-lo da seguinte maneira:

Exemplo do uso de for… of

E serão impressos os itens do array.

Rest operator

Em ES5 podemos utilizar o arguments para pegar todos os argumentos recebidos em uma função e podermos trabalhar com eles.

Retornando arguments

Um exemplo melhor em ES5, retirado direto do artigo do Adrian Mejia:

Exemplo do uso de arguments do Adrian

Em ES6 temos o rest operator (ou rest parameters) que também carrega os argumentos recebidos:

Exemplo do uso de rest operator do Adrian

Onde pegamos os valores recebidos e adicionamos a uma variável chamada prams com a expressão …params.

Então por que utilizar rest operator e não arguments?

Há três diferenças principais entre rest parameters e os arguments objects:

rest parameters são os únicos que não foram atribuidos a um nome separado, enquanto os arguments object contêm todos os argumentos passados para a função;

o objeto arguments não é um array, enquanto rest parameters são instâncias Array, isso significa que métodos como sort, map, forEach ou pop não podem ser aplicados diretamente;

o objeto arguments possui a funcionalidade adicional de especificar ele mesmo (como a propriedade callee).

Mozilla > https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Functions/rest_parameters

Spread operator

É comum utilizar um array de argumentos em uma função e utilizar os seus valores internamente (ou somente retornar o array) e para isso é preciso utilizar o .apply():

Exemplificando: tendo uma função chamada returnArgs que irá retornar o arguments. Se passarmos um array como parâmetro na invocação dessa função, acontece o seguinte:

Retornando arguments em ES5 sem apply

Porém quero retornar os itens do que eu passei por parâmetro, então é necessário escrever o mesmo código da seguinte maneira:

Retornando arguments em ES5 com apply

Agora conseguimos o mesmo comportamento acima com a seguinte sintaxe:

Retornando os argumentos expandindo-os com rest operator

O spread operator expande o array na chamada de returnArgs e por isso temos o retorno esperado.

Outro exemplo legal do que podemos fazer com spread operator é a adicionar um array ao final de outro array.

Um exemplo em ES5 do que acontece se utilizarmos .push() para adicionar um array ao final de outro:

Adicionando um array no final de outro array

Agora utilizando o spread operator para expandir o array na hora de adicioná-lo:

Adicionando um array no final de outro array expandindo com rest operator

Promises

Se você não conhece Promises em JavaScript, talvez esteja praticando algo bem conhecido na comunidade que é o Callback Hell para trabalhar com programação assíncrona!

Expressão essa que é tão importante (ou incomoda) que existe um site só para explicar sobre: http://callbackhell.com/

Agora as Promises são nativas do JavaScript, não sendo necessário o uso de jQuery, Angular ou qualquer outra lib ou framework para poder utilizá-las de uma maneira mais bonita.

Um exemplo utilizando Promises Nativas para buscar dados via XHR retirado do livro Exploring ES6:

Uma Promise com XHR em ES6

Outro cenário do uso de Promises com ES5:

Uma promise em ES5

E agora com ES6:

A Promise com ES6

Módulos

Esses serão os únicos exemplos que não funcionarão no seu navegador a não ser que utilize um module loader ou um transpiler.

Modularização é muito importante para organizarmos nossas aplicações.

No navegador, não existe uma opção nativa para isso a não ser a utilização de alguns padrões de código como o Module Pattern. Também é possível o uso de bibliotecas como o RequireJS.

Em ES6 foi implementada uma nova maneira de se trabalhar com módulos nativa.

Criando seu módulo

A sintaxe para criar seus módulos é a seguinte:

Exportando uma função em um módulo

Utilizando a keyword export você exporta a função addTwo para ser utilizada em outros locais em sua aplicação.

E pode criar várias funções e as exportar:

Exportando várias funções em um módulo

Você também pode exportar variáveis:

Exportando uma variável em um módulo

Importando um módulo

Para importar as funções e variáveis criadas acima você precisa utilizar a keyword import, da seguinte maneira:

Importando um módulo

Considerando que eu tivesse nomeado o arquivo com a função addTwo de adds.js.

Você pode importar as duas funções:

Importando dois métodos/funções de um módulo

Ou importar as alguma função e as variáveis:

Importando métodos/funções e variáveis de um módulo

Ou pode importar tudo de uma vez:

Importando tudo de um módulo

Nesse exemplo criado um objeto myAdds que receberá o conteúdo do módulo.

Conclusão

O uso de ES6 deixa a sintaxe do JavaScript muito mais elegante e de fácil entendimento fora suas novas funcionalidades que adicionaram mais poder a linguagem.

Frameworks novos ou antigos já estão nascendo ou sendo migrados para essa nova sintaxe por isso recomendo fortemente que você tire um tempo para estudar mais sobre ela.

Claro que nem todos os navegadores dão um bom suporte a ES6 e mesmo que todos o fizessem não conseguimos garantir que os usuários terão em suas máquinas sempre o navegador mais atualizado. Portanto é recomendado o uso de transpiladores como o Babel para traduzir seu código ES6 para ES5.

Para saber mais sobre o Babel, da uma olhada nesses links:

Também podemos transpilar código ES5 para ES6 utilizando o Lebab (sim, Babel ao contrário).

Para escrevermos um código melhor em ES6, podemos seguir o guia Clean Code JavaScript do Ryan McDermott e, se você utiliza Angular 1.x, pode seguir o Style Guide do Todd Motto que (se você for do futuro, na época desse post) usa Angular 1.6 com ES6.

Eai, você já está utilizando ES6 em produção? Comente aqui como está sendo sua experiência e quais ferramentas está utilizando para suportar os navegadores. Se souber de mais alguma coisa interessante sobre ES6, comente aqui também.

Compartilhe esse artigo nas redes sociais, manda pra família, lê isso em voz alta pro seu cachorro aprender JS também. Espalhe a palavra.

--

--

William Oliveira
Tech@Grupo ZAP

Engenheiro de software frontend, escritor do livro O Universo da Programação (http://bit.ly/universo-da-programacao), periférico e bissexual