ES2015 führte viele neue Funktionen in die JavaScript-Sprache ein, darunter erhebliche Verbesserungen an der regulären Ausdruckssyntax mit den Flags „Unicode“ (/u
) und „Sticky“ (/y
). Die Entwicklung hat aber seitdem nicht aufgehört. In enger Zusammenarbeit mit anderen Mitgliedern von TC39 (der ECMAScript-Standardorganisation) hat das V8-Team mehrere neue Funktionen vorgeschlagen und gemeinsam entwickelt, um reguläre Ausdrücke noch leistungsfähiger zu machen.
Diese Funktionen werden derzeit für die Aufnahme in die JavaScript-Spezifikation vorgeschlagen. Auch wenn die Vorschläge noch nicht vollständig akzeptiert wurden, befinden sie sich bereits in Phase 3 des TC39-Prozesses. Wir haben diese Funktionen mit einem Flag versehen (siehe unten), damit wir den jeweiligen Verfassern von Vorschlägen rechtzeitig Feedback zu Design und Implementierung geben können, bevor die Spezifikation fertiggestellt ist.
In diesem Blogpost erhalten Sie einen ersten Einblick in diese spannende Zukunft. Wenn Sie die folgenden Beispiele nachvollziehen möchten, aktivieren Sie unter chrome://flags/#enable-javascript-harmony
die experimentellen JavaScript-Funktionen.
Benannte Aufnahmen
Reguläre Ausdrücke können sogenannte Erfassungen (oder Gruppen) enthalten, mit denen ein Teil des übereinstimmenden Texts erfasst werden kann. Bisher konnten Entwickler auf diese Erfassungen nur über ihren Index verweisen, der durch die Position der Erfassung im Muster bestimmt wird.
const pattern = /(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2017-07-10');
// result[0] === '2017-07-10'
// result[1] === '2017'
// result[2] === '07'
// result[3] === '10'
Reguläre Ausdrücke sind jedoch bekanntermaßen schwer zu lesen, zu schreiben und zu verwalten. Numerische Verweise können die Sache noch komplizierter machen. Bei längeren Mustern kann es beispielsweise schwierig sein, den Index einer bestimmten Erfassung zu ermitteln:
/(?:(.)(.(?<=[^(])(.)))/ // Index of the last capture?
Schlimmer noch: Änderungen an einem Muster können potenziell die Indizes aller vorhandenen Aufnahmen verschieben:
/(a)(b)(c)\3\2\1/ // A few simple numbered backreferences.
/(.)(a)(b)(c)\4\3\2/ // All need to be updated.
Mit der anstehenden Funktion „Benannte Erfassungen“ können Entwickler Erfassungen Namen zuweisen, um diese Probleme zu minimieren. Die Syntax ähnelt der von Perl, Java, .Net und Ruby:
const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2017-07-10');
// result.groups.year === '2017'
// result.groups.month === '07'
// result.groups.day === '10'
Auf benannte Erfassungen kann auch über benannte Rückverweise und über String.prototype.replace
verwiesen werden:
// Named backreferences.
/(?<LowerCaseX>x)y\k<LowerCaseX>/.test('xyx'); // true
// String replacement.
const pattern = /(?<fst>a)(?<snd>b)/;
'ab'.replace(pattern, '$<snd>$<fst>'); // 'ba'
'ab'.replace(pattern, (m, p1, p2, o, s, {fst, snd}) => fst + snd); // 'ba'
Ausführliche Informationen zu dieser neuen Funktion finden Sie im Spezifikationsvorschlag.
Flag „dotAll“
Standardmäßig entspricht das Atom .
in regulären Ausdrücken jedem Zeichen mit Ausnahme von Zeilenenden:
/foo.bar/u.test('foo\nbar'); // false
In einem Vorschlag wird der dotAll-Modus eingeführt, der über das Flag /s
aktiviert wird. Im Modus „dotAll“ stimmt .
auch mit Zeilenenden überein.
/foo.bar/su.test('foo\nbar'); // true
Ausführliche Informationen zu dieser neuen Funktion finden Sie im Spezifikationsvorschlag.
Unicode-Escape-Zeichen
Durch die Unicode-Unterstützung, die in ES2015 eingeführt wurde, gibt es plötzlich viele weitere Zeichen, die als Ziffern betrachtet werden können, z. B. die kreisförmige Ziffer 1: ① oder als Wortzeichen, z. B. das chinesische Schriftzeichen für Schnee: 雪.
Keine dieser Optionen kann mit \d
oder \w
abgeglichen werden. Wenn die Bedeutung dieser Kurzzeichen geändert würde, würden vorhandene reguläre Ausdrucksmuster aufgelöst.
Stattdessen werden neue Escape-Sequenzen für Properties eingeführt.
Sie sind nur für Unicode-kompatible reguläre Ausdrücke verfügbar, die durch das Flag /u
gekennzeichnet sind.
/\p{Number}/u.test('①'); // true
/\p{Alphabetic}/u.test('雪'); // true
Das Gegenteil kann mit \P
abgeglichen werden.
/\P{Number}/u.test('①'); // false
/\P{Alphabetic}/u.test('雪'); // false
Das Unicode-Konsortium definiert viele weitere Eigenschaften, z. B. für mathematische Symbole oder japanische Hiragana-Zeichen:
/^\p{Math}+$/u.test('∛∞∉'); // true
/^\p{Script_Extensions=Hiragana}+$/u.test('ひらがな'); // true
Eine vollständige Liste der unterstützten Unicode-Eigenschaftsklassen finden Sie im aktuellen Spezifikationsvorschlag. Weitere Beispiele finden Sie in diesem Artikel.
Lookbehind-Behauptungen
Vorausschauende Bestätigungen waren von Anfang an Teil der regulären Ausdruckssyntax von JavaScript. Ihr Pendant, die Rückwärts-Bedingungen, werden jetzt endlich eingeführt. Einige von Ihnen erinnern sich vielleicht, dass diese Funktion schon seit einiger Zeit in V8 verfügbar ist. Wir verwenden sogar Lookbehind-Behauptungen, um das in ES2015 angegebene Unicode-Flag zu implementieren.
Der Name beschreibt die Bedeutung bereits ziemlich gut. So lässt sich ein Muster so einschränken, dass es nur dann übereinstimmt, wenn es vom Muster in der Rückwärtssuchgruppe vorangestellt wird. Es gibt sowohl übereinstimmende als auch nicht übereinstimmende Varianten:
/(?<=\$)\d+/.exec('$1 is worth about ¥123'); // ['1']
/(?<!\$)\d+/.exec('$1 is worth about ¥123'); // ['123']
Weitere Informationen finden Sie in unserem vorherigen Blogpost zu Lookbehind-Behauptungen und in den Beispielen in den zugehörigen V8-Testfällen.
Danksagungen
Dieser Blogpost wäre nicht vollständig, ohne einige der Personen zu erwähnen, die hart daran gearbeitet haben, dass dies möglich wurde: insbesondere die Sprachexperten Mathias Bynens, Dan Ehrenberg, Claude Pache, Brian Terlson, Thomas Wood, Gorkem Yakin und der Irregexp-Guru Erik Corry. Aber auch alle anderen, die zur Sprachspezifikation und zur Implementierung dieser Funktionen in V8 beigetragen haben.
Wir hoffen, dass Sie diese neuen Funktionen für reguläre Ausdrücke genauso spannend finden wie wir.