ES10 - Novas features do JavaScript
Neste post vamos ficar por dentro das novidades do ES10 (ECMAScript 2019) no JavaScript.
Se quiser ver o que rolou nas versões anteriores, recomendo a leitura dos posts: ES07, ES08 e ES09.
Tópicos
- Array.flat() e Array.flatMap()
- Object.fromEntries()
- String.trimStart() e String.trimEnd()
- Symbol.description()
- Optional Catch Binding
- Array.sort()
- Well-formed JSON.stringify()
- JSON superset
- toString() revision
- Conclusão
- Links
Array: flat e flatMap
- flat
flat vem do termo flatten, que significa achatar ou, dentro do contexto do, Array — nivelar. O método flat() cria uma cópia do array com todos os elementos nivelados, de forma recursiva até a profundidade especificada com o parâmetro opcional depth (profundidade).
Parâmetro opcional depth: o nível de profundidade especifica o quanto um array deve ser nivelado. Quando não especificado, o valor padrão é 1, não há limites para definir o valor, inclusive pode ser passado Infinity
para nivelar uma matriz multidimensional (aninhado — estrutura dentro da outra) em um simples array.
Sintaxe: array.flat(<depth>);
const arr1 = [ 1, 2, [3, 4] ];
console.log(arr1.flat());
// valor default é 1 então concatena com o primeiro nível
// [1, 2, 3, 4]
const arr2 = [ 1, 2, [3, 4, [5, 6] ] ];
console.log(arr2.flat());
// valor default é 1 então concatena com o primeiro nível
// [1, 2, 3, 4, [5, 6]]
const arr3 = [1, 2, [3, 4, [5, 6]]];
console.log(arr3.flat(2));
// com valor 2 para depth é criado uma cópia e todos ficam achatados em um nível só
// [1, 2, 3, 4, 5, 6]
const arr4 = [1, 2, [3, 4, [5, 6, [7, 8]]]];
console.log(arr4.flat(Infinity));
// [1, 2, 3, 4, 5, 6, 7, 8]
// Olha o Poder do flat, nem tente ver quantos níveis tem nesse array
const arrayComMuitosArrays = [1, 2, [3, 4, [5, 6, [7, [() => {}, () => {}], 8, ["a", "b"]]], [0n, 1n, 2n]]];
// Resultado - Tudo nivelado em um único array
// [ 1, 2, 3, 4, 5, 6, 7, [Function (anonymous)], [Function (anonymous)], 8, 'a', 'b', 0n, 1n, 2n ]
Existe uma alternativa ao flat com um pouco mais de código, se te interessar veja aqui: usando o reduce e concat.
- flatMap
É a combinação do map() com flat(), primeiro é executado o map() e depois o flat(1).
Sintaxe: array.flatMap()
Usando apenas o map.
console.log([1, 2, 3, 4].map((item) => [item * 2]));
// [ [ 2 ], [ 4 ], [ 6 ], [ 8 ] ]
Usando o flatMap
console.log([1, 2, 3, 4].flatMap((item) => [item * 2]));
// [ 2, 4, 6, 8 ]
O map retorna um array. Usando o flat, é feito o nivelamento. Observe o resultado do exemplo com map e depois do flatMap.
Object.fromEntries()
Transforma uma lista em objeto com par de chave e valor { key: value }
, sendo o primeiro valor a propriedade e o segundo o valor em si.
const array = [
["a", 1],
["b", 2],
];
const newObj = Object.fromEntries(array);
console.log(newObj);
// { a: 1, b: 2 }
Object.fromEntries() faz o oposto de Object.entries(), enquanto o primeiro método cria um objeto a partir do array, o segundo cria um array a partir do objeto.
console.log(newObj);
// { a: 1, b: 2 }
console.log(Object.entries(newObj));
// [ [ 'a', 1 ], [ 'b', 2 ] ]
trimStart() e trimEnd()
Usando o método trim(), o JavaScript já suportava remover todos os espaços em branco, tanto no começo quanto no final da String.
> ' Rocketseat '.trim()
//'Rocketseat'
Nessa versão foi introduzido a remoção de espaços em branco apenas no começo com trimStart() e no final com trimEnd().
Exemplos:
// trimStart()
> ' Rocketseat '.trimStart()
// 'Rocketseat '
// trimEnd()
> ' Rocketseat '.trimEnd()
// ' Rocketseat'
Para manter a compatibilidade com códigos legados, os métodos trimLeft() e trimRight() serão mantidos como apelidos para trimStart() e trimEnd().
Symbol.description
É uma propriedade somente leitura (read-only) que serve para descrever uma intenção no código. É utilizado na metaprogramação (programas para criar programas, por exemplo: Electron é um programa para criar app desktop), se quiser se aprofundar para ver a utilidade dessa feature leia aqui e aqui. Mas cotidianamente não é muito utilizado.
Sintaxe: Symbol.description
Criando um símbolo (Symbol) e verificando sua descrição:
const rocketSymbol = Symbol("Rocketseat");
console.log(rocketSymbol.description);
// Rocketseat
console.log(String(rocketSymbol));
// Symbol(Rocketseat)
Optional catch binding
A proposta foi remover a variável passada como parâmetro no try/catch(error), promovendo um código mais limpo, visto que em determinadas situações essa variável não é utilizada.
Sintaxe:
try {
// ···
} catch {
// ···
}
Antes dessa versão era obrigatório utilizar o parâmetro no catch:
try {
// ···
} catch (error) {
// ···
}
Embora seja possível omitir o parâmetro, não é aconselhável, é interessante ao menos fazer um console.log(error) desse erro para que você acompanhe o comportamento do programa.
Um possível caso de uso para essa funcionalidade é omitir o error quando for fazer um JSON.parse(str), que possui um erro previsível e único.
let jsonData;
try {
jsonData = JSON.parse(str);
} catch {
jsonData = DEFAULT_DATA;
}
Pode parecer interessante esse caso de uso, mas segundo o Dr. Axel Rauschmayer existe um problema: ele recomenda relançar o erro caso você cometa um erro de digitação que só aparece em tempo de execução (um dos motivos do TypeScript ser adotado, porque erro poderia ser evitado em tempo de compilação).
Exemplo, ao invés de chamar JSON.parse(str)
, você chama JSON.prase(str)
. O erro será silenciado, caindo no catch como se nada tivesse acontecido.
let jsonData;
try {
jsonData = JSON.prase(str); // Erro de sintaxe "prase"
} catch {
jsonData = DEFAULT_DATA;
}
// Erro omitido no catch:
// TypeError: JSON.prase is not a function
O recomendado seria implementar algo assim:
let jsonData;
try {
jsonData = JSON.parse(str);
} catch (err) {
if (err instanceof SyntaxError) {
jsonData = DEFAULT_DATA;
} else {
throw err;
}
}
Agora um outro possível erro seria relançado.
Array Sort
Nessa versão o método sort() está estável. Se os elementos são iguais, então o método não altera a ordem dos elementos. Isso trouxe um benefício para os testes unitários, uma vez que com essa ordem previsível e estável fica mais fácil manter os testes.
const arr = [
{ key: 'b', value: 1 },
{ key: 'a', value: 2 },
{ key: 'b', value: 3 },
];
arr.sort((x, y) => x.key.localeCompare(y.key, 'en-US'));
assert.deepEqual(arr, [
{ key: 'a', value: 2 },
{ key: 'b', value: 1 },
{ key: 'b', value: 3 },
]);
A V8 antes ela utilizava o QuickSort para arrays com mais de dez elementos, já na versão v.70 / Chrome 70, usa o algoritmo TimSort, trazendo uma melhoria na performance no navegador.
Para saber como utilizar o sort() de maneira correta, leia este artigo aqui.
Well-formed JSON.stringify
Foi feita uma melhoria no método JSON.stringify() para evitar que o método retorne strings Unicode malformados: "�".
// Exemplo
console.log(JSON.stringify("𝌆"));
// → '"𝌆"'
console.log(JSON.stringify("\\uD834\\uDF06"));
// → '"𝌆"' - BMP correspodente
// BMP sem o correspondente - Proposta:
console.log(JSON.stringify("\\uDF06\\uD834"));
// → '"\\\\udf06\\\\ud834"'
console.log(JSON.stringify("\\uDEAD"));
// → '"\\\\udead"'
// UTF-16
'😎'.length;
// 2
JSON superset
Foi incluída na linguagem a possibilidade de lidar com caracteres U+2028 LINE SEPARATOR
e U+2029 PARAGRAPH SEPARATOR
, algo que só era possível no JSON.
// A proposta foi não mais lançar a exceção para os caracteres mencionados
// Antes como era reconhecido como final de linha era lançado um erro de sintaxe
const sourceCode = '"\\u2028"';
eval(sourceCode); // SyntaxError
Nessa versão ficou decidido remover a restrição. Algo que simplifica a especificação, já que não precisam mais de regras separadas para strings do JavaScript e strings do JSON.
const json = '"\\u2028"';
JSON.parse(json); // OK
toString() revision
Foi feita uma revisão no método toString(). Quando o método toString() é chamado em uma função, por exemplo: sayHello.toString()
, são exibidos o código e os comentários, isto é, o código na íntegra.
function /* Diga Hello Educadamente */ sayHello(name) {
// Olá caro <name>
console.log(`Hello dear ${name}.`);
}
console.log(sayHello.toString());
Resultado:
❯ node index.js
function /* Diga Hello Educadamente */ sayHello(name) {
// Olá caro <name>
console.log(`Hello dear ${name}.`);
}
👊 Conclusão
As funcionalidades mais interessantes dessa versão são os novos métodos flat e flatMap, que simplificam muito para lidar com arrays, transformando uma matriz em um array de mesmo nível. fromEntries(), trimStart() e trimEnd() são bem úteis.
A melhoria no array.sort(), trazendo estabilidade, é excelente. As outras melhorias e suportes na linguagem agregam valor, melhorando a experiência do(a) dev JavaScript.
🔗 Links
Propostas do Comitê Técnico 39 - ECMAScript | TC39:
- https://github.com/tc39/proposal-flatMap
- https://github.com/tc39/proposal-string-left-right-trim
- https://github.com/tc39/proposal-object-from-entries
- https://github.com/tc39/proposal-Symbol-description
- https://github.com/tc39/proposal-optional-catch-binding
- https://github.com/tc39/proposal-well-formed-stringify
- https://github.com/tc39/proposal-json-superset
- https://tc39.es/Function-prototype-toString-revision/
Fontes:
- https://exploringjs.com/es2018-es2019/pt_es2019.html
- https://javascript.christmas/2019/7
- https://developer.mozilla.org/pt-BR/
- Thread no Twitter sobre o lançamento da versão ECMAScript 2019
- https://medium.com/trainingcenter/javascript-symbols-decifrando-o-mistério-383e359e64e3
E aí, o que achou do post?
Espero que tenha curtido! 💜
O aprendizado é contínuo e sempre haverá um próximo nível! 🚀