Pular para o conteúdo principal

39 postagens marcadas com "ECMAScript"

Ver todas os Marcadores

Referências fracas e finalizadores

· Leitura de 11 minutos
Sathya Gunasekaran ([@_gsathya](https://twitter.com/_gsathya)), Mathias Bynens ([@mathias](https://twitter.com/mathias)), Shu-yu Guo ([@_shu](https://twitter.com/_shu)), e Leszek Swirski ([@leszekswirski](https://twitter.com/leszekswirski))

Geralmente, referências a objetos são fortemente mantidas no JavaScript, o que significa que enquanto você tiver uma referência ao objeto, ele não será coletado pelo garbage collector.

const ref = { x: 42, y: 51 };
// Enquanto você tiver acesso a `ref` (ou qualquer outra referência ao
// mesmo objeto), o objeto não será coletado pelo garbage collector.

Atualmente, WeakMaps e WeakSets são a única maneira de referenciar um objeto de forma quase fraca no JavaScript: adicionar um objeto como chave a um WeakMap ou WeakSet não impede que ele seja coletado pelo garbage collector.

const wm = new WeakMap();
{
const ref = {};
const metaData = 'foo';
wm.set(ref, metaData);
wm.get(ref);
// → metaData
}
// Não temos mais uma referência a `ref` neste escopo de bloco, então ele
// pode ser coletado pelo garbage collector agora, mesmo que seja uma chave no `wm`
// ao qual ainda temos acesso.

Ordenação estável de `Array.prototype.sort`

· Leitura de 3 minutos
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Digamos que você tenha um array de cachorros, onde cada cachorro tem um nome e uma classificação. (Se isso parecer um exemplo estranho, você deve saber que existe uma conta no Twitter que se especializa exatamente nisso... Não pergunte!)

// Note como o array já está ordenado alfabeticamente por `nome`.
const doggos = [
{ name: 'Abby', rating: 12 },
{ name: 'Bandit', rating: 13 },
{ name: 'Choco', rating: 14 },
{ name: 'Daisy', rating: 12 },
{ name: 'Elmo', rating: 12 },
{ name: 'Falco', rating: 13 },
{ name: 'Ghost', rating: 14 },
];
// Ordene os cachorros por `rating` em ordem decrescente.
// (Isso atualiza `doggos` diretamente.)
doggos.sort((a, b) => b.rating - a.rating);

`Symbol.prototype.description`

· Leitura de um minuto
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Os Symbols do JavaScript podem receber uma descrição ao serem criados:

const symbol = Symbol('foo');
// ^^^^^

Anteriormente, a única maneira de acessar essa descrição de forma programática era indiretamente através de Symbol.prototype.toString():

const symbol = Symbol('foo');
// ^^^^^
symbol.toString();
// → 'Symbol(foo)'
// ^^^
symbol.toString().slice(7, -1); // 🤔
// → 'foo'

No entanto, o código parece um pouco mágico, não é muito autoexplicativo, e viola o princípio "exprima a intenção, não a implementação". A técnica acima também não permite distinguir entre um símbolo sem descrição (ou seja, Symbol()) e um símbolo com uma string vazia como descrição (ou seja, Symbol('')).

`Object.fromEntries`

· Leitura de 4 minutos
Mathias Bynens ([@mathias](https://twitter.com/mathias)), encantador de JavaScript

Object.fromEntries é uma adição útil à biblioteca JavaScript incorporada. Antes de explicar o que ele faz, é útil entender a API pré-existente Object.entries.

Object.entries

A API Object.entries existe há algum tempo.

Para cada par de chave-valor em um objeto, Object.entries fornece um array em que o primeiro elemento é a chave e o segundo elemento é o valor.

Object.entries é especialmente útil em combinação com for-of, pois permite iterar de forma muito elegante sobre todos os pares de chave-valor em um objeto:

const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]

for (const [key, value] of entries) {
console.log(`O valor de ${key} é ${value}.`);
}
// Registros:
// O valor de x é 42.
// O valor de y é 50.

Infelizmente, não há uma maneira fácil de voltar do resultado de entries para um objeto equivalente… até agora!

Object.fromEntries

A nova API Object.fromEntries realiza a operação inversa de Object.entries. Isso facilita a reconstrução de um objeto com base em seus entries:

const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]

const result = Object.fromEntries(entries);
// → { x: 42, y: 50 }

Um caso de uso comum é a transformação de objetos. Agora você pode fazer isso iterando sobre seus entries e usando métodos de array com os quais você já pode estar familiarizado:

const object = { x: 42, y: 50, abc: 9001 };
const result = Object.fromEntries(
Object.entries(object)
.filter(([ key, value ]) => key.length === 1)
.map(([ key, value ]) => [ key, value * 2 ])
);
// → { x: 84, y: 100 }

Neste exemplo, estamos filterando o objeto para obter apenas chaves de comprimento 1, ou seja, apenas as chaves x e y, mas não a chave abc. Em seguida, mapeamos os entries restantes e retornamos um par atualizado de chave-valor para cada um. Neste exemplo, dobramos cada valor multiplicando-o por 2. O resultado final é um novo objeto, com apenas as propriedades x e y, e os novos valores.

Combinadores de Promise

· Leitura de 5 minutos
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Desde a introdução de promises no ES2015, o JavaScript suporta exatamente dois combinadores de promise: os métodos estáticos Promise.all e Promise.race.

Duas novas propostas estão atualmente passando pelo processo de padronização: Promise.allSettled e Promise.any. Com essas adições, haverá um total de quatro combinadores de promise no JavaScript, cada um possibilitando diferentes casos de uso.

`Array.prototype.flat` e `Array.prototype.flatMap`

· Leitura de 2 minutos
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Array.prototype.flat

O array neste exemplo tem vários níveis de profundidade: contém um array que, por sua vez, contém outro array.

const array = [1, [2, [3]]];
// ^^^^^^^^^^^^^ array externo
// ^^^^^^^^ array interno
// ^^^ array mais interno

Array#flat retorna uma versão achatada de um array fornecido.

array.flat();
// → [1, 2, [3]]

// …é equivalente a:
array.flat(1);
// → [1, 2, [3]]

A profundidade padrão é 1, mas você pode passar qualquer número para achatar recursivamente até essa profundidade. Para continuar achatando recursivamente até o resultado não conter mais arrays aninhados, passamos Infinity.

// Achatar recursivamente até o array não conter mais arrays aninhados:
array.flat(Infinity);
// → [1, 2, 3]

Por que esse método é chamado de Array.prototype.flat e não Array.prototype.flatten? Leia nosso artigo sobre #SmooshGate para descobrir!

Array.prototype.flatMap

Aqui está outro exemplo. Temos uma função duplicate que recebe um valor e retorna um array que contém esse valor duas vezes. Se aplicarmos duplicate a cada valor de um array, terminamos com um array aninhado.

const duplicate = (x) => [x, x];

[2, 3, 4].map(duplicate);
// → [[2, 2], [3, 3], [4, 4]]

Você pode então chamar flat no resultado para achatar o array:

[2, 3, 4].map(duplicate).flat(); // 🐌
// → [2, 2, 3, 3, 4, 4]

Como esse padrão é tão comum na programação funcional, agora há um método dedicado flatMap para ele.

[2, 3, 4].flatMap(duplicate); // 🚀
// → [2, 2, 3, 3, 4, 4]

flatMap é um pouco mais eficiente em comparação a fazer um map seguido de um flat separadamente.

Interessado em casos de uso para flatMap? Confira a explicação de Axel Rauschmayer.

Suporte a Array#{flat,flatMap}

Separadores Numéricos

· Leitura de 2 minutos
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Literais numéricos grandes são difíceis para o olho humano interpretar rapidamente, especialmente quando há muitos dígitos repetidos:

1000000000000
1019436871.42

Para melhorar a legibilidade, uma nova funcionalidade da linguagem JavaScript permite sublinhados como separadores em literais numéricos. Assim, o exemplo acima agora pode ser reescrito para agrupar os dígitos por milhar, por exemplo:

`String.prototype.matchAll`

· Leitura de 3 minutos
Mathias Bynens ([@mathias](https://twitter.com/mathias))

É comum aplicar repetidamente a mesma expressão regular em uma string para obter todas as correspondências. Até certo ponto, isso já é possível hoje usando o método String#match.

Neste exemplo, encontramos todas as palavras que consistem apenas em dígitos hexadecimais e, em seguida, registramos cada correspondência:

const string = 'Números hex mágicos: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}

// Saída:
//
// 'DEADBEEF'
// 'CAFE'

No entanto, isso só fornece os substrings que correspondem. Normalmente, você não quer apenas os substrings, mas também informações adicionais, como o índice de cada substring ou os grupos de captura dentro de cada correspondência.

Já é possível alcançar isso escrevendo seu próprio loop e rastreando os objetos de correspondência você mesmo, mas é um pouco chato e não muito prático:

const string = 'Números hex mágicos: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}

// Saída:
//
// [ 'DEADBEEF', índice: 19, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]
// [ 'CAFE', índice: 28, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]

A nova API String#matchAll torna isso mais fácil do que nunca: agora você pode escrever um simples loop for-of para obter todos os objetos de correspondência.

const string = 'Números hex mágicos: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}

// Saída:
//
// [ 'DEADBEEF', índice: 19, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]
// [ 'CAFE', índice: 28, entrada: 'Números hex mágicos: DEADBEEF CAFE' ]

String#matchAll é especialmente útil para expressões regulares com grupos de captura. Ele fornece todas as informações para cada correspondência individual, incluindo os grupos de captura.

const string = 'Repositórios favoritos do GitHub: tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} em ${match.index} com '${match.input}'`);
console.log(`→ proprietário: ${match.groups.owner}`);
console.log(`→ repositório: ${match.groups.repo}`);
}

Exportações de namespace de módulo

· Leitura de um minuto
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Em módulos JavaScript, já era possível usar a seguinte sintaxe:

import * as utils from './utils.mjs';

No entanto, não existia uma sintaxe export simétrica… até agora:

export * as utils from './utils.mjs';

Isso é equivalente ao seguinte:

import * as utils from './utils.mjs';
export { utils };

Campos de classe públicos e privados

· Leitura de 5 minutos
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Várias propostas expandem a sintaxe de classes JavaScript existente com novas funcionalidades. Este artigo explica a nova sintaxe de campos de classe públicos no V8 v7.2 e Chrome 72, assim como a futura sintaxe de campos de classe privados.

Aqui está um exemplo de código que cria uma instância de uma classe chamada IncreasingCounter:

const counter = new IncreasingCounter();
counter.value;
// registra 'Obtendo o valor atual!'
// → 0
counter.increment();
counter.value;
// registra 'Obtendo o valor atual!'
// → 1

Note que acessar o value executa algum código (isto é, registra uma mensagem) antes de retornar o resultado. Agora pergunte-se, como você implementaria esta classe em JavaScript? 🤔

Sintaxe de classe ES2015

Veja como IncreasingCounter poderia ser implementado usando a sintaxe de classe ES2015:

class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Obtendo o valor atual!');
return this._count;
}
increment() {
this._count++;
}
}

A classe instala o getter value e um método increment no protótipo. Mais interessantemente, a classe tem um construtor que cria uma propriedade de instância _count e define seu valor padrão como 0. Atualmente, costumamos usar o prefixo de sublinhado para indicar que _count não deve ser usado diretamente pelos consumidores da classe, mas isso é apenas uma convenção; não é realmente uma propriedade “privada” com semântica especial aplicada pela linguagem.