JavaScript-Innovationen: Ergebnisse von Google I / O 2019 Teil 1

Das Material, dessen erster Teil der Übersetzung wir heute veröffentlichen, ist den neuen Standard-JavaScript-Funktionen gewidmet, die auf der Google I / O 2019- Konferenz erörtert wurden. Insbesondere werden wir hier über reguläre Ausdrücke, über Klassenfelder und über die Arbeit mit Zeichenfolgen sprechen.



Überprüfungen des regulären Ausdrucks


Reguläre Ausdrücke (kurz RegEx oder RegExp) sind eine leistungsstarke String-Verarbeitungstechnologie, die in vielen Programmiersprachen implementiert ist. Reguläre Ausdrücke sind sehr nützlich, wenn Sie beispielsweise nach Fragmenten von Zeichenfolgen anhand komplexer Muster suchen müssen. Bis vor kurzem hatte die JavaScript-Implementierung regulärer Ausdrücke alles andere als Lookbackhinds.

Um zu verstehen, was eine retrospektive Überprüfung ist, sprechen wir zunächst über Lookaheads, die bereits in JavaScript unterstützt werden.

Der zweite Teil

▍ Vorabprüfung


Die Syntax führender Prüfungen in regulären Ausdrücken ermöglicht es Ihnen, nach Fragmenten von Zeichenfolgen zu suchen, wenn bekannt ist, dass sich andere Fragmente rechts davon befinden. Wenn Sie beispielsweise mit der Zeichenfolge MangoJuice, VanillaShake, GrapeJuice Sie die Syntax eines positiven führenden Checks verwenden, um die Wörter zu finden, auf die unmittelbar das Wort Juice folgt. In unserem Fall sind dies die Wörter Mango und Grape .

Es gibt zwei Arten von Leitprüfungen. Dies sind positive Lookaheads und negative Lookaheads.

Positive Bleiprüfung


Eine positive Leitprüfung wird verwendet, um nach Zeilen zu suchen, rechts davon andere, zuvor bekannte Zeilen sind. So sieht die für diese Prüfung verwendete Syntax für reguläre Ausdrücke aus:

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

Mit dieser Vorlage können Sie Wörter auswählen, die aus Klein- oder Großbuchstaben bestehen, gefolgt vom Wort Juice . Verwechseln Sie keine Strukturen, die führende und retrospektive Prüfungen beschreiben, mit Erfassungsgruppen. Obwohl die Bedingungen dieser Prüfungen in Klammern angegeben sind, erfasst das System sie nicht. Schauen wir uns ein Beispiel für eine positive Lead-Prüfung an.

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

Negative Bleiprüfung


Wenn wir anhand der obigen Zeile den Wirkungsmechanismus negativer Leitschecks betrachten, stellt sich heraus, dass Sie damit Wörter finden können, rechts von denen es kein Wort Juice . Die Syntax von negativen führenden Prüfungen ähnelt der Syntax von positiven Prüfungen. Es gibt jedoch ein Merkmal: Das Symbol = (gleich) ändert sich in ein Symbol ! (Ausrufezeichen). So sieht es aus:

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

Mit diesem regulären Ausdruck können Sie alle Wörter auswählen, rechts von denen es kein Wort Juice . Beim Anwenden einer solchen Vorlage werden jedoch alle Wörter in der Zeile ausgewählt ( MangoJuice, VanillaShake, GrapeJuice ). Tatsache ist, dass laut System hier kein einziges Wort mit Juice endet. Um das gewünschte Ergebnis zu erzielen, müssen Sie daher den regulären Ausdruck klarstellen und wie folgt umschreiben:

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

Mit dieser Vorlage können Sie die Wörter Mango , Vanilla oder Grape auswählen. Mango gibt es kein Wort Juice . Hier ist ein Beispiel:

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

▍ Rückblick


In Analogie zur Syntax führender Prüfungen können Sie mit der Syntax retrospektiver Prüfungen nur dann Zeichenfolgen auswählen, wenn links von diesen Folgen ein bestimmtes Muster steht. Wenn Sie beispielsweise die Zeichenfolge FrozenBananas, DriedApples, FrozenFish können Sie eine positive retrospektive Prüfung verwenden, um Wörter zu finden, links davon das Wort Frozen . In unserem Fall entsprechen die Wörter Bananas und Fish diesem Zustand.

Es gibt, wie es bei führenden Prüfungen der Fall ist, positive nachträgliche Prüfungen (positives Aussehen) und negative nachträgliche Prüfungen (negatives oder negatives Aussehen).

Positive retrospektive Bewertung


Positive retrospektive Überprüfungen werden verwendet, um nach Mustern zu suchen, links davon andere Muster sind. Hier ist ein Beispiel für die Syntax, mit der solche Überprüfungen beschrieben werden:

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

Hier wird das Symbol < verwendet, das nicht in der Beschreibung der führenden Prüfungen enthalten war. Außerdem befindet sich die Bedingung im regulären Ausdruck nicht rechts von der für uns interessanten Vorlage, sondern links. Mit der obigen Vorlage können Sie alle Wörter auswählen, die mit Frozen . Betrachten Sie ein Beispiel:

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

Negative retrospektive Prüfung


Mit dem Mechanismus negativer retrospektiver Überprüfungen können Sie in den Zeilen links nach Mustern suchen, für die kein Muster angegeben ist. Wenn Sie beispielsweise Wörter auswählen müssen, die nicht mit Frozen in der FrozenBananas, DriedApples, FrozenFish , können Sie versuchen, diesen regulären Ausdruck zu verwenden:

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

Da die Verwendung dieser Konstruktion jedoch zur Auswahl aller Wörter aus der Zeichenfolge führt, muss der reguläre Ausdruck geklärt werden, da keines von ihnen mit Frozen beginnt.

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

Hier ist ein Beispiel:

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

→ Unterstützung


Dieser und ähnliche Abschnitte enthalten Informationen zum Stadium der Harmonisierung der beschriebenen Merkmale von JS im Technischen Komitee 39 (Technisches Komitee 39, TC39), das in ECMA International für die Unterstützung der ECMAScript-Spezifikationen verantwortlich ist. In solchen Abschnitten werden auch Daten zu Versionen von Chrome und Node.js (und manchmal zur Version von Firefox) bereitgestellt, mit denen Sie die entsprechenden Funktionen nutzen können.


Klassenfelder


Ein Klassenfeld ist ein neues Syntaxkonstrukt, mit dem die Eigenschaften von Klasseninstanzen (Objekten) außerhalb des Klassenkonstruktors definiert werden. Es gibt zwei Arten von Klassenfeldern: öffentliche Klassenfelder und private Klassenfelder.

▍Öffentliche Klassenfelder


Bis vor kurzem mussten die Eigenschaften von Objekten im Klassenkonstruktor definiert werden. Diese Eigenschaften waren öffentlich (öffentlich). Dies bedeutet, dass auf sie zugegriffen werden kann, indem mit einer Instanz der Klasse (Objekt) gearbeitet wird. Hier ist ein Beispiel für die Deklaration eines öffentlichen Eigentums:

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

Wenn eine Klasse erstellt werden musste, die eine bestimmte übergeordnete Klasse erweitert, musste im Konstruktor der untergeordneten Klasse super() aufgerufen werden. Dies musste durchgeführt werden, bevor der untergeordneten Klasse eigene Eigenschaften hinzugefügt werden konnten. So sieht es aus:

 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! 

Dank des Erscheinungsbilds der Syntax öffentlicher Felder einer Klasse ist es möglich, Klassenfelder außerhalb des Konstruktors zu beschreiben. Das System ruft implizit super() .

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

Beim impliziten Aufruf von super() werden alle Argumente, die der Benutzer beim Erstellen der Klasseninstanz angegeben hat, an ihn übergeben (dies ist das Standardverhalten von JavaScript, private Klassenfelder haben nichts Besonderes). Wenn der Konstruktor der übergeordneten Klasse Argumente benötigt, die auf spezielle Weise vorbereitet wurden, müssen Sie super() selbst aufrufen. Sehen Sie sich die Ergebnisse des impliziten Konstruktoraufrufs der übergeordneten Klasse an, wenn Sie eine Instanz der untergeordneten Klasse erstellen.

 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!' ] 

▍ Private Klassenfelder


Wie Sie wissen, gibt es in JavaScript keine Zugriffsmodifikatoren für Klassenfelder wie public , private oder protected . Alle Eigenschaften von Objekten sind standardmäßig öffentlich. Dies bedeutet, dass der Zugriff auf sie unbegrenzt ist. Am nächsten daran, eine Eigenschaft eines Objekts einer privaten Eigenschaft ähnlich zu machen, ist die Verwendung des Datentyps Symbol . Auf diese Weise können Sie die Eigenschaften von Objekten vor der Außenwelt verbergen. Möglicherweise haben Sie Eigenschaftsnamen verwendet, denen ein _ (Unterstrich) vorangestellt ist, um anzugeben, dass die entsprechenden Eigenschaften nur für die Verwendung innerhalb des Objekts vorgesehen sind. Dies ist jedoch nur eine Art Benachrichtigung für diejenigen, die die Einrichtung nutzen werden. Dies löst nicht das Problem einer echten Einschränkung des Zugangs zu Immobilien.

Dank des Mechanismus privater Klassenfelder ist es möglich, die Klasseneigenschaften nur innerhalb dieser Klasse zugänglich zu machen. Dies führt dazu, dass von außen nicht auf sie zugegriffen werden kann und mit einer Instanz der Klasse (Objekt) gearbeitet wird. Nehmen Sie das vorherige Beispiel und versuchen Sie, von außen auf die Eigenschaft der Klasse zuzugreifen, wenn das Präfix _ verwendet wurde.

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

Wie Sie sehen können, löst die Verwendung des Präfixes _ unser Problem nicht. Private Felder von Klassen können wie öffentliche Felder deklariert werden. Anstelle eines Präfixes in Form eines Unterstrichs müssen Sie ihren Namen jedoch ein Präfix in Form eines Nummernzeichens ( # ) hinzufügen. Ein Versuch eines nicht autorisierten Zugriffs auf das auf diese Weise deklarierte Privateigentum des Objekts führt zu folgendem Fehler:

 SyntaxError: Undefined private field 

Hier ist ein Beispiel:

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

Beachten Sie, dass auf private Eigenschaften nur von der Klasse aus zugegriffen werden kann, in der sie deklariert sind. Daher können Nachkommenklassen ähnliche Eigenschaften der übergeordneten Klasse nicht direkt verwenden.

Private (und öffentliche) Felder können deklariert werden, ohne bestimmte Werte in sie zu schreiben:

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

→ Unterstützung


  • TC39: Stufe 3
  • Chrome: 74+
  • Knoten: 12+

String-Methode .matchAll ()


Der Prototyp des String Datentyps verfügt über eine .match() -Methode, die ein Array von String-Fragmenten zurückgibt, die der durch den regulären Ausdruck angegebenen Bedingung entsprechen. Hier ist ein Beispiel mit dieser Methode:

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

Bei Verwendung dieser Methode werden jedoch keine zusätzlichen Informationen (wie Indizes) zu den gefundenen Fragmenten der Zeichenfolge angegeben. Wenn Sie das g Flag aus dem regulären Ausdruck entfernen, der an die .match() -Methode übergeben wird, wird ein Array zurückgegeben, das zusätzliche Informationen zu den Suchergebnissen enthält. Bei diesem Ansatz wird jedoch nur das erste Fragment der Zeichenfolge gefunden, das dem regulären Ausdruck entspricht.

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

Um etwas Ähnliches zu erhalten, müssen Sie für mehrere Fragmente eines Strings die Methode des regulären Ausdrucks .exec() . Die dafür erforderlichen Konstruktionen sind komplizierter als die, bei denen eine einzelne Zeichenfolgenmethode verwendet würde, um ähnliche Ergebnisse zu erzielen. Insbesondere benötigen wir hier eine while , die ausgeführt wird, bis .exec() null zurückgibt. .exec() diesem Ansatz, dass .exec() keinen Iterator .exec() .

 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>"] 

Um solche Probleme zu lösen, können wir jetzt die String-Methode .matchAll() , die einen Iterator zurückgibt. Jeder Aufruf der .next() -Methode dieses Iterators .next() nächste Element aus den Suchergebnissen zurück. Infolgedessen kann das obige Beispiel wie folgt umgeschrieben werden:

 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>"] 

→ Unterstützung



Benannte Gruppen in regulären Ausdrücken


Das Konzept von Gruppen in der JavaScript-Implementierung von Mechanismen für reguläre Ausdrücke unterscheidet sich geringfügig von der Implementierung eines ähnlichen Konzepts in anderen Sprachen. Wenn die RegEx-Vorlage unter Verwendung von JavaScript in Klammern gesetzt wird (außer wenn Klammern für retrospektive oder erweiterte Prüfungen verwendet werden), wird die Vorlage zu einer Gruppe.

Die von der Gruppe erfassten Fragmente der Zeichenfolge spiegeln sich in den Ergebnissen der Anwendung des regulären Ausdrucks wider.

Im vorherigen Beispiel konnten Sie sehen, dass das erste Element des Arrays mit den Suchergebnissen dasjenige ist, das dem gesamten regulären Ausdruck entspricht, und das zweite Element dasjenige, das der Gruppe entspricht. Hier ist dieses Array-Element:

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

Wenn der reguläre Ausdruck mehrere Gruppen enthält, werden die Ergebnisse der Verarbeitung der Zeichenfolge in der Reihenfolge ihrer Beschreibung im regulären Ausdruck angezeigt. Betrachten Sie ein Beispiel:

 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"} 

Hier sehen Sie, dass die erste Zeile der Ausgabe die gesamte Zeile ist, die dem regulären Ausdruck entspricht. Das zweite und dritte Element repräsentieren, was von den Gruppen erfasst wurde.

Mit benannten Gruppen können Sie speichern, welche Gruppen im Gruppenobjekt erfasst werden, dessen Eigenschaftsnamen den Namen entsprechen, die Gruppen zugewiesen wurden.

 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"} 

Es ist zu beachten, dass benannte Gruppen gut mit der Methode .matchAll() .

→ Unterstützung


  • TC39: Stufe 4
  • Chrome: 64+
  • Knoten: 10+

Fortsetzung folgt…

Liebe Leser! Haben Sie eine der hier beschriebenen JavaScript-Innovationen verwendet?

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


All Articles