O que há de novo no ES2020?
Como você já nem deve lembrar, escrevi há um tempo sobre as novas funcionalidades do ES7 + ES8, caso não tenha lido, pode ler clicando aqui.
As propostas do ECMAScript continuarão crescendo e dando origem a novas funcionalidades interessantes a medida que o tempo vai passando. Portanto, você já pode acessar estas novas funcionalidades do ECMAScript resumidas no ES2020. Para não perder o trem, vale a pena manter-se informado(a) sobre cada uma dessas funcionalidades, vamos a elas.
Dynamic import
Imports dinâmicos (Dynamic importing
) é um dos recursos mais interessantes do ES2020, e como o nome já diz, serve para fazer carregamento sob demanda ou o que com frequência chamamos de lazy loading.
Anteriormente, se você quisesse importar um módulo em JavaScript, teria algo como:
import Calculator from './Calculator.js
const Calc = new Calculator();
const values = [1, 3, 2];
const max = Calc.getMaxValue(...values); console.log('Calculator - getMaxValue: ', max);
O problema que tínhamos nesse caso é que era uma maneira "estática" de importar o módulo Calculator
e não poderíamos simular uma importação dinâmica se quiséssemos que esse módulo dependesse de uma ação do usuário.
Graças ao ES2020, agora você pode usar o lazy loading sem um module bundler (leia webpack)! A partir de agora você pode usar a importação como uma função em qualquer lugar do seu código, usando uma função assíncrona que retorna uma Promise.
O código acima, no ES2020, pode ser escrito da seguinte maneira:
import('./Calculator.js')
.then(Calc => {
const Calc = new Calculator();
const values = [1, 3, 2];
const max = Calc.getMaxValue(...values);
console.log('Calculator - getMaxValue: ', max)
})
.catch(err => {
// Exibe os erros de carregamento do módulo
console.error('Calculator - err: ', err)
})
Agora vamos imaginar a importaçao condicionado a uma operação realizada pelo usuário:
const downloadButton = document.querySelector('.download')
downloadButton.addEventListener('click', () => {
import('./download.js')
.then(downloadModule => {
downloadModule.downloadPDF()
})
.catch(err => {
// Exibe os erros de carregamento do módulo
console.error('Download MODULE - err: ', err)
})
})
Neste caso o módulo download.js
será apenas importado na primeira vez que o usuário clicar no botão de "Download".
BigInt
Atualmente representar um número maior do que 2^53-1
em JavaScript pode significar um problema, pois este é o maior número que o JavaScript pode representar com segurança usando o objeto Number.
O BigInt é um objeto nativo que ajuda a representar números maiores que 2^53-1
, assim sendo, nos traz a possibilidade de fazer cálculos em números inteiros¹ grandes sem perder a precisão.
Como o nome Int sugere, você não pode usar o BigInt com números flutuantes.
const biggestIntNumber = BigInt(Number.MAX_SAFE_INTEGER)
// 9007199254740991n
const largerNumber = biggestIntNumber + 10n;
console.log('BigInt value: ', largerNumber);
// 9007199254741001n
Simples, não?
Atualmente você já pode tirar proveito desta nova funcionalidade nos seguintes navegadores: Chrome 67, Firefox 68, Opera 54 e Edge 79.
globalThis
A propriedade globalThis
é uma ótima notícia! globalThis
contém o valor global de acordo com o contexto do objeto global.
O recurso é bastante simples, mas resolve muitas inconsistências que às vezes podem deixar você bastante frustrado(a). Simplificando, globalThis
contém uma referência ao objeto global. No navegador o objeto global é representado por window
. Em um ambiente Node.js, o objeto global é literalmente chamado de global
. Ao usarmos o globalThis
garantimos que sempre tenhamos uma referência válida ao objeto global, independentemente do ambiente em que seu código esteja sendo executado. Dessa forma, é possível escrever módulos JavaScript portáteis que serão executados corretamente no segmento principal do navegador, em um web worker, ou em um ambiente NodeJS.
Exemplo:
function isAvailable(method) {
return typeof globalThis[method] === 'function'
}
console.log(isAvailable('XMLHttpRequest'))
// true (se executado em um navegador)
Graças ao globalThis
, você não precisa diferenciar se o código está sendo executado em um navegador ou um ambiente NodeJS, além disso já podemos começar a evitar escrever coisas como var self = this
:)
Nullish Coalescing
O operador de coalescência nula (??) é um operador lógico que retorna o seu operando do lado direito quando o seu operador do lado esquerdo é
null
ouundefined
. Caso contrário, ele retorna o seu operando do lado esquerdo.
console.log(true ?? true) // true
console.log(false ?? true) // false
console.log(undefined ?? true) // true
console.log(null ?? true) // true
console.log(0 ?? true) // 0
console.log(1 ?? null) // 1
Ainda não viu utilidade?
Vamos então aos exemplos mais complexo, imagine um objeto retornado de uma requisição e depois tente acessar uma propriedade que não existe:
const user = {
name: 'John',
age: 15,
company: 'BrazilJS'
}
console.log(user.gender ?? 'Would rather not say')
// Would rather not say
O operador de encadeamento opcional ?
permite ler o valor de uma propriedade localizada profundamente dentro de uma cadeia de objetos conectados sem ter que validar expressamente que cada referência na cadeia é válida. O ?.
(interrogação seguido de ponto) funciona de maneira semelhante ao .
(ponto) operador de encadeamento, exceto que, em vez de causar um erro se uma referência para o nullish (null
ou undefined
), a expressão entrará em curto-circuito com um valor de retorno indefinido. Quando usado com chamadas de função, ele retorna undefined
se a função fornecida não existir.
Para acessar com segurança essas propriedades, usamos o novo operador de encadeamento opcional, vamos explorar juntos:
const user = {
name: 'Megan',
pets: {
dogs: [
{
name: 'Simba'
},
{
name: 'Nala'
}
],
},
}
// user.pets.cat.name ~> Error: user.pets.cat is undefined
const catName = user.pets.cat?.name;
console.log(catName); // undefined
console.log(user.getFullName?.()) // undefined
Além da simplicidade da implementação, com esta nova funcionalidade acabamos evitando uma quantidade considerável de if
's encadeados ;)
Promise.allSettled
O método Promise.allSettled
aceita um array de Promises e e só resolve quando todos forme resolvidos independente se foram resolved ou rejected.
Já haviam soluções que tentavam simular algo parecido a isso, como race
e all
, mas até então esta solução não estava disponível originalmente. Em outras palavras podemos dizer que esta funcionalidade "executa todas as promessas e resolve quando todas estiverem prontas não se importando com os resultados".
const promiseResolved = Promise.resolve(666)
const promiseRejected = Promise.reject(null)
const promiseRejectedError = Promise.reject(new Error('BrazilJS Conf'))
const allPromises = [promiseResolved, promiseRejected, promiseRejectedError]
Promise.allSettled(allPromises).then(result => {
console.log('Todas as promises foram resolvidas: ', result)
De fato, Promise.allSettled
se parece com Promise.all
, a diferença é que o método Promise.all
espera que todas as promessas sejam cumpridas (resolved) ou que qualquer promessa seja rejeitada (rejected).
String.prototype.matchAll()
O método String.prototype.matchAll()
, se comporta de maneira semelhante ao match()
, mas retorna um iterador com todas os resultados da expressão regular. Isso oferece uma maneira simples de iterar sobre os resultados, especialmente quando você precisa de acesso para capturing groups.
Há algo de errado com o método match()
?
A resposta curta não é "não", a menos que você esteja tentando retornar os resultados com grupos de captura.
Nada melhor que os exemplos para nos ajudar a entender melhor, segue comigo:
const str = 'BrazilJS1BrazilJS2';
const regex = /Bra(zil)(JS(\d?))/g;
const result = [...str.matchAll(regex)]; // [ ["BrazilJS1", "zil", "JS1", "1"], ["BrazilJS2", "zil", "JS2", "2"] ]
console.log(result[0]); // ["BrazilJS1", "zil", "JS1", "1", index: 0, input: "BrazilJS1BrazilJS2", groups: undefined]
console.log(result[1]); // ["BrazilJS1", "zil", "JS2", "2", index: 0, input: "BrazilJS1BrazilJS2", groups: undefined]
Eis que é hora da pergunta, mas esse não é o mesmo retorno do método match()
?
Tecnicamente "sim", apenas quando temos um único retorno ou seja, quando a flag /g
(global) não estiver presente. Se tivermos utilizando o /g
o retorno serão os resultados encontrados.
const str = 'BrazilJS1BrazilJS2';
const regexWithoutGlobal = /Bra(zil)(JS(\d?))/;
const resultWithoutGlobal = [...str.match(regexWithoutGlobal)]; // ["BrazilJS1", "zil", "JS1", "1"]
const regexWithGlobal = /Bra(zil)(JS(\d?))/g;
const resultWithGlobal = [...str.match(regexWithGlobal)]; // ["BrazilJS1", "BrazilJS2"]
Conclusão
São várias novas funcionalidades que a primeira vista podem parecer muita coisa, mas lembre-se que você pode ir incrementando seus projetos começando pelas menores funcionalidades até que tudo esteja sendo utilizado.
A linguagem em si tem crescido a medida que surgem novas demandas da própria comunidade, por isso não esqueça que se você tiver alguma sugestão poderá sempre enviar ao comitê ECMA TC39 e quem sabe em um futuro ver a sua funcionalidade sendo assunto por aqui...
Agora que já aprendemos juntos sobre todas elas, que tal compartilhar conosco qual é o seu recurso favorito do ES2020?
Se você ficou quiser conhecer mais a respeito das novas funcionalidades, mantenha-se atento aos seguintes tópicos: