https://www.udemy.com/entendendo-o-ecmascript-6/learn/v4/content
- Introdução
- Ferramentas
- Métodos Auxiliares
- Iteradores e Iteráveis
- For...of
- Novas estruturas de dados
- Const e Let
- Template Strings
- Arrow Functions
- Melhorias em objetos literais
- Valores Padrões
- Operadores Rest e Spread
- Desestruturamento
- Classes
- Módulos
- Funções Geradoras
- Promises
- Conclusão
Os exercícios práticos estão disponíveis no GitHub.:
ECMAScript x JavaScript
O ECMAScript (ES) é a especificação da linguagem de script que o JavaScript implementa. Isto é, a descrição de uma linguagem de script, sendo padronizado pela Ecma International — associação criada em 1961 dedicada à padronização de sistemas de informação e comunicação — na especificação ECMA-262.
A especificação é definida pelo ECMA Technical Comittee 39 (TC39), comitê formado por especialistas de grandes companhias da área de tecnologia, tais como: Apple, Google, Microsoft e Mozilla. Este comitê se reúne a cada dois meses, normalmente na Califórnia, para discutir o futuro da especificação. As datas das reuniões, assim como trechos delas, também estão disponíveis online no site oficial.
O ECMAScript é suportado por uma diversidade de aplicações, principalmente navegadores, em que são implementados pela linguagem JavaScript. Muitas das implementações adicionam extensões específicas a linguagem, como é o caso do JScript da Microsoft. Isso significa que aplicações escritas em uma implementação podem ser incompatíveis com outras. É sempre preciso estar atento a isto.
A partir da versão ES6, será adotado o versionamento por ano e não mais por número. É por isso que em muitos lugares encontramos o ES6 como ECMA2015 ou ES2015. São a mesma coisa. Esta nova atitude se deve ao fato da pretensão de termos uma nova atualização da especificação a cada ano.
Babel
Além do compilador online do Babel, existem outras excelentes ferramentas gratuitas disponíveis na internet que podem ser usadas livremente para acompanhar e testar os códigos que serão apresentados neste cruso. A única exceção é a do capítulo Módulos, pois nele é necessário dividir o código em vários arquivos para ver os efeitos da modularização.
Algumas outras ferramentas gratuitas e online são:
- repl.it
- JS Bin
- ES6 Fiddle
- ES6 Console
Referências:
Site Oficial da ECMA Internacional - http://www.ecmascript.org/index.php Especificação ES6 - http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf ECMA Compatibility Table - https://kangax.github.io/compat-table/es6/ Repositório do TC39 no GitHub - https://github.com/tc39/ecma262 Site Oficial do Babel - https://babeljs.io/
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>ECMAScript 6</title>
</head>
<body>
<script>
var frutas = ["abacaxi", "maça", "uva"];
for(var i = 0; i < frutas.length ; i++){
console.log(frutas[i]);
}
</script>
</body>
</html>
O maior problema com esta abordagem é que é impossível saber qual o objetivo do corpo da iteração sem ver sua implementação.
Os métodos auxiliares nos permite iterar um array de forma muito mais clara e simples. Neste capítulo, vimos 5 delas: forEach, map, filter, find, every, some e o reduce. Apesar de terem uma sintaxe muito parecida, cada um tem uma função bem específica. Abaixo, segue um exemplo de como utilizá-las:
var nomes = ['maria', 'josé', 'luciana'];
for(var i = 0; i < nomes.length ; i++){
console.log(nomes[i]);
}
console.log('------------------');
nomes.forEach(function (nome) {
console.log(nome);
});
Repare no que aconteceu. Dentro do forEach , passamos uma função anônima de retorno, que costumamos chamar de função de callback . Ela é executada para cada elemento dentro da lista. A cada iteração, o valor da lista é atribuído à variável passada como parâmetro no callback — no nosso caso, a variável nome.
Entretanto, note que a função de callback não precisa necessariamente ser anônima. Podemos defini-la antes e atribuí-la a uma variável para passá-la como parâmetro ao forEach :
var nomes = ['maria', 'josé', 'luciana'];
function imprimeNome(nome){
console.log(nome);
}
nomes.forEach(imprimeNome);
Isso acontece exatamente porque os elementos processados pelo forEach são determinados antes da primeira invocação da função de callback . Entretanto, isso não quer dizer que os valores não foram adicionados à lista. Ao adicionar um segundo console.log ao final do código para exibir a lista, notamos que a RedeTV foi adicionada várias vezes ao nosso Array . Uma cópia para cada iteração:
var canais = ['globo','sbt', 'record'];
canais.forEach(function (canal) {
console.log(canal);
canais.push('REDE TV'); //este item será ignorado
});
console.log(canais);
Utilizamos para quando precisamos passar por todos os nossos itens de um Array.
Exemplo: exibir todos os seus itens no console.
var numeros = [1,2,3,4,5];
numeros.forEach(function(numero){
console.log(numero);
});
// saída
// 1
// 2
// 3
// 4
// 5
O método map é muito útil quando precisamos não somente passar por todos os elementos de um Array , mas também modificá-los.
var numeros = [1,2,3];
var dobro = [];
numeros.forEach(function (numero) {
dobro.push(numero * 2);
});
console.log(dobro);
var numeros = [1,2,3];
var dobro = numeros.map(function (numero) {
return numero * 2;
});
console.log(dobro);
console.log(numeros);
O map executa a função de callback recebida por parâmetro para cada elemento iterado de numeros e constrói um novo Array com base nos retornos de cada uma das chamadas. Como o map nos devolve uma outra instância de Array , a lista original nunca é realmente modificada, o que mantém sua integridade.
Como o próprio nome já pode induzir, este método é deve ser utilizado quando temos a necessidade de filtrar nossa lista de acordo com algum critério.
var alunos = [
{nome:'José', idade:15},
{nome:'Maria', idade:3},
{nome:'luciana', idade:44},
{nome:'lucimara', idade:22}
];
var alunosDeMaior = [];
for(var i = 0; i < alunos.length; i++){
var aluno = alunos[i];
if(aluno.idade >= 18){
alunosDeMaior.push(aluno);
}
}
console.log(alunosDeMaior);
A função de callback recebe como parâmetro cada um dos alunos da lista em cada iteração — assim como aconteceu nas outras funções auxiliares que vimos — e o atribui na variável aluno .
Dentro da função, utilizamos um critério de avaliação para devolver um valor booleano para o filter : true ou false . Se for retornado verdadeiro, o valor é inserido no novo Array retornado; caso contrário, é simplesmente ignorado e não é incluído.
var alunos = [
{nome:'José', idade:15},
{nome:'Maria', idade:3},
{nome:'luciana', idade:44},
{nome:'lucimara', idade:22}
];
var alunosDeMaior = alunos.filter(function (aluno) {
return aluno.idade < 18;
});
console.log(alunosDeMaior);
Esta função auxiliar é particularmente interessante quando o objetivo é encontrar um item específico dentro de um Array.
var alunos = [
{nome:'Jose', idade:34},
{nome:'Joao', idade:31},
{nome:'Josemar', idade:44},
{nome:'Maria', idade:11},
];
var alunoProcurado;
for(var i = 0; i < alunos.length ; i++){
var aluno = alunos[i];
if(aluno.nome === "Maria"){
alunoProcurado = aluno;
break;
}
}
console.log(alunoProcurado);
var alunos = [
{nome:'Jose', idade:34},
{nome:'Joao', idade:31},
{nome:'Josemar', idade:44},
{nome:'Maria', idade:11},
];
var alunoProducado = alunos.find(function (aluno) {
//return aluno.nome === "Maria";
return aluno.idade >= 15; // retorna apenas 1 resultado
});
console.log(alunoProducado);
Esta é uma função auxiliar bem interessante. Ao contrário das outras que vimos até então, esta não retorna uma cópia do Array, mas sim um valor booleano.
var jogos = [
{nome: 'jogo 1', categoria: 'ação'},
{nome: 'jogo 2', categoria: 'corrida'},
{nome: 'jogo 3', categoria: 'corrida'},
{nome: 'jogo 4', categoria: 'corrida'},
{nome: 'jogo 5', categoria: 'corrida'},
{nome: 'jogo 6', categoria: 'corrida'}
];
// como se são todos jogos de corrida
var todosJogosDeCOrrida = true;
for(var i = 0 ; i < jogos.length ; i++){
var jogo = jogos[i];
if(jogo.categoria !== 'corrida'){
todosJogosDeCOrrida = false;
break;
}
}
console.log(todosJogosDeCOrrida);
var jogos = [
{nome: 'jogo 1', categoria: 'ação'},
{nome: 'jogo 2', categoria: 'corrida'},
{nome: 'jogo 3', categoria: 'corrida'},
{nome: 'jogo 4', categoria: 'corrida'},
{nome: 'jogo 5', categoria: 'corrida'},
{nome: 'jogo 6', categoria: 'corrida'}
];
// como se são todos jogos de corrida
var todosJogosCorrida = jogos.every(function (jogo) {
return jogo.categoria === 'corrida';
});
console.log(todosJogosCorrida);
Se a tarefa é validar se, pelo menos, um dos elementos de um Array satisfaz uma dada condição, o some é o método perfeito para o trabalho.
var jogos = [
{nome: 'jogo 1', categoria: 'ação'},
{nome: 'jogo 2', categoria: 'plataforma'},
{nome: 'jogo 3', categoria: 'tipo 1'},
{nome: 'jogo 4', categoria: 'aventura'},
{nome: 'jogo 5', categoria: 'shooter'},
{nome: 'jogo 6', categoria: 'corrida'}
];
// como se há pelo menos um jogo de corrida?
var temJogoDeCorrida = false;
for(var i = 0 ; i < jogos.length ; i++){
var jogo = jogos[i];
if(jogo.categoria === 'corrida'){
temJogoDeCorrida = true;
break;
}
}
console.log("Tem jogo de corrida? R: ", temJogoDeCorrida);
var jogos = [
{nome: 'jogo 1', categoria: 'ação'},
{nome: 'jogo 2', categoria: 'plataforma'},
{nome: 'jogo 3', categoria: 'tipo 1'},
{nome: 'jogo 4', categoria: 'aventura'},
{nome: 'jogo 5', categoria: 'shooter'},
{nome: 'jogo 6', categoria: 'corrida'}
];
// como se há pelo menos um jogo de corrida?
var temJogoDeCorrida = jogos.some(function (jogo) {
return jogo.categoria === 'corrida';
});
console.log("Tem jogo de corrida? R: ", temJogoDeCorrida);
A ideia por trás dela é pegar todos os valores de um Array e condensá-los em um único.
var numeros = [10,10,10,20];
// como faço para obter a doma de todos os numeros?
var soma = 0;
for(var i = 0 ; i < numeros.length ; i++){
soma += numeros[i];
}
console.log(soma);
var numeros = [10,10,10,20];
// como faço para obter a doma de todos os numeros?
var soma = numeros.reduce(function (somaAux, numero) {
return somaAux + numero;
}, 0 ); // 0 -> valor inicial de somaAux
console.log(soma);
var alunos = [
{nome:'joão', idade: 10},
{nome:'josé', idade: 20},
{nome:'marcos', idade: 30}
];
var nomes = alunos.reduce(function (arrayNomes, aluno) {
arrayNomes.push(aluno.nome);
return arrayNomes;
},[]);
console.log(nomes);
A iteração é definida por dois conceitos centrais: iteradores e iteráveis. Um iterável está ligado com um iterador que define como ele será percorrido. O seu objetivo é prover uma forma de sequencialmente acessar os elementos de um iterável sem expor sua representação interna, retirando a responsabilidade dele de saber como acessar e caminhar sobre sua estrutura.
// alguns objetos no JavaScript já são iteraveis por padrão
// arrays
// string
// maps e sets
// basta a gente obter o seu iterador...
var pessoas = ['jose', 'maria', 'ronny', 'mateus', 'jorge'];
var interadorPessoas = pessoas[Symbol.iterator]();
console.log(interadorPessoas.next());
console.log(interadorPessoas.next());
console.log(interadorPessoas.next());
console.log(interadorPessoas.next());
console.log(interadorPessoas.next());
console.log(interadorPessoas.next());
console.log(interadorPessoas.next());
var chapeuSeletor = function (pessoa) {
var casas = ['Belém','Cametá','Castanhal','Mosqueiro'];
var casa = casas[Math.floor(Math.random() * casas.length)];
console.log("Pessoa: " + pessoa + " Casa: "+ casa);
}
var pessoa = ['José', 'Luciana','Vovó', 'Mayque de Mosqueiro'];
var iterador = pessoa[Symbol.iterator]();
var done = false;
var proximo = iterador.next();
do{
var pessoa = proximo.value;
chapeuSeletor(pessoa);
proximo = iterador.next();
}while(!proximo.done);
Por natureza, iteradores são Lazy. Isso significa que ele não sabe de antemão quantos valores vai iterar. É preciso ter muito cuidado, pois ele pode iterar algo infinitamente.
Neste capítulo vimos como funciona um novo tipo de laço de repetição: o laço for...of. Este tipo de laço foi criado para percorrer um objeto se, e somente se, ele for iterável. Seu funcionamento é bem simples. Sua sintaxe é:
for (variavel of iteravel) {
// corpo
}
A variavel representa uma variável de auxílio que assume valores diferentes a cada iteração, e o iteravel é o objeto que será iterado. O caso de uso mais recorrente deste tipo de laço é para passar por todos os valores contidos em um Array, Set ou um Map.
Sua utilização é muito simples.
var carro = {
modelo: 'fiat',
ano: 2000
};
for(var propriedade in carro){
var info = carro[propriedade];
console.log(info);
}
var numeros = [1,2,3,4,5,6,7,8,9,0];
for(var numero of numeros){
if(numero > 3){
break;
}
console.log(numero);
}
console.log('--------------------');
for(var numero of numeros){
if(numero === 3){
continue;
}
console.log(numero);
}
var chapeuSeletor = function (pessoa) {
var casas = ['Belém','Cametá','Castanhal','Mosqueiro'];
var casa = casas[Math.floor(Math.random() * casas.length)];
console.log("Pessoa: " + pessoa + " Casa: "+ casa);
}
var pessoas = ['José', 'Luciana','Vovó', 'Mayque de Mosqueiro'];
/*
var iterador = pessoas[Symbol.iterator]();
var done = false;
var proximo = iterador.next();
do{
var pessoa = proximo.value;
chapeuSeletor(pessoa);
proximo = iterador.next();
}while(!proximo.done);
*/
for(var pessoa of pessoas){
chapeuSeletor(pessoa);
}
Com a chegada do ES6, o JavaScript introduz duas novas estruturas de dados: Map e WeakMap. Essas estruturas, ao contrário da tradicional concepção de que todos objetos são mapas, são implementações reais de mapas como estrutura de dados, assim como já estamos acostumados a ver em outras linguagens. Estas novas estruturas nos permitem:
- Adicionar elementos pelo par (chave, valor);
- Remover elementos pela chave;
- Acessar elementos dada uma chave;
- Pesquisar elementos, descobrindo se ele pertece ou não a coleção por meio da chave;
- Indagar sobre atributos, como o número de elementos, por exemplo.
- Etc
Em um Map do JavaScript, qualquer valor (tanto objetos, funções ou valores primitivos) podem ser usados como chave ou valor. Os principais métodos e propriedades são: set, get, size, has, delete, clear.
- Adicionar elementos pelo par(chave valor)
- Remover elementos pela chave;
- Acessar elementos dada uma chave
- Pesquisar elementos, descobrindo se ele pertence ou não a coleção por meio da chave;
- indagar sobre atributos, como o numero de elementos, por exemplo.
// map
var map = new Map();
function funcao(){};
var objeto = {};
map.set("String", "Sou uma String");
map.set("String2", "Sou uma String");
map.set("String3", "Sou uma String");
map.set(objeto, "Sou um objeto");
map.set(funcao, "Sou uma função");
// GET
console.log(map.get("String"));
console.log(map.get(objeto));
console.log(map.get(funcao));
/*
Sou uma String
Sou um objeto
Sou uma função
*/
console.log("Tamanho = "+map.size);
// HAS
console.log(map.has("String")); // true
console.log(map.has("String99")); // false
// DELETE
map.delete("String");
console.log(map.has("String")); // false
// CLEAR - remove todos os elementos do map
console.log(map.size); // 4 elementos
//map.clear();
console.log(map.size); // 0 elementos
console.log("---------------------------")
/* E como vimos nos capítulos anteriores, o Map é um objeto iterável.
Sendo assim, podemos utilizar o laço for...of para iterá-los através
dos métodos: keys, values e entries.
Eles retornam todas as chaves, todos os valores e todas as
entradas (par chave/valor), respectivamente. */
for (var chave of map.keys()){
console.log(chave);
}
for(var valor of map.values()){
console.log(valor);
}
for(var entrada of map.entries()){
console.log(entrada);
}
var livros = [
{titulo: "O Poder do Habito", autor:"Charles D.", local: "A1"},
{titulo: "O Poder da Mente", autor:"Charles D.", local: "G1"},
{titulo: "Geração de Valor", autor:"Flavio A..", local: "B1"},
{titulo: "Java Como Programar", autor:"Deitel", local: "P1"},
];
var estantes = new Map();
for(var livro of livros){
estantes.set(livro.titulo, livro.local);
}
function getLocalizacaoDoLivro(titulo){
var estante = estantes.get(titulo);
if(estante === undefined){
return "Livro não encontrado";
}
return estante;
}
var localizacao = getLocalizacaoDoLivro('Java aComo Programar');
console.log(localizacao);
Um WeakMap é uma coleção de pares de chave/valor na qual as chaves só podem ser objetos. As referências do objetos nas chaves são fracamente mantidas. Isso significa que eles não estão previnidos de serem coletados pelo Garbage Collector caso não existir nenhuma outra referência para o objeto em memória. Como toda chave do WeakMap necessariamente precisa ser um objeto, se tentamos utilizar qualquer outro tipo de valor no lugar, tomamos um erro. Além disso, dos métodos que vimos em Map, somente temos quatro deles disponíveis aqui: delete, has, get e set. Não temos como limpar todos os dados de uma vez (método clear) e nem ter uma visão geral do conteúdo do WeakMap (método entries) por causa da natureza fraca das ligações.
// WeajMaps são "Fraquinhos"
var weakMap = new WeakMap();
var elemento1 = {atr:1};
var elemento2 = {atr:2};
weakMap.set(elemento1, 'Sou o elemento 1');
weakMap.set(elemento2, 'Sou o Elemento 2');
console.log(weakMap.get(elemento1));
console.log(weakMap.get(elemento2));
elemento2 = null;
setTimeout(function(){
console.log(weakMap.get(elemento2));
},3000);
// Onde usar:
// Para manter o programa protegido de memory leak
// manter dados privados dentro da aplicação
var Pessoa = (function(){
var dadosPrivados = new WeakMap();
function Pessoa(nome){
dadosPrivados.set(this,{nome:nome});
}
Pessoa.prototype.getNome = function(){
return dadosPrivados.get(this).nome;
};
return Pessoa;
}());
var roberto = new Pessoa('Roberto');
console.log(roberto.getNome()); // Roberto
console.log(roberto._nome); // undefined
Às vezes, necessitamos tratar coleções de itens únicos, ou seja, itens que não repetem. Estes itens podem ser qualquer coisa: números, strings, objetos ou mesmo funções. Na prática, isso significa que precisamos criar listas em que só podemos adicionar um item específico uma única vez. Se tentarmos adicioná-lo mais de uma vez, ela deve ser inteligente o suficiente para saber que aquele item já está lá e não o adicionar novamente.
Usando ES5
function Set(){
var array = [];
this.add = function(valor){
if(array.indexOf(valor)=== -1){
array.push(valor);
}
},
this.mostravalores = function(){
console.log(array);
}
}
var set = new Set();
set.add(2);
set.add(4);
set.add(6);
set.add(2);
set.add(2);
set.mostravalores();
// [ 2, 4, 6 ]
O Set é uma estrutura de dados que nos permite ter listas com valores que nunca se duplicam e que mantém a ordem de inserção dos seus itens. Sua utilização também é muito simples. Seus principais métodos são: add, delete, clear e o has.
/* Músicas Disponíveis */
var musica_1 = {titulo:'Musica 1', autor: 'autor 1'};
var musica_2 = {titulo:'Musica 2', autor: 'autor 2'};
var musica_3 = {titulo:'Musica 3', autor: 'autor 3'};
var musica_4 = {titulo:'Musica 4', autor: 'autor 4'};
var playlist = new Set([musica_1,musica_2,musica_1,musica_3,musica_4,musica_1]);
/* Features */
// Adicionar música
//playlist.add(musica_1);
//playlist.add(musica_2);
// remover musica
//playlist.delete(musica_1);
// remover todas as músicas
//playlist.clear();
//ver quantas musicas estão na playlist
console.log(playlist.size);
for(var musica of playlist){
console.log(musica);
}
/*
4
{ titulo: 'Musica 1', autor: 'autor 1' }
{ titulo: 'Musica 2', autor: 'autor 2' }
{ titulo: 'Musica 3', autor: 'autor 3' }
{ titulo: 'Musica 4', autor: 'autor 4' }
*/
O WeakSet é um Set que não previne os seus elementos de serem coletados pelo Garbage Collector. Uma vez que o elemento não existe mais e seja identificado pelo coletor para ser coletado, ele também é automaticamente removido do WeakSet. Além disso, há três restrições importantes nesta estrutura que o diferem do Set:
- Só é possível adicionar objetos: valores de tipos primitivos como números e strings não são aceitos
- O WeakSet não é iterável: isso significa que não podemos usar laços de repetição como o for...of nele
- Não há como remover todos os elementos de uma vez: esta estrutura não implementa o método clear, pois a lista é mantida "automaticamente" pelo Garbage Collector.
Quando dizemos que a lista se mantém automaticamente, isso significa que não é preciso se preocupar com vazamentos de memória, pois a lista nunca vai conter uma referência para algo que não existe mais. Há poucas circunstâncias nas quais ele poderá ser útil no dia a dia. Existem muitas discussões em fóruns e blogs de desenvolvimento sobre as possíveis utilidades da estrutura de WeakSet. Um dos casos de uso mais interessante é o de garantir que certo método ou propriedade pertence a um objeto específico e não a todas as instâncias do mesmo tipo. Mas no uso geral, sempre que você tiver preocupação com vazamento de memória, o WeakSet estará a seu dispor.