Evolução do ECMAScript: Recursos Essenciais do ES7 ao ES10

A partir de 2016, o padrão ECMAScript adotou um ciclo de atualizações anuais, introduzindo incrementos graduais que modernizaram significativamente a linguagem JavaScript. Esta análise técnica foca nas adições mais relevantes entre as versões ES7 e ES10, demonstrando como essas funcionalidades resolvem problemas comuns de desenvolvimento e melhoram a legibilidade do código.

ES7 (ECMAScript 2016)

Verificação de existência em Arrays com includes()

Antes do ES7, a validação de presença de um elemento em um array dependia de indexOf() ou find(), que exigiam comparações verbosas ou falhavam ao detectar NaN. O método Array.prototype.includes() retorna um booleano direto e utiliza o algoritmo SameValueZero internamente, tratando corretamente valores especiais.

const itens = [10, 25, 30, NaN, -0];

// Verificação direta
console.log(itens.includes(25)); // true
console.log(itens.includes(NaN)); // true
console.log(itens.includes(-0)); // true

// Suporta índice inicial de busca
console.log(itens.includes(10, 1)); // false (inicia a busca a partir do índice 1)

Operador de Exponenciação (**)

A sintaxe ** fornece um atalho nativo para cálculo de potências, eliminando a dependência de Math.pow(). O operador segue a associatividade à direita, permitindo expressões encadeadas.

const base = 7;
const expoente = 3;

const resultadoDireto = base ** expoente; // 343
const equivalente = Math.pow(base, expoente); // 343

// Associatividade à direita: 2 ** (3 ** 2) = 2 ** 9 = 512
console.log(2 ** 3 ** 2); // 512

ES8 (ECMAScript 2017)

Async/Await para Fluxo Assíncrono Linear

A sintaxe async/await transforma operações baseadas em Promise em fluxo sequencial, reduzindo a complexidade visual causada por encadeamentos de .then(). Funções declaradas com async retornam implicitamente uma Promise, e await pausa a execução interna até que o objeto assíncrono se resolva.

// Abordagem com Promise (antiga)
fetchConfiguracao()
  .then(config => {
    return carregarUsuario(config.id);
  })
  .then(usuario => console.log(usuario))
  .catch(erro => console.error(erro));

// Abordagem com Async/Await (moderna)
async function inicializarApp() {
  try {
    const config = await fetchConfiguracao();
    const usuario = await carregarUsuario(config.id);
    console.log(usuario);
  } catch (erro) {
    console.error('Falha na inicialização:', erro);
  }
}
inicializarApp();

Iteração de Objetos: values() e entries()

Enquanto Object.keys() retorna apenas as chaves, as novas funções complementares retornam valores ou pares chave-valor, facilitando iterações com for...of.

const registro = { codigo: 88, nome: 'Módulo', ativo: true };

console.log(Object.values(registro)); 
// ["88", "Módulo", true]

console.log(Object.entries(registro)); 
// [["codigo", 88], ["nome", "Módulo"], ["ativo", true]]

// Iteração prática
for (const [chave, valor] of Object.entries(registro)) {
  console.log(`${chave}: ${valor}`);
}

Preenchimento de Strings (padStart e padEnd)

Os métodos padStart() e padEnd() permitem alinhar strings a um comprimento específico, preenchendo com espaços ou outro caractere. São úteis para formatação de IDs, valores monetários ou relatórios.

const valorBruto = '45';
const valorFormatado = valorBruto.padStart(6, '0'); // "000045"

const etiqueta = 'OK';
const etiquetaCentralizada = etiqueta.padEnd(10, '.'); // "OK........"

Descritores de Propriedades para Cópias Seguras

Object.assign() não preserva getters e setters durante a cópia, convertendo-os em valores estáticos. Object.getOwnPropertyDescriptors() resolve isso ao capturar metadados completos das propriedades, permitindo réplicas exatas via Object.defineProperties().

const fonte = {
  get status() { return 'ativo'; },
  set status(val) { console.log(`Atualizado para ${val}`); }
};

// Object.assign perde o getter/setter
// Solução correta:
const destino = {};
Object.defineProperties(destino, Object.getOwnPropertyDescriptors(fonte));

console.log(destino.status); // "ativo" (chama o getter)

ES9 (ECMAScript 2018)

Iteração Assíncrona com for await...of

O loop for await...of consome iteradores assíncronos, aguardando a resolução de cada Promise antes de avançar para o próximo item. Isso garante processamento sequencial em fluxos de dados assíncronos.

async function gerarTarefas() {
  yield new Promise(res => setTimeout(res, 200));
  yield new Promise(res => setTimeout(res, 500));
  yield new Promise(res => setTimeout(res, 100));
}

async function executarSequencial() {
  for await (const tarefa of gerarTarefas()) {
    console.log('Tarefa concluída em', Date.now());
  }
}
executarSequencial();

Spread e Rest em Objetos

O operador spread (...) permite clonar ou mesclar objetos superficialmente. O operador rest coleta propriedades restantes em um novo objeto. Note que propriedades com chaves duplicadas são sobrescritas pela última ocorrência.

const padroes = { tema: 'escuro', idioma: 'pt-BR' };
const preferencias = { idioma: 'en-US', notificacoes: true };

const configuracao = { ...padroes, ...preferencias };
console.log(configuracao); 
// { tema: 'escuro', idioma: 'en-US', notificacoes: true }

// Uso de rest
const { idioma, ...restoConfig } = configuracao;
console.log(restoConfig); // { tema: 'escuro', notificacoes: true }

Promise.prototype.finally()

O método finally() executa um callback independentemente do estado da Promise (resolvida ou rejeitada). Ideal para limpeza de interfaces, como ocultar indicadores de carregamento.

carregarDados()
  .then(response => processar(response))
  .catch(erro => registrarErro(erro))
  .finally(() => {
    document.getElementById('loader').classList.remove('visivel');
  });

Atualizações em Expressões Regulares

  • Flag s (dotAll): Faz o ponto (.) corresponder a quebras de linha.
  • Named Capture Groups: Permite nomear grupos para facilitar a extração.
  • Lookbehind: Afirmação posterior ((?<=...)) que verifica conteúdo antecedente sem capturá-lo.
  • Unicode Property Escapes: Correspondência baseada em categorias Unicode (\p{...}).
// Named groups e Lookbehind
const texto = 'ID: 5543 | Usuário: joao';
const regex = /(?<=\| \w+: )(\w+)/;
const match = texto.match(regex);
console.log(match[0]); // "joao"

// Unicode
const emoji = '🚀';
console.log(/\p{Emoji}/u.test(emoji)); // true

ES10 (ECMAScript 2019)

Array.prototype.flat() e flatMap()

flat() reduz a profundidade de arrays aninhados. O parâmetro de profundidade define quantos níveis serão recursivamente mesclados. flatMap() combina mapeamento e aplatamento em uma única iteração, equivalente a map().flat(1).

const categorias = [['A'], ['B', ['C', 'D']], ['E']];

console.log(categorias.flat()); 
// ["A", "B", ["C", "D"], "E"]

console.log(categorias.flat(2)); 
// ["A", "B", "C", "D", "E"]

const numeros = [2, 4, 6];
const expandido = numeros.flatMap(n => [n, n * 10]);
console.log(expandido); // [2, 20, 4, 40, 6, 60]

Object.fromEntries()

Inverso de Object.entries(), converte listas de pares chave-valor em objetos. Facilita transformações funcionais em estruturas de dados.

const dados = [['chave1', 10], ['chave2', 20]];
const objeto = Object.fromEntries(dados);
console.log(objeto); // { chave1: 10, chave2: 20 }

// Filtragem e reconstrução
const produto = { preco: 150, estoque: 0, visivel: true };
const disponiveis = Object.fromEntries(
  Object.entries(produto).filter(([_, valor]) => valor > 0)
);
console.log(disponiveis); // { preco: 150, visivel: true }

Remoção de Espaços: trimStart() e trimEnd()

Alternativas diretas a regex para limpeza de strings, removendo caracteres em branco apenas do início ou do final.

const entrada = '  42.50  ';
console.log(entrada.trimStart()); // "42.50  "
console.log(entrada.trimEnd());   // "  42.50"

String.prototype.matchAll()

Retorna um iterader com todas as correspondências de uma regex global, incluindo informações de grupos e índices, sem a necessidade de loops while manuais com exec().

const log = 'Erro: E404 | Aviso: W301 | Sucesso: 200';
const regex = /([A-Z]\d{3})/g;

for (const match of log.matchAll(regex)) {
  console.log(`Código: ${match[1]} na posição ${match.index}`);
}

Optional Catch Binding

A cláusula catch torna-se opcional. Quando o erro não precisa ser processado ou logado, o parâmetro pode ser omitido, reduzindo variáveis inutilizadas no escopo.

try {
  operacaoCritica();
} catch {
  console.warn('Operação falhou, continuando...');
}

BigInt

Nova primitiva para inteiros de precisão arbitrária. Resolve limitações do tipo Number (64-bit float), que perde precisão acima de Number.MAX_SAFE_INTEGER (2^53 - 1). Criação via sufixo n ou construtor BigInt().

const valorGrande = 9007199254740992n;
const soma = valorGrande + 1n;

console.log(typeof soma); // "bigint"
console.log(valorGrande === BigInt(valorGrande.toString())); // true

// Operações mistas geram TypeError
// console.log(10n + 5); // Erro! Use 10n + 5n

Symbol.prototype.description

Propriedade de leitura direta para acessar a descrição interna de um Symbol, sem necessidade de converter para string.

const token = Symbol('acesso_api');
console.log(token.description); // "acesso_api"

Function.prototype.toString() Prseerva Formatação

A partir do ES10, o método retorna o código fonte exato da função, incluindo espaçamento, quebras de linha e comentários, diferente do comportamento anterior que removia formatação excessiva.

function calcular(base, taxa) {
  // Aplica desconto
  return base - (base * taxa);
}

console.log(calcular.toString());
/* Retorna exatamente:
function calcular(base, taxa) {
  // Aplica desconto
  return base - (base * taxa);
}
*/

Tags: javascript ecmascript async_await promise regular_expressions

Publicado em 8-16 03:54