Sim, outro post sobre ECMAScript 2015 ou ES6
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.
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:
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:
Só que nem toda constante em ES6 é constante. Se você declarar um objeto como const, ainda poderá alterar suas propriedades:
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:
Utilizando destructuring pela atribuição básica de variáveis podemos fazer algo como:
Onde declaramos as três constantes recebendo os três primeiros valores de myArray.
Fazendo o mesmo 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:
Isso é interessante, pois nos possibilita trocar os valores dos arrays de maneira simples:
E 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:
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:
Poderia ser feito isso aqui:
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:
Agora, com a funcionalidade template literals ou template strings, podemos fazer assim:
E também poderia ser feito da seguinte maneira:
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:
Multi-line strings
Outra funcionalidade muito legal para trabalhar com strings foi o multi-line strings. Agora, ao invés de fazer isso aqui:
Ou:
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:
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:
Fora o acerto com o this, essa sintaxe para a declaração de funções é bem mais bonita:
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:
E caso você só utilize um parâmetro ainda pode omitir os parênteses que o envolvem:
Isso também deixa mais bonito o uso de callbacks não nomeados:
E se eu quisesse utilizar uma Arrow Function em uma IIFE, seria possível?
Sim!
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:
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:
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:
Já em ES6 podemos fazer:
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:
Em ES6 não precisamos usar function, podemos fazer o seguinte:
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:
E utilizamos assim:
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:
Com ES5 seria necessário utilizar o método call do objeto pai para fazer o mesmo:
Parâmetros Padrão
Em ES5 declaramos parâmetros padrão para nossas funções da seguinte maneira:
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:
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:
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.
Um exemplo melhor em ES5, retirado direto do artigo do Adrian Mejia:
Em ES6 temos o rest operator (ou rest parameters) que também carrega os argumentos recebidos:
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ânciasArray
, isso significa que métodos comosort
,map
,forEach
oupop
não podem ser aplicados diretamente;o objeto
arguments
possui a funcionalidade adicional de especificar ele mesmo (como a propriedadecallee
).
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:
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:
Agora conseguimos o mesmo comportamento acima com a seguinte sintaxe:
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:
Agora utilizando o spread operator para expandir o array na hora de adicioná-lo:
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:
Outro cenário do uso de Promises com ES5:
E agora 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:
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:
Você também pode exportar variáveis:
Importando um módulo
Para importar as funções e variáveis criadas acima você precisa utilizar a keyword import, da seguinte maneira:
Considerando que eu tivesse nomeado o arquivo com a função addTwo de adds.js.
Você pode importar as duas funções:
Ou importar as alguma função e as variáveis:
Ou pode importar tudo de uma vez:
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:
- https://udgwebdev.com/frontend-lindo-usando-babel-para-rodar-es6/
- https://tableless.com.br/es2015-babel-6-com-browserify-e-babelify/
- http://jamesknelson.com/using-es6-in-the-browser-with-babel-6-and-webpack/
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.
Leia mais artigos meus em:
Referências
- https://medium.com/@matheusml/o-guia-do-es6-tudo-que-voc%C3%AA-precisa-saber-8c287876325f#.ot5ad6ei8
- http://jsrocks.org/pt-br/2014/08/what-you-need-to-know-about-block-scope-let/
- http://jsrocks.org/pt-br/2014/10/arrow-functions-and-their-scope/
- http://adrianmejia.com/blog/2016/10/19/Overview-of-JavaScript-ES6-features-a-k-a-ECMAScript-6-and-ES2015
- https://davidwalsh.name/es6-generators
- https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Operators/Atribuicao_via_desestruturacao
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions
- https://developer.mozilla.org/pt-BR/docs/Web/JavaScript/Reference/Global_Objects/Promise