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

O material, a primeira parte da tradução que estamos publicando hoje, é dedicado aos novos recursos JavaScript padrão que foram discutidos na conferência Google I / O 2019 . Em particular, aqui falaremos sobre expressões regulares, sobre campos de classe, sobre como trabalhar com strings.



Verificações de expressão regular


Expressões regulares (Expressão Regular, para abreviação - RegEx ou RegExp) é uma poderosa tecnologia de processamento de strings implementada em muitas linguagens de programação. Expressões regulares são muito úteis nos casos em que você precisa, por exemplo, procurar fragmentos de cadeias por padrões complexos. Até recentemente, a implementação de JavaScript de expressões regulares tinha tudo, menos olhar para trás.

Para entender o que é uma verificação retrospectiva, primeiro vamos falar sobre os cabeçotes de impressão que já são compatíveis com JavaScript.

A segunda parte

Check Verificação prévia


A sintaxe das verificações iniciais em expressões regulares permite procurar fragmentos de seqüências de caracteres quando se sabe que outros fragmentos estão à direita deles. Por exemplo, ao trabalhar com as seqüências MangoJuice, VanillaShake, GrapeJuice você pode usar a sintaxe da verificação inicial positiva para encontrar as palavras imediatamente seguidas pela palavra Juice . No nosso caso, essas são as palavras Mango e Grape .

Existem dois tipos de verificações iniciais. Essas são visões positivas e negativas.

Verificação positiva de chumbo


Uma verificação inicial positiva é usada para procurar linhas à direita das quais são outras linhas anteriormente conhecidas. Veja como é a sintaxe da expressão regular usada para esta verificação:

 /[a-zA-Z]+(?=Juice)/ 

Este modelo permite selecionar palavras que consistem em letras minúsculas ou maiúsculas, seguidas pela palavra Juice . Não confunda estruturas que descrevem verificações iniciais e retrospectivas com grupos de captura. Embora as condições dessas verificações sejam escritas entre parênteses, o sistema não as captura. Vejamos um exemplo de uma verificação de lead positiva.

 const testString = "MangoJuice, VanillaShake, GrapeJuice"; const testRegExp = /[a-zA-Z]+(?=Juice)/g; const matches = testString.match( testRegExp ); console.log( matches ); // ["Mango", "Grape"] 

Verificação negativa de lead


Se considerarmos, usando a linha acima, o mecanismo de ação dos cheques iniciais negativos, verifica-se que eles permitem que você encontre palavras à direita das quais não há palavra Juice . A sintaxe das verificações iniciais negativas é semelhante à sintaxe das verificações positivas. No entanto, existe uma característica: o símbolo = (igual) muda para um símbolo ! (ponto de exclamação). Aqui está o que parece:

 /[a-zA-Z]+(?!Juice)/ 

Essa expressão regular permite selecionar todas as palavras à direita das quais não há palavra Juice . Porém, ao aplicar esse modelo, todas as palavras da linha serão selecionadas ( MangoJuice, VanillaShake, GrapeJuice ). O fato é que, de acordo com o sistema, nenhuma palavra aqui termina com Juice . Como resultado, para alcançar o resultado desejado, é necessário esclarecer a expressão regular e reescrevê-la da seguinte maneira:

 /(Mango|Vanilla|Grape)(?!Juice)/ 

O uso desse modelo permite selecionar as palavras Mango , Vanilla ou Grape , após as quais não há palavra Juice . Aqui está um exemplo:

 const testString = "MangoJuice, VanillaShake, GrapeJuice"; const testRegExp = /(Mango|Vanilla|Grape)(?!Juice)/g; const matches = testString.match( testRegExp ); console.log( matches ); // ["Vanilla"] 

Check Verificação Retrospectiva


Por analogia com a sintaxe das verificações iniciais, a sintaxe das verificações retrospectivas permite selecionar seqüências de caracteres apenas se à esquerda dessas seqüências houver um determinado padrão especificado. Por exemplo, ao processar a sequência FrozenBananas, DriedApples, FrozenFish podemos usar uma verificação retrospectiva positiva para encontrar palavras à esquerda das quais existe a palavra Frozen . No nosso caso, as palavras Bananas e Fish correspondem a essa condição.

Existem, como é o caso das verificações iniciais, verificações retrospectivas positivas (olhar positivo para trás) e verificações retrospectivas negativas (olhar negativo ou negativo).

Revisão retrospectiva positiva


Verificações retrospectivas positivas são usadas para procurar padrões à esquerda dos quais existem outros padrões. Aqui está um exemplo da sintaxe usada para descrever essas verificações:

 /(?<=Frozen)[a-zA-Z]+/ 

O símbolo < usado aqui, que não estava na descrição das verificações iniciais. Além disso, a condição na expressão regular está localizada não à direita do modelo de interesse para nós, mas à esquerda. Usando o modelo acima, você pode selecionar todas as palavras que começam com Frozen . Considere um exemplo:

 const testString = "FrozenBananas, DriedApples, FrozenFish"; const testRegExp = /(?<=Frozen)[a-zA-Z]+/g; const matches = testString.match( testRegExp ); console.log( matches ); // ["Bananas", "Fish"] 

Verificação retrospectiva negativa


O mecanismo de verificações retrospectivas negativas permite procurar padrões nas linhas à esquerda das quais não há um padrão especificado. Por exemplo, se você precisar selecionar palavras que não iniciam com Frozen na linha FrozenBananas, DriedApples, FrozenFish , tente usar esta expressão regular:

 /(?<!Frozen)[a-zA-Z]+/ 

Mas, como usar essa construção levará à seleção de todas as palavras da string, uma vez que nenhuma delas começa com Frozen , a expressão regular precisa ser esclarecida:

 /(?<!Frozen)(Bananas|Apples|Fish)/ 

Aqui está um exemplo:

 const testString = "FrozenBananas, DriedApples, FrozenFish"; const testRegExp = /(?<!Frozen)(Bananas|Apples|Fish)/g; const matches = testString.match( testRegExp ); console.log( matches ); // ["Apples"] 

→ Suporte


Esta e outras seções semelhantes fornecerão informações sobre o estágio de harmonização dos recursos JS descritos no Comitê Técnico 39 (Comitê Técnico 39, TC39), responsável pela ECMA International pelo suporte às especificações do ECMAScript. Essas seções também fornecerão dados sobre as versões do Chrome e do Node.js (e algumas vezes sobre a versão do Firefox), começando com as quais você pode usar os recursos correspondentes.


Campos de classe


Um campo de classe é uma nova construção de sintaxe usada para definir as propriedades de instâncias de classe (objetos) fora do construtor de classe. Existem dois tipos de campos de classe: campos de classe pública e privada.

Campos de classe pública


Até recentemente, as propriedades dos objetos tinham que ser definidas dentro do construtor da classe. Essas propriedades eram públicas (públicas). Isso significa que eles podem ser acessados ​​trabalhando com uma instância da classe (objeto). Aqui está um exemplo de declarar uma propriedade pública:

 class Dog {    constructor() {        this.name = 'Tommy';    } } 

Quando era necessário criar uma classe que estendesse uma certa classe pai, era necessário chamar super() no construtor da classe filho. Isso precisava ser feito antes que suas próprias propriedades pudessem ser adicionadas à classe filho. Aqui está o que parece:

 class Animal {} class Dog extends Animal {    constructor() {        super(); //  super   `this`          this.sound = 'Woof! Woof!';    }    makeSound() {        console.log( this.sound );    } } //    const tommy = new Dog(); tommy.makeSound(); // Woof! Woof! 

Graças à aparência da sintaxe dos campos públicos de uma classe, é possível descrever os campos de classe fora do construtor. O sistema fará uma chamada implícita para super() .

 class Animal {} class Dog extends Animal {    sound = 'Woof! Woof!'; //       makeSound() {        console.log( this.sound );    } } //    const tommy = new Dog(); tommy.makeSound(); // Woof! Woof! 

Ao chamar implicitamente super() ele passa todos os argumentos fornecidos pelo usuário ao criar a instância da classe (esse é o comportamento padrão do JavaScript, não há nada de especial nos campos da classe privada). Se o construtor da classe pai precisar de argumentos preparados de uma maneira especial, você precisará chamar super() . Dê uma olhada nos resultados da chamada implícita do construtor da classe pai ao criar uma instância da classe filho.

 class Animal {    constructor( ...args ) {        console.log( 'Animal args:', args );    } } class Dog extends Animal {    sound = 'Woof! Woof!'; //    makeSound() {        console.log( this.sound );    } } //    const tommy = new Dog( 'Tommy', 'Loves', 'Toys!' ); tommy.makeSound(); // Animal args: [ 'Tommy', 'Loves', 'Toys!' ] 

Fields Campos de classe privada


Como você sabe, em JavaScript, não há modificadores de acesso aos campos de classe, como public , private ou protected . Todas as propriedades dos objetos são públicas por padrão. Isso significa que o acesso a eles é ilimitado. O mais próximo de tornar uma propriedade de um objeto semelhante a uma propriedade privada é usar o tipo de dados Symbol . Isso permite ocultar as propriedades dos objetos do mundo exterior. Você pode ter usado nomes de propriedades prefixados com _ (sublinhado) para indicar que as propriedades correspondentes devem ser consideradas apenas destinadas ao uso no objeto. No entanto, esse é apenas um tipo de notificação para quem usará a instalação. Isso não resolve o problema de restrição real do acesso às propriedades.

Graças ao mecanismo de campos particulares de classes, é possível tornar as propriedades da classe acessíveis somente dentro desta classe. Isso leva ao fato de que eles não podem ser acessados ​​de fora e trabalhar com uma instância da classe (objeto). Pegue o exemplo anterior e tente acessar a propriedade da classe de fora, quando o prefixo _ foi usado.

 class Dog {    _sound = 'Woof! Woof!'; //            makeSound() {        console.log( this._sound );    } } //    const tommy = new Dog(); console.log( tommy._sound ); // Woof! Woof! 

Como você pode ver, o uso do prefixo _ não resolve nosso problema. Os campos privados das classes podem ser declarados da mesma maneira que os campos públicos, mas, em vez de um prefixo na forma de sublinhado, você deve adicionar um prefixo na forma de um sinal de cerquilha ( # ) a seus nomes. Uma tentativa de acesso não autorizado à propriedade privada do objeto declarada dessa maneira resultará no seguinte erro:

 SyntaxError: Undefined private field 

Aqui está um exemplo:

 class Dog {    #sound = 'Woof! Woof!'; //  -      makeSound() {        console.log( this.#sound );    } } //    const tommy = new Dog(); tommy.makeSound() // Woof! Woof! //console.log( tommy.#sound ); // SyntaxError 

Observe que propriedades privadas podem ser acessadas apenas a partir da classe em que são declaradas. Como resultado, as classes descendentes não podem usar diretamente propriedades semelhantes da classe pai.

Campos privados (e públicos) podem ser declarados sem escrever determinados valores neles:

 class Dog {    #name;    constructor( name ) {        this.#name = name;    }    showName() {        console.log( this.#name );    } } //    const tommy = new Dog( 'Tommy' ); tommy.showName(); // Tommy 

→ Suporte


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

Método de string .matchAll ()


O protótipo de tipo de dados String possui um método .match() que retorna uma matriz de fragmentos de string que correspondem à condição especificada pela expressão regular. Aqui está um exemplo usando este método:

 const colors = "#EEE, #CCC, #FAFAFA, #F00, #000"; const matchColorRegExp = /([A-Z0-9]+)/g; console.log( colors.match( matchColorRegExp ) ); // : ["EEE", "CCC", "FAFAFA", "F00", "000"] 

Ao usar esse método, no entanto, nenhuma informação adicional é fornecida (como índices) sobre os fragmentos encontrados da string. Se você remover o sinalizador g da expressão regular passada para o método .match() , ele retornará uma matriz que conterá informações adicionais sobre os resultados da pesquisa. No entanto, com essa abordagem, apenas o primeiro fragmento da string correspondente à expressão regular será encontrado.

 const colors = "#EEE, #CCC, #FAFAFA, #F00, #000"; const matchColorRegExp = /#([A-Z0-9]+)/; console.log( colors.match( matchColorRegExp ) ); // : (       ) ["#EEE", "EEE", index: 0, input: "<colors>"] 

Para obter algo semelhante, mas para vários fragmentos de uma string, você precisará usar o método de expressão regular .exec() . As construções necessárias para isso são mais complicadas do que aquela em que um método de cadeia única seria usado para obter resultados semelhantes. Em particular, aqui precisamos de um while que será executado até que .exec() retorne null . Usando essa abordagem, lembre-se de que .exec() não retorna um iterador.

 const colors = "#EEE, #CCC, #FAFAFA, #F00, #000"; const matchColorRegExp = /#([A-Z0-9]+)/g; //        , // Uncaught ReferenceError: match is not defined while( match = matchColorRegExp.exec( colors ) ) {  console.log( match ); } // : (       ) ["#EEE", "EEE", index: 0, input: "<colors>"] ["#CCC", "CCC", index: 6, input: "<colors>"] ["#FAFAFA", "FAFAFA", index: 12, input: "<colors>"] ["#F00", "F00", index: 21, input: input: "<colors>"] ["#000", "000", index: 27, input: input: "<colors>"] 

Para resolver esses problemas, agora podemos usar o método string .matchAll() , que retorna um iterador. Cada chamada para o método .next() deste iterador .next() próximo elemento dos resultados da pesquisa. Como resultado, o exemplo acima pode ser reescrito da seguinte maneira:

 const colors = "#EEE, #CCC, #FAFAFA, #F00, #000"; const matchColorRegExp = /#([A-Z0-9]+)/g; console.log( ...colors.matchAll( matchColorRegExp ) ); // : (       ) ["#EEE", "EEE", index: 0, input: "<colors>"] ["#CCC", "CCC", index: 6, input: "<colors>"] ["#FAFAFA", "FAFAFA", index: 12, input: "<colors>"] ["#F00", "F00", index: 21, input: input: "<colors>"] ["#000", "000", index: 27, input: input: "<colors>"] 

→ Suporte



Grupos nomeados em expressões regulares


O conceito de grupos na implementação de JavaScript de mecanismos de expressão regular é um pouco diferente da implementação de um conceito semelhante em outros idiomas. Ou seja, quando, usando JavaScript, o modelo RegEx é colocado entre parênteses (exceto quando parênteses são usados ​​para verificações retrospectivas ou avançadas), o modelo se torna um grupo.

Os fragmentos da sequência capturada pelo grupo serão refletidos nos resultados da aplicação da expressão regular.

No exemplo anterior, você pode ver que o primeiro elemento da matriz com os resultados da pesquisa é o que corresponde a toda a expressão regular e o segundo é o que corresponde ao grupo. Aqui está este elemento do array:

 ["#EEE", "EEE", index: 0, input: "<colors>"] 

Se houver vários grupos na expressão regular, eles entrarão nos resultados do processamento da sequência na ordem de sua descrição na expressão regular. Considere um exemplo:

 const str = "My name is John Doe."; const matchRegExp = /My name is ([az]+) ([az]+)/i; const result = str.match( matchRegExp );console.log( result ); //   result  null -   console.log( { firstName: result[1], lastName: result[2] } ); // : ["My name is John Doe", "John", "Doe", index: 0, input: "My name is John Doe.", groups: undefined] {firstName: "John", lastName: "Doe"} 

Aqui você pode ver que a primeira linha da saída é a linha inteira correspondente à expressão regular. O segundo e o terceiro elementos representam o que foi capturado pelos grupos.

O uso de grupos nomeados permite salvar quais grupos estão capturando no objeto de groups , cujos nomes de propriedades correspondem aos nomes atribuídos aos grupos.

 const str = "My name is John Doe."; const matchRegExp = /My name is (?<firstName>[az]+) (?<lastName>[az]+)/i; const result = str.match( matchRegExp ); console.log( result ); console.log( result.groups ); // : ["My name is John Doe", "John", "Doe", index: 0, input: "My name is John Doe.", groups: {firstName: "John", lastName: "Doe"}] {firstName: "John", lastName: "Doe"} 

Note-se que os grupos nomeados funcionam bem junto com o método .matchAll() .

→ Suporte


  • TC39: Etapa 4
  • Chrome: mais de 64
  • Nó: 10+

Para continuar ...

Caros leitores! Você já usou alguma das inovações em JavaScript descritas aqui?

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


All Articles