Estes são os recursos do ES6 que você deve conhecer

Cristi Salcescu Blocked Unblock Seguir Seguindo 4 de janeiro Foto de Alex Siale no Unsplash

O ES6 traz mais recursos para a linguagem JavaScript. Algumas novas sintaxes permitem que você escreva código de maneira mais expressiva, alguns recursos completam a caixa de ferramentas de programação funcional e alguns recursos são questionáveis.

vamos e const

Existem duas maneiras de declarar uma variável ( let e const ) mais uma que se tornou obsoleta ( var ).

deixei

let declara e opcionalmente inicializa uma variável no escopo atual. O escopo atual pode ser um módulo, uma função ou um bloco. O valor de uma variável que não é inicializada é undefined .

Escopo define o tempo de vida e a visibilidade de uma variável. Variáveis não são visíveis fora do escopo em que são declaradas.

Considere o código seguinte que enfatiza let escopo de bloco:

 seja x = 1; 
{
seja x = 2;
}
console.log (x); // 1

Em contraste, a declaração var não tinha escopo de bloco:

 var x = 1; 
{
var x = 2;
}
console.log (x); // 2

A instrução for loop, com a declaração let , cria uma nova variável local para o escopo do bloco, para cada iteração. O próximo loop cria cinco closures em cinco variáveis i diferentes.

 (função run () { 
para ( let i = 0 ; i <5; i ++) {
setTimeout (função log () {
console.log (i); // 0 1 2 3 4
}, 100);
}
}) ();

Escrever o mesmo código com var criará cinco closures, sobre a mesma variável, para que todos os closures exibam o último valor de i .

A função log() é um encerramento. Para mais informações sobre fechamentos, dê uma olhada em Descubra o poder dos fechamentos em JavaScript .

const

const declara uma variável que não pode ser reatribuída. Torna-se uma constante somente quando o valor atribuído é imutável. Valores primitivos são imutáveis, objetos são mutáveis.

const congela a variável, Object.freeze() congela o objeto.

A inicialização da variável const é obrigatória.

Módulos

Antes dos módulos, uma variável declarada fora de qualquer função era uma variável global.

Com os módulos, uma variável declarada fora de qualquer função é ocultada e não está disponível para outros módulos, a menos que seja explicitamente exportada.

Exportar disponibiliza uma função ou objeto para outros módulos. No próximo exemplo, eu exporto funções de fábrica de diferentes módulos:

 // module "./dataaccess/TodoDataService.js" 
função padrão de exportação TodoDataService () {}
 // module "./dataaccess/UserDataService.js" 
função padrão de exportação UserDataService () {}

Importar torna uma função ou objeto, de outros módulos, disponível para o módulo atual.

 import TodoDataService de "./dataaccess/TodoDataService"; 
import UserDataService de "./dataaccess/UserDataService";

(função startApplication () {
const userDataService = UserDataService ();
const todoDataService = TodoDataService ();
} ());

Espalhar / Descansar

O operador pode ser o operador spread ou o parâmetro rest, dependendo de onde é usado. Considere o próximo exemplo:

 números const = [1, 2, 3]; 
const arr = ['a', 'b', 'c', ... números ];
 console.log (arr); 
["a", "b", "c", 1, 2, 3]

Este é o operador de spread. Agora veja o próximo exemplo:

 processo de função (x, y, ... arr ) { 
console.log (arr)
}
processo (1,2,3,4,5);
// [3, 4, 5]
 function processArray ( ... arr ) { 
console.log (arr)
}
processArray (1,2,3,4,5);
// [1, 2, 3, 4, 5]

Este é o parâmetro de descanso.

argumentos

Com o parâmetro rest, podemos substituir o pseudo-parâmetro de arguments . O parâmetro rest é um array, os arguments não são.

 function addNumber (total, valor) { 
retornar total + valor;
}
 função sum ( ... args ) { 
return args.reduce (addNumber, 0) ;
}
 soma (1,2,3); // 6 

Clonagem

O operador de propagação torna a clonagem de objetos e matrizes mais simples e mais expressiva.

 const book = {title: "JavaScript: as boas partes"}; 
 // clone com Object.assign () 
const clone = Object.assign ({}, book);
 // clone com o operador spread 
clone const = {... livro};
 const arr = [1, 2, 3]; 
 // clone com fatia 
const cloneArr = arr.slice ();
 // clone com o operador spread 
const cloneArr = [... arr];

Concatenação

No próximo exemplo, o operador spread é usado para concatenar matrizes:

 const part1 = [1, 2, 3]; 
const part2 = [4, 5, 6];
 const arr = part1.concat (part2); 
 const arr = [... part1, ... part2]; 

Herança múltipla

O operador de propagação, como Object.assign() , pode ser usado para copiar propriedades de um ou mais objetos para um objeto vazio e fazer várias heranças.

 const authorDataService = { 
getAuthors: function () {},
editAuthor: function () {}
};
 const bookDataService = { 
getBooks: function () {},
editBook: function () {}
};
 // copie com Object.assign () 
const dataService = Object.assign ({},
authorDataService,
bookDataService);
 // copia com operador de spread 
const dataService = {
... authorDataService,
... bookDataService
};

console.log (dataService);

Propriedade short-hands

Considere o próximo código:

 função BookDataService () { 
função getBooks () {}
função editBook () {}

Retorna {
getBooks: getBooks,
editBook: editBook
}
}

Com propriedades curtas, quando o nome da propriedade e o nome da variável usada como o valor são os mesmos, podemos apenas escrever a chave uma vez.

 função BookDataService () { 
função getBooks () {}
função editBook () {}

return {
getBooks,
editBook
}

}

Aqui está outro exemplo:

 const userDataService = UserDataService (); 
const todoDataService = TodoDataService ();

serviços const = {
userDataService,
todoDataService
};

Tarefa de Destruturação

Considere o próximo código:

 função TodoStore (args) { 
ajudante const = args.helper;
const dataAccess = args.dataAccess;
const userStore = args.userStore;
}

Com a sintaxe de atribuição de desestruturação, pode ser escrito assim:

 função TodoStore (args) { 
const {
ajudante,
data de acesso,
userStore} = args;

}
 TodoStore ({ 
ajudante: {},
data de acesso: {},
userStore: {});

ou podemos usar a sintaxe de desestruturação na lista de parâmetros:

 função TodoStore ( {helper, dataAccess, userStore} ) {} 

Parâmetros padrão

Funções podem ter parâmetros padrão. Veja o próximo exemplo:

 log de funções (message, mode = "Info" ) { 
console.log (mode + ":" + mensagem);
}
 log ("Uma informação"); 
// Info: uma informação
 log ("Um erro", "Erro"); 
// Erro: um erro

Literais de string de modelo

Cordas modelo são definidas com a ` charter. Com seqüências de caracteres de modelo, a mensagem de registro anterior pode ser escrita assim:

 log de funções (message, mode = "Info") { 
console.log ( `$ {modo}: $ {mensagem}` );
}

Cadeias de modelos podem ser definidas em várias linhas. No entanto, uma opção melhor é manter as mensagens de texto longas como recursos, em um banco de dados, por exemplo.

Veja abaixo uma função que gera um HTML que abrange várias linhas:

 function createTodoListItemHtml (todo) { 
retornar `<li>
<div> {todo.title} </ div>
<div> {todo.userName} </ div>
</ li> `;
}

Chamadas adequadas

Uma função recursiva é a cauda recursiva quando a chamada recursiva é a última coisa que a função faz.

As funções recursivas da cauda têm melhor desempenho que as funções recursivas sem cauda. A chamada recursiva da cauda otimizada não cria um novo quadro de pilha para cada chamada de função, mas usa um único quadro de pilha.

O ES6 traz a otimização de chamada no modo estrito.

A função a seguir deve se beneficiar da otimização da chamada final.

 função print (de, para) 
{
const n = de;
if (n> to) retornar;

console.log (n);

// a última declaração é a chamada recursiva
imprimir (n + 1, para);

}
 impressão (1, 10); 

Nota: a otimização da chamada final ainda não é suportada pelos principais navegadores.

Promessas

Uma promessa é uma referência a uma chamada assíncrona. Pode resolver ou falhar em algum lugar no futuro.

Promessas são mais fáceis de combinar. Como você vê no próximo exemplo , é fácil chamar uma função quando todas as promessas forem resolvidas ou quando a primeira promessa for resolvida.

 função getTodos () {return fetch (" / todos "); } 
função getUsers () {return fetch (" / users "); }
função getAlbums () {return fetch (" / albums "); }
 const getPromises = [ 
getTodos (),
getUsers (),
getAlbums ()
];
 Promise.all (getPromises) .then (doSomethingWhenAll); 
Promise.race (getPromises) .then (doSomethingWhenOne);
 function doSomethingWhenAll () {} 
function doSomethingWhenOne () {}

A função fetch() , parte da API Fetch, retorna uma promessa.

Promise.all() retorna uma promessa que resolve quando todas as promessas de entrada foram resolvidas. Promise.race() retorna uma promessa que resolve ou rejeita quando uma das promessas de entrada resolve ou rejeita.

Uma promessa pode estar em um dos três estados: pendente, resolvida ou rejeitada. A promessa estará pendente até ser resolvida ou rejeitada.

As promessas suportam um sistema de encadeamento que permite passar dados por meio de um conjunto de funções. No próximo exemplo , o resultado de getTodos() é getTodos() como entrada para toJson() , então seu resultado é passado como entrada para getTopPriority() e, em seguida, seu resultado é passado como entrada para a função renderTodos() . Quando um erro é lançado ou uma promessa é rejeitada, o handleError é chamado.

 getTodos () 
. então (toJson)
.then (getTopPriority)
.then (renderTodos)
.catch (handleError);
 function toJson (response) {} 
function getTopPriority (todos) {}
function renderTodos (todos) {}
função handleError (error) {}

No exemplo anterior, .then() lida com o cenário de sucesso e .catch() lida com o cenário de erro. Se houver um erro em qualquer etapa, o controle da cadeia salta para o manipulador de rejeição mais próximo na cadeia.

Promise.resolve() retorna uma promessa resolvida. Promise.reject() retorna uma promessa rejeitada.

Classe

Classe é a sintaxe do açúcar para criar objetos com um protótipo customizado. Tem uma sintaxe melhor que a anterior, o construtor da função. Confira o próximo exemplo :

 class Service { 
doSomething () {console.log ("doSomething"); }
}
 deixe serviço = novo Serviço (); 
console.log (serviço .__ proto__ === Service.prototype);

Todos os métodos definidos na classe de Service serão adicionados ao objeto Service.prototype . Instâncias da classe Service terão o mesmo objeto de protótipo ( Service.prototype ). Todas as instâncias delegarão chamadas de método ao objeto Service.prototype . Os métodos são definidos uma vez no Service.prototype e, em seguida, são herdados por todas as instâncias.

Herança

“Classes podem herdar de outras classes”. Abaixo está um exemplo de herança em que a classe SpecialService “herda” da classe Service :

 class Service { 
doSomething () {console.log ("doSomething"); }
}
 classe SpecialService estende o serviço { 
doSomethingElse () {console.log ("doSomethingElse"); }
}
 deixe specialService = new SpecialService (); 
specialService.doSomething ();
specialService.doSomethingElse ();

Todos os métodos definidos na classe SpecialService serão incluídos no objeto SpecialService.prototype . Todas as instâncias delegarão chamadas de método ao objeto SpecialService.prototype . Se o método não for encontrado em SpecialService.prototype , ele será pesquisado no objeto Service.prototype . Se ainda não for encontrado, será pesquisado em Object.prototype .

A classe pode se tornar um mau recurso

Mesmo que pareçam encapsulados, todos os membros de uma classe são públicos. Você ainda precisa gerenciar problemas com this contexto de perda. A API pública é mutável.

class pode se tornar um recurso ruim se você negligenciar o lado funcional do JavaScript. class pode dar a impressão de uma linguagem baseada em classe quando o JavaScript é uma linguagem de programação funcional e uma linguagem baseada em protótipo.

Objetos encapsulados podem ser criados com funções de fábrica. Considere o próximo exemplo:

 function Service () { 
function doSomething () {console.log ("doSomething"); }

return Object.freeze ({
faça alguma coisa
});
}

Desta vez, todos os membros são privados por padrão. A API pública é imutável. Não há necessidade de gerenciar problemas com this contexto de perda.

class pode ser usada apenas como uma exceção, se exigido pela estrutura de componentes. Este foi o caso do React, mas não é mais o caso com o React Hooks .

Para mais sobre por que favorecer as funções de fábrica, dê uma olhada na função Classe vs Fábrica: explorando o caminho a seguir .

Funções de seta

As funções de seta podem criar funções anônimas em tempo real. Eles podem ser usados para criar pequenos retornos de chamada, com uma sintaxe mais curta.

Vamos pegar uma coleção de tarefas. Uma tarefa tem um id , um title e uma propriedade booleana completed . Agora, considere o próximo código que seleciona apenas o title da coleção:

 const títulos = todos.map (todo => todo.title); 

ou o próximo exemplo selecionando apenas os todos que não foram concluídos:

 const filtradoTodos = todos.filter (todo =>! todo.completed); 

Mesmo neste caso, ainda acho que o estilo livre de pontos é melhor:

 const títulos = todos.map (prop ("título")) ; 
const filteredTodos = todos.filter (isPriority) ;
 function isPriority (todo) { 
voltar! todo.completed;
}

isto

Funções de seta não têm o seu próprio this e arguments . Como resultado, você pode ver a função de seta usada para corrigir problemas com this contexto de perda. Eu acho que a melhor maneira de evitar esse problema é não usar this .

As funções de seta podem se tornar um mau recurso

As funções de seta podem se tornar um recurso ruim quando usadas em detrimento de funções nomeadas. Isso criará problemas de legibilidade e manutenção. Veja o próximo código escrito apenas com as funções de seta anônima:

 const newTodos = todos.filter (todo => 
! todo.completed && todo.type === "RE")
.map (todo => ({
título: todo.title,
userName: users [todo.userId] .name
}))
.sort ((todo1, todo2) =>
todo1.userName.localeCompare (todo2.userName));

Agora, confira a mesma lógica refatorada para funções puras com intenção de revelar nomes e decidir qual deles é mais fácil de entender:

 const newTodos = todos.filter (isTopPriority) 
.map (parcial (toTodoView, usuários))
.sort (ascByUserName);
 function isTopPriority (todo) { 
return! todo.completed && todo.type === "RE";
}
 function ascByUserName (todo1, todo2) { 
return todo1.userName.localeCompare (todo2.userName);
}

function toTodoView (usuários, todo) {
Retorna {
título: todo.title,
userName: users [todo.userId] .name
}
}

Ainda mais, as funções de seta anônimas aparecerão como (anonymous) na pilha de chamadas:

Função anônima na pilha de chamadas

Para mais sobre por que favorecer funções nomeadas, dê uma olhada em Como tornar seu código melhor com nomes de função que revelam a intenção .

Menos código não significa necessariamente mais legível. Veja o próximo exemplo e decida qual versão é mais fácil para você entender:

 // com função de seta 
const prop = chave => obj => obj [chave];
 // com palavra-chave de função 
função prop (chave) {
função de retorno (obj) {
return obj [key];
}
}

Preste atenção ao devolver um objeto. No próximo exemplo, o getSampleTodo() retorna undefined .

 const getSampleTodo = () => {title: "Um exemplo todo"}; 
 getSampleTodo (); 
//Indefinido

Geradores

O ES6 Generator é um recurso desnecessário que torna o código mais complicado.

O gerador ES6 cria um objeto que possui o método next() . O método next() cria um objeto que possui a propriedade value . Os geradores ES6 promovem o uso de loops. Veja o código abaixo :

 function * counter () { 
deixe contar = 0;
while (true) {
contar + = 1;
contagem de rendimento;
}
}
 gerador de const = contador (); 
generator.next (). value; // 1
generator.next (). value; // 2
generator.next (). value; // 3

O mesmo gerador pode ser simplesmente implementado com um fechamento.

 contador de funções () { 
deixe contar = 0;
função de retorno () {
contar + = 1;
contagem de retorno;
}
}
 gerador de const = contador (); 
gerador () // 1
gerador () // 2
gerador () // 2

Conclusão

let e const declaram e inicializam variáveis.

Os módulos encapsulam a funcionalidade e expõem apenas uma pequena parte.

O operador de dispersão, o parâmetro de descanso e a abreviação de propriedade facilitam a expressão.

Promessas e recursão de cauda completam a caixa de ferramentas de programação funcional.

Para mais informações sobre JavaScript, veja:

Descubra a programação funcional em JavaScript com esta introdução completa

Aprenda esses fundamentos JavaScript e torne-se um desenvolvedor melhor

Vamos explorar objetos em JavaScript

Como composição livre de pontos fará de você um melhor programador funcional

Como melhorar seu código com nomes de função que revelam a intenção

Torne seu código mais fácil de ler com a programação funcional