Inovações em JavaScript: resultados do Google I / O 2019. Parte 2

Hoje estamos publicando a segunda parte da tradução de inovações em JavaScript. Aqui falamos sobre separadores de dígitos de números, sobre números BigInt, sobre como trabalhar com matrizes e objetos, sobre globalThis , sobre classificação, sobre a API de internacionalização e sobre promessas.



A primeira parte

Separadores de números


Números longos encontrados nos programas são difíceis de ler. Por exemplo, 1000000000 é um bilhão em decimal. Mas de relance é difícil de entender. Portanto, se o leitor do programa encontrar algo semelhante - ele, para percebê-lo corretamente, terá que considerar cuidadosamente zeros.

No JavaScript moderno, você pode usar o separador de dígitos dos números - um sublinhado ( _ ), cujo uso melhora a legibilidade de números longos. Veja como os números escritos usando um delimitador aparecem no código:

 var billion = 1_000_000_000; console.log( billion ); // 1000000000 

Separadores podem ser usados ​​para dividir arbitrariamente números em fragmentos. JavaScript, ao lidar com números, simplesmente ignora separadores. Eles podem ser usados ​​ao escrever qualquer número: números inteiros, ponto flutuante, binário, hexadecimal, octal.

 console.log( 1_000_000_000.11 ); // 1000000000.11 console.log( 1_000_000_000.1_012 ); // 1000000000.1012 console.log( 0xFF_00_FF ); // 16711935 console.log( 0b1001_0011 ); // 147 console.log( 0o11_17 ); // 591 

→ Suporte


  • TC39: Etapa 3
  • Chrome: mais de 75
  • Nó: 12,5+

Tipo de dados Bigint


Os números em JavaScript são criados usando a função construtora Number .

O valor máximo que pode ser representado com segurança usando o tipo de dados Number é (2⁵³ - 1), ou seja, 9007199254740991. Esse número pode ser visto usando a construção Number.MAX_SAFE_INTEGER .

Observe que, quando um literal numérico é usado no código JS, o JavaScript o processa, criando um objeto baseado nele, usando o construtor Number . O protótipo deste objeto contém métodos para trabalhar com números. Isso acontece com todos os tipos de dados primitivos .

O que acontecerá se tentarmos adicionar algo ao número 9007199254740991?

 console.log( Number.MAX_SAFE_INTEGER ); // 9007199254740991 console.log( Number.MAX_SAFE_INTEGER + 10 ); // 9007199254741000 

O resultado da adição de Number.MAX_SAFE_INTEGER e 10, a segunda saída do console.log() , está incorreto. Isso ocorre porque o JS não pode executar corretamente cálculos com números maiores que o valor de Number.MAX_SAFE_INTEGER . Você pode lidar com esse problema usando o bigint dados bigint .

O tipo bigint permite representar números inteiros maiores que Number.MAX_SAFE_INTEGER . Trabalhar com valores BigInt é semelhante a trabalhar com valores do tipo Number . Em particular, o idioma possui a função BigInt() , com a qual você pode criar os valores correspondentes, e o tipo de dados bigint primitivo bigint usado para representar números inteiros grandes.

 var large = BigInt( 9007199254740991 ); console.log( large ); // 9007199254740991n console.log( typeof large ); // bigint 

O JavaScript adiciona n ao final dos literais do BigInt. Para nós, isso significa que esses literais podem ser escritos adicionando n ao final dos números inteiros.

Agora que temos os números BigInt à nossa disposição, podemos realizar operações matemáticas com segurança em grandes números do tipo bigint .

 var large = 9007199254740991n; console.log( large + 10n ); // 9007199254741001n 

Um número de número de tipo não é igual a um número de tipo bigint . Em particular, estamos falando do fato de que os números BigInt podem ser apenas números inteiros. Como resultado, verifica-se que você não pode executar operações aritméticas que usam os tipos bigint e number .

Note-se que a função BigInt() pode assumir vários números: decimal, binário, hexadecimal, octal. Dentro desta função, eles serão convertidos em números, para a representação da qual o sistema de números decimais é usado.

O tipo bigint também suporta separadores de bits:

 var large = 9_007_199_254_741_001n; console.log( large ); // 9007199254741001n 

→ Suporte


  • TC39: Etapa 3
  • Chrome: mais de 67
  • Nó: 10.4+
  • Firefox: 68+

Novos métodos de matriz: .flat () e .flatMap ()


Aqui, falaremos sobre os novos métodos de protótipo para o objeto Array - os .flat() e .flatMap() .

▍ método .flat ()


Agora, os objetos do tipo Array têm um novo método - .flat(n) . Ele retorna uma nova matriz, permitindo elevar recursivamente os elementos das matrizes para o nível especificado n . Por padrão, n é 1. Esse método pode transmitir n igual a Infinity , o que permite converter uma matriz com matrizes aninhadas em uma matriz unidimensional.

 var nums = [1, [2, [3, [4, 5]]]]; console.log( nums.flat() ); // [1, 2, [3, [4,5]]] console.log( nums.flat(2) ); // [1, 2, 3, [4,5]] console.log( nums.flat(Infinity) ); // [1, 2, 3, 4, 5] 

→ Suporte


  • TC39: Etapa 4
  • Chrome: mais de 69
  • Nó: 11+
  • Firefox: 62+

▍ método .flatMap ()


Ao resolver tarefas diárias, o programador às vezes pode precisar processar a matriz usando o método .map() com sua subsequente transformação em uma estrutura plana. Por exemplo, crie uma matriz contendo os números e quadrados desses números:

 var nums = [1, 2, 3]; var squares = nums.map( n => [ n, n*n ] ) console.log( squares ); // [[1,1],[2,4],[3,9]] console.log( squares.flat() ); // [1, 1, 2, 4, 3, 9] 

A solução para esse problema pode ser simplificada usando o método .flatMap() . Ele converte as matrizes retornadas pela função de retorno de chamada passada para ele, assim como eles converteriam seu método .flat() com o parâmetro n igual a 1.

 var nums = [1, 2, 3]; var makeSquare = n => [ n, n*n ]; console.log( nums.flatMap( makeSquare ) ); // [1, 1, 2, 4, 3, 9] 

→ Suporte


  • TC39: Etapa 4
  • Chrome: mais de 69
  • Nó: 11+
  • Firefox: 62+

Método Object.fromEntries ()


É possível extrair pares de tipos de chaves de um objeto : pode ser usado usando o Object Method estático, que retorna uma matriz, cada elemento da qual é uma matriz contendo, como o primeiro elemento, uma chave e como o segundo - um valor.

 var obj = { x: 1, y: 2, z: 3 }; var objEntries = Object.entries( obj ); console.log( objEntries ); // [["x", 1],["y", 2],["z", 3]] 

Agora temos à nossa disposição um método estático Object.fromEntries() , que nos permite converter uma estrutura semelhante novamente em um objeto.

 var entries = [["x", 1],["y", 2],["z", 3]]; var obj = Object.fromEntries( entries ); console.log( obj ); // {x: 1, y: 2, z: 3} 

O método entries() foi usado para facilitar a filtragem e o mapeamento dos dados armazenados nos objetos. O resultado é uma matriz. Mas, até agora, a tarefa de converter essa matriz em um objeto não teve uma solução bonita. É para resolver esse problema que você pode usar o método Object.fromEntries() .

 var obj = { x: 1, y: 2, z: 3 }; // [["x", 1],["y", 2],["z", 3]] var objEntries = Object.entries( obj ); // [["x", 1],["z", 3]] var filtered = objEntries.filter( ( [key, value] ) => value % 2 !== 0 //  ,     ); console.log( Object.fromEntries( filtered ) ); // {x: 1, z: 3} 

Se a estrutura Map : data for usada para armazenar a : pairs : os dados nela serão armazenados na ordem em que foram adicionados a ela. Ao mesmo tempo, como os dados são armazenados se assemelha à matriz retornada pelo método Object.entries() . O método Object.fromEntries() é fácil de usar para transformar estruturas de dados do Map em objetos.

 var m = new Map([["x", 1],["y", 2],["z", 3]]); console.log( m ); // {"x" => 1, "y" => 2, "z" => 3} console.log( Object.fromEntries( m ) ); // {x: 1, y: 2, z: 3} 

→ Suporte


  • TC39: Etapa 4
  • Chrome: mais de 73
  • Nó: 12+
  • Firefox: 63+

▍ Propriedade global globalThis


Estamos familiarizados com a this usada no JavaScript. Não possui algum valor fixo. Em vez disso, o significado this depende do contexto em que é acessado. Em qualquer ambiente, a this aponta para um objeto global quando é acessado a partir do contexto do nível mais alto. Este é o significado global this .

No JavaScript baseado em navegador, por exemplo, o valor global para this é o objeto de window . Você pode verificar isso usando a construção console.log(this) no nível superior do arquivo JavaScript (no contexto mais externo) ou no console JS do navegador.


Acessando isso no console do navegador

O valor global this no Node.js aponta para um objeto global . Dentro de um trabalhador da Web, ele aponta para o próprio trabalhador. No entanto, obter this valor global não é uma tarefa fácil. O fato é que você não pode se referir a this nenhum lugar. Por exemplo, se você tentar fazer isso no construtor da classe, isso indica que this aponta para uma instância da classe correspondente.

Em alguns ambientes, a this self pode ser usada para acessar o valor global this . Essa palavra-chave desempenha o mesmo papel que os mecanismos para acessar esse valor nos navegadores, no Node.js e nos trabalhadores da Web. Usando o conhecimento de como o valor global this é chamado em ambientes diferentes, você pode criar uma função que retorne esse valor:

 const getGlobalThis = () => { if (typeof self !== 'undefined') return self; if (typeof window !== 'undefined') return window; if (typeof global !== 'undefined') return global; if (typeof this !== 'undefined') return this; throw new Error('Unable to locate global `this`'); }; var globalThis = getGlobalThis(); 

Diante de nós, há um polyfill primitivo para obter this objeto global. Leia mais sobre isso aqui . O JavaScript agora possui a palavra-chave globalThis . Ele fornece uma maneira universal de acessar o valor global this para diferentes ambientes e não depende da localização do programa a partir do qual é acessado.

 var obj = { fn: function() {  console.log( 'this', this === obj ); // true  console.log( 'globalThis', globalThis === window ); // true } }; obj.fn(); 

→ Suporte


  • TC39: Etapa 3
  • Chrome: mais de 71
  • Nó: 12+
  • Firefox: 65+

Classificação estável


O padrão ECMAScript não oferece um algoritmo de classificação de matriz específico que os mecanismos JavaScript devem implementar. Ele descreve apenas a API usada para classificação. Como resultado, usando diferentes mecanismos JS, é possível encontrar diferenças no desempenho das operações de classificação e na estabilidade (estabilidade) dos algoritmos de classificação.

Agora, o padrão exige que as matrizes de classificação sejam estáveis. Detalhes sobre a estabilidade da classificação podem ser encontrados aqui . A essência dessa característica dos algoritmos de classificação é a seguinte. O algoritmo é estável se o resultado da classificação, que é uma matriz modificada, contiver elementos com os mesmos valores que não foram afetados pela classificação na mesma ordem em que foram colocados na matriz original. Considere um exemplo:

 var list = [  { name: 'Anna', age: 21 },  { name: 'Barbra', age: 25 },  { name: 'Zoe', age: 18 },  { name: 'Natasha', age: 25 } ]; //      age [  { name: 'Natasha', age: 25 }  { name: 'Barbra', age: 25 },  { name: 'Anna', age: 21 },  { name: 'Zoe', age: 18 }, ] 

Aqui, a matriz da list contém os objetos é classificada pelo campo de age desses objetos. Na matriz da list , um objeto com a propriedade name igual a Barbra está localizado antes do objeto com a propriedade name igual a Natasha . Como os valores de age desses objetos são iguais, poderíamos esperar que na matriz classificada esses elementos retenham a ordem de arranjo anterior em relação um ao outro. No entanto, na prática, isso não poderia ser esperado. Como exatamente a matriz classificada será formada dependia inteiramente do mecanismo JS usado.

Agora, todos os navegadores modernos e o Node.js usam um algoritmo de classificação estável, chamado ao acessar o método da matriz .sort() . Isso permite obter sempre, para os mesmos dados, o mesmo resultado:

 //    [  { name: 'Barbra', age: 25 },  { name: 'Natasha', age: 25 }  { name: 'Anna', age: 21 },  { name: 'Zoe', age: 18 }, ] 

No passado, alguns mecanismos JS suportavam classificação estável, mas apenas para pequenas matrizes. Para melhorar o desempenho ao processar matrizes grandes, eles poderiam usar algoritmos mais rápidos e sacrificar a estabilidade da classificação.

→ Suporte


  • Chrome: mais de 70
  • Nó: 12+
  • Firefox: 62+

API de internacionalização


A API de internacionalização destina-se a organizar comparações de cadeias, para formatar números, datas e horas, como é habitual em vários padrões regionais (localidades). O acesso a essa API é organizado por meio do objeto Intl . Este objeto fornece construtores para criar objetos classificadores e objetos que formatam dados. A lista de códigos de idioma suportados pelo objeto Intl pode ser encontrada aqui .

▍Intl.RelativeTimeFormat ()


Em muitas aplicações, muitas vezes é necessário imprimir a hora em um formato relativo. Pode parecer "5 minutos atrás", "ontem", "1 minuto atrás" e assim por diante. Se os materiais do site forem traduzidos para diferentes idiomas, você deverá incluir todas as combinações possíveis de construções relativas que descrevem o horário na montagem do site.

O JS agora possui o Intl.RelativeTimeFormat(locale, config) , que permite criar sistemas de formatação de data e hora para vários Intl.RelativeTimeFormat(locale, config) . Em particular, estamos falando de objetos que possuem um método .format(value, unit) , que permite gerar vários registros de data e hora relativos. É assim:

 // español ( ) var rtfEspanol= new Intl.RelativeTimeFormat('es', {  numeric: 'auto' }); console.log( rtfEspanol.format( 5, 'day' ) ); // dentro de 5 días console.log( rtfEspanol.format( -5, 'day' ) ); // hace 5 días console.log( rtfEspanol.format( 15, 'minute' ) ); // dentro de 15 minutos 

→ Suporte


  • TC39: Etapa 3
  • Chrome: mais de 71
  • Nó: 12+
  • Firefox: 65+

▍Intl.ListFormat ()


O construtor Intl.ListFormat permite combinar itens de lista usando as palavras and ( ) e or ( ). Ao criar o objeto correspondente, o construtor recebe o código do idioma e o objeto com os parâmetros. Seu parâmetro de type pode ser conjunction , disjunction e unit . Por exemplo, se queremos combinar os elementos da [apples, mangoes, bananas] usando um objeto de conjunção, obtemos uma sequência da forma apples, mangoes and bananas . Se usarmos um objeto de disjunção, obteremos uma série de apples, mangoes or bananas .

O objeto criado pelo construtor Intl.ListFormat possui um método Intl.ListFormat .format(list) que combina listas. Considere um exemplo:

 // español ( ) var lfEspanol = new Intl.ListFormat('es', {  type: 'disjunction' }); var list = [ 'manzanas', 'mangos', 'plátanos' ]; console.log( lfEspanol.format( list ) ); // manzanas, mangos o plátanos 

→ Suporte



▍Intl.Locale ()


O conceito de "padrão regional" é geralmente muito mais do que apenas o nome de um idioma. Isso pode incluir o tipo de calendário, informações sobre os ciclos de tempo usados ​​e os nomes dos idiomas. O construtor Intl.Locale(localeId, config) usado para criar cadeias de caracteres de Intl.Locale(localeId, config) formatadas com base no objeto de config passado para ele.

Intl.Locale objeto criado usando o Intl.Locale contém todas as configurações regionais especificadas. Seu método .toString() produz uma sequência padrão regional formatada.

 const krLocale = new Intl.Locale( 'ko', {  script: 'Kore', region: 'KR',  hourCycle: 'h12', calendar: 'gregory' } ); console.log( krLocale.baseName ); // ko-Kore-KR console.log( krLocale.toString() ); // ko-Kore-KR-u-ca-gregory-hc-h12 

Aqui você pode ler sobre identificadores e tags de localidade em Unicode.

→ Suporte


  • TC39: Etapa 3
  • Chrome: mais de 74
  • Nó: 12+

Promessas


A partir de agora, o JS possui os métodos estáticos Promise.all() e Promise.race() . O Promise.all([...promises]) retorna uma promessa que foi resolvida com êxito depois que todas as promessas passadas ao método como argumento são resolvidas. Essa promessa é rejeitada no caso de pelo menos uma das promessas transferidas para ela ser rejeitada. O Promise.race([...promises]) retorna uma promessa, que é resolvida após a resolução de qualquer uma das promessas transferidas e é rejeitada se pelo menos uma dessas promessas for rejeitada.

A comunidade de desenvolvedores de JS estava desesperada por um método estático, a promessa retornada que seria resolvida depois que todas as promessas passadas fossem concluídas (permitidas ou rejeitadas). Além disso, precisávamos de um método semelhante ao race() , que retornasse uma promessa aguardando a resolução de qualquer uma das promessas passadas.

Método Promise.allSettled ()


O método Promise.allSettled() aceita uma variedade de promessas. A promessa retornada por ele é permitida depois que todas as promessas são rejeitadas ou permitidas. O resultado é que a promessa retornada por esse método não precisa de um catch .

O fato é que essa promessa é sempre resolvida com sucesso. O bloco then recebe status e value de cada promessa na ordem em que aparecem.

 var p1 = () => new Promise(  (resolve, reject) => setTimeout( () => resolve( 'val1' ), 2000 ) ); var p2 = () => new Promise(  (resolve, reject) => setTimeout( () => resolve( 'val2' ), 2000 ) ); var p3 = () => new Promise(  (resolve, reject) => setTimeout( () => reject( 'err3' ), 2000 ) ); var p = Promise.allSettled( [p1(), p2(), p3()] ).then(  ( values ) => console.log( values ) ); //  [ {status: "fulfilled", value: "val1"}  {status: "fulfilled", value: "val2"}  {status: "rejected", value: "err3"} ] 

→ Suporte



▍ Método Promise.any ()


O método Promise.any() é semelhante ao Promise.race() , mas a promessa retornada por ele não executa o catch quando uma das promessas passadas para esse método é rejeitada.

Em vez disso, ele aguarda a resolução de todas as promessas. Se nenhuma promessa foi permitida, o bloco catch será executado. Se alguma das promessas for resolvida com êxito, then será executada.

Sumário


Neste artigo, vimos algumas das inovações em JavaScript discutidas na conferência Google I / O 2019 . Esperamos que você encontre algo útil entre eles.

Caros leitores! O que você sente falta especialmente em JavaScript?

Source: https://habr.com/ru/post/pt464131/


All Articles