Começamos mais um ano e, como já é de se esperar, o mundo da tecnologia nunca parou de desenvolver novas ideias e novas features para suas ferramentas. Notavelmente, como já dissemos em artigos anteriores o JavaScript lidera essa sede por novidades com a nova versão da especificação que deve estar saindo ainda este ano. Vamos entender o que o JS reserva para nós em 2020!
Dynamic Imports
Com o advento dos módulos JavaScript, que vamos discutir um pouco mais em futuros artigos, vamos ser capazes de utilizar a sintaxe export e import por padrão, sem precisar de nenhum tipo de polyfill, ou seja, aos poucos o nosso antigo require que utilizamos muito em NodeJS vai acabar caindo em desuso. Isto é algo bom, porque permite que possamos utilizar importações dinâmicas no nosso código!
Hoje, quando temos um carregamento de módulos no JavaScript feito no momento pre-runtime ou seja, eles são carregados antes do código ser inicializado, por isso que colocamos eles no topo dos nossos arquivos. Com essa proposta, devs vão ter maior capacidade de controle de quando querem que seus módulos sejam importados, por exemplo, podemos importar um módulo somente quando formos utilizar ou então dentro de uma condição específica:
const nums = [1, 2, 3]
if (nums.length) {
const math = import('../math').then(module => module.pow(...nums))
}
Veja que só estamos importando o módulo math quando temos alguma operação para executar. Além disso, o import retorna uma promise, o que significa que ele pode ser utilizado normalmente com then ou então com async/await:
async function doSomething (variable) {
if (variable && variable > 2) {
const module = await import('../myModule')
const result = module.doOtherThing(variable)
return result
}
return variable
}
Atualmente o suporte para esta funcionalidade é o seguinte:
- Chrome: 63
- Edge: Sem suporte
- Firefox: 67
- IE: Sem suporte
- Opera: 50
- Safari: 11.1
Veja a descrição completa da proposta: https://github.com/tc39/proposal-dynamic-import
String.prototype.matchAll()
Este método permite que criemos regras de matching com Regex em uma string diretamente, encontrando todas as combinações resolvidas por ela:
const re = /(Dr\. )\w+/g;
const str = 'Dr. Smith and Dr. Anderson';
const matches = str.matchAll(re);
for (const match of matches) {
console.log(match);
}
// saídas:
// => ["Dr. Smith", "Dr. ", index: 0, input: "Dr. Smith and Dr. Anderson", groups: undefined]
// => ["Dr. Anderson", "Dr. ", index: 14, input: "Dr. Smith and Dr. Anderson", groups: undefined]
Promise.allSettled
Esta é uma funcionalidade que já comentamos em outro artigo, porém, para dar um overview, o método allSettled de uma Promise vai permitir que um array inteiro de promises seja resolvido com seus valores para erro ou para sucesso, ao invés de termos uma função como o all que resolve tudo para sucesso ou então dá um curto-circuito no primeiro erro:
const promises = [ fetch('existe.html'), fetch('http://nao-existe.com')
]
const resultados = await Promise.allSettled(promises)
/*
[
{ status: 'fulfilled', value: 'html do site' },
{ status: 'rejected', reason: 'not found' }
]
*/
O método allSettled pode ser usado nessas versões dos maiores browsers:
- Chrome 76+
- Firefox 71+
- Safari 13+
Optional Chaining
Esta é uma funcionalidade muito pedida pela maioria dos desenvolvedores, já até falamos dela por aqui. Se você já utilizou qualquer superset como TypeScript, é realmente muito chato quando tempos que realizar alguma operação com um objeto que possua nós intermediários, por exemplo:
const pessoa = {
nome: 'Lucas',
telefones: {
residencial: 'xxxx',
celular: 'yyyyy'
}
}
Se quiséssemos fazer uma operação para buscar o número de telefone residencial, teríamos que obter a chave pessoa.telefones.residencial, porém, no TypeScript por exemplo, se o objeto pode ser nulo, teremos que verificar se telefones existe, então nossa condição seria:
const telefone = pessoa.telefone && pessoa.telefone.residencial ? pessoa.telefone.residencial : null
Com o Optional Chaining isso não é mais necessário, podemos fazer essa verificação de uma única vez utilizando o operador ?.:
const telefone = pessoa?.telefone?.residencial
Caso a propriedade não exista, ela voltará como undefined. O Optional Chaining pode ser utilizado em várias posições:
obj?.prop // Acesso a uma propriedade estática
obj?.[expr] // Acesso a uma propriedade dinâmica
func?.(...args) // Função ou método
Objetct.fromEntries
Um outro método bastante interessante que vai chegar na nova versão do ES em 2020 é o fromEntries. Uma das práticas mais comuns em JS é converter objetos para arrays e vice-versa, então se tivermos um objeto do tipo:
const obj = {
foo: 'bar',
num: 2
}
Poderemos converter em um array do tipo [[‘foo’, ‘bar’], [‘num’, 2]], que é o que fazemos quando executamos a função Object.entries(). Agora, fromEntries é o oposto, podemos pegar um array neste modelo e transformar em um Objeto:
const toObj = new Map([
['foo', 'bar'],
['num', 23]
])
const obj = Object.fromEntries(toObj) // { foo: 'bar', num: 23 }
BigInt
Um dos maiores problemas desde os primórdios do JavaScript era tratar com números muito grandes, por exemplo:
Number.MAX_VALUE * 2 // Infinity
Para resolver esse problema surgiu o BigInt, um novo tipo de dados que permite que você trabalhe com números muito grandes! Basta transformar um número qualquer em um BigInt desta forma:
const num = 42
const bigNum = BigInt(num)
bigNum // 42n
bigNum === 42n // true
Sempre que tivermos um n no final do número, significa um BigInt, podemos fazer quaisquer operações com ele também:
const bigN = BigInt(10)
bigN + bigN // 20n
bigN * 3n // 30n
bigN - BigInt('55') // 45n
bigN / 3n // 3n
Mas atente-se que bigN / 3n retorna 3n e não um numero quebrado como 10/3 que seria 3.33, isto porque ele só trabalha com inteiros! Portanto não podemos fazer um BigInt a partir de um decimal como BigInt(3.3) de nenhuma forma.
Outra coisa interessante para se atentar é que só podemos misturar os tipos quando tivermos realizando operações de comparação:
if (BigInt(45) < 44) { ... } // false
Portanto um BigInt é sempre tratado como um number em condições if.
Veja a tabela de compatibilidade:
- Chrome: 67
- Edge: Sem suporte
- Firefox: 68
- IE: Sem Suporte
- Opera: 54
- Safari: Sem Suporte
GlobalThis
Em outro artigo já falei sobre esta nova proposta. Basicamente o GlobalThis se refere ao objeto this no escopo global. Isso varia de contexto para contexto, por exemplo, no browser o globalThis referencia o this enquanto no Node, referencia o global:
// worker.js
globalThis === self
// node.js
globalThis === global
// browser.js
globalThis === window
E este é o suporte para os browsers:
- Chrome: 71
- Edge: Sem suporte
- Firefox: 65
- IE: Sem suporte
- Opera: Sem suporte
- Safari: 12.1
- Node: 12
Conclusão
O JavaScript em 2020 promete várias mudanças interessantes e legais que vão ser muito úteis para a maioria dos programadores. Vamos aguardar o lançamento oficial e tirar proveito das mesmas assim que possível! Logo mais vou postar um outro artigo somente focado em NodeJS onde vamos explorar mais os ciclos de release e outras informações.