JavaScript: Die Neuerungen in ES2018

Reguläre Ausdrücke

Bezüglich regulärer Ausdrücke wird es in ES2018 gleich mehrere Neuerungen geben. Über sogenannte Lookahead Assertions ist es bereits jetzt möglich, nach Mustern zu suchen, denen ein anderes Muster folgt oder eben genau nicht folgt.

Demnach unterscheidet man zwischen "Positive Lookaheads" und "Negative Lookaheads":

// Positive lookahead:
// Suche nach Mustern, denen das Muster "Mustermann" folgt:
const pattern = /\w+(?= Mustermann)/u;
const result = pattern.exec('Max Mustermann');
console.log(result[0]);
// result[0] === 'Max'

// Negative lookahead:
// Suche nach Mustern, denen das Muster
// "Mustermann" nicht folgt:
const pattern = /\w+(?! Mustermann)/u;
const result = pattern.exec('Max Defaultname');
console.log(result[0]);
// result[0] === 'Max'

Analog dazu wird es in ES2018 sogenannte Lookbehind Assertions geben, mit denen sich nach Mustern suchen lässt, denen ein anderes Muster vorausgeht ("Positive Lookbehind Assertions") oder genau nicht vorausgeht ("Negative Lookbehind Assertions").

// Positive lookbehind:
// Suche nach Mustern, denen das Muster "Max " vorausgeht:
const pattern = /(?<=Max )\w+/u;
const result = pattern.exec('Max Mustermann');
console.log(result[0]);
// result[0] === 'Mustermann'

// Negative lookbehind:
// Suche nach Mustern, denen das Muster
// "Max " nicht vorausgeht:
const pattern = /(?<!Max )\w+/u;
const result = pattern.exec('Mustermann');
console.log(result[0]);
// result[0] === 'Mustermann'

Über sogenannte Named capturing groups wiederum lassen sich innerhalb regulärer Ausdrücke Gruppen mit (eindeutigen) Namen versehen. Bislang sieht die Definition von Gruppen wie im folgenden Codeausschnitt aus, und die gefundenen Treffer lassen sich nach Anwendung des regulären Ausdrucks nur über den Index abrufen:

const pattern = /(\d{4})-(\d{2})-(\d{2})/u;
const result = pattern.exec('2018-01-31');
console.log(result[0]); // '2018-01-31'
console.log(result[1]); // '2018'
console.log(result[2]); // '01'
console.log(result[3]); // '31'

Benannte Gruppen erleichtern den Zugriff auf die Treffer und sorgen für verständlichere reguläre Ausdrücke. Die Definition verwendet einschließende spitze Klammern – im folgenden Ausschnitt <year>, <month> und <day>:

const pattern = 
/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const result = pattern.exec('2018-01-31');

// Zugriff entweder über Namen:
console.log(result.groups.year); // '2018'
console.log(result.groups.month); // '01'
console.log(result.groups.day); // '31'

// Oder wie bisher auch über den Index:
console.log(result[0]); // '2018-01-31'
console.log(result[1]]; // '2018'
console.log(result[2]]; // '01'
console.log(result[3]]; // '31'

In Kombination mit Object Destructuring sind reguläre Ausdrücke ein wahrer Genuss, weil sich die benannten Gruppen darüber direkt gleichnamigen Variablen zuweisen lassen:

const pattern = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/u;
const {
groups: {
year,
month,
day
}
} = pattern.exec('2018-01-31');
console.log(year); // '2018'
console.log(month); // '01'
console.log(day); // '31'

Darüber hinaus gibt es einige weitere Features bezüglich regulärer Ausdrücke. Über Unicode Property Escapes ist es möglich, innerhalb von regulären Ausdrücken auf Eigenschaften von Unicode-Symbolen und deren Werte zuzugreifen. Das neu eingeführte s-Flag (dotAll-Flag) sorgt zudem dafür, dass das Punktzeichen innerhalb von regulären Ausdrücken auch Zeilenenden als Treffer berücksichtigt:

// Ohne s-Flag:
console.log(/some.thing/.test('some\nthing')); // false

// Mit s-Flag
console.log(/some.thing/s.test('some\nthing')); // true

Erweiterung für finally

Neu ist zudem die Methode finally(), die für Promise-Objekte zur Verfügung stehen wird. Über das Schlüsselwort finally lässt sich bereits im aktuellen JavaScript-Standard bei einem try/catch-Statement ein Codeblock definieren, der in jedem Fall ausgeführt werden soll – unabhängig davon, ob ein Fehler auftritt oder nicht. Für synchrone Abläufe sieht das in JavaScript folgendermaßen aus:

const doSomething = () => {};
try {
doSomething();
console.log('Did something');
} catch(error) {
console.error('An error occured');
} finally {
// wird in jedem Fall aufgerufen
console.log('Executed in any case')
}

Seit ES2016 lässt sich das Vorgehen dank async/await auch bei asynchronen Abläufen verwenden:

const doSomethingAsync = () 
=> new Promise((resolve, reject) => {
setTimeout(resolve, 5000);
});

(async () => {
try {
await doSomethingAsync();
console.log('Did something');
} catch(error) {
console.error('An error occured');
} finally {
// wird in jedem Fall aufgerufen
console.log('Executed in any case')
}
})();

Die (parameterlose) finally()-Methode komplettiert das Ganze, indem sich darüber entsprechende Codeblöcke auch bei Verwendung der Promise-API definieren lassen:

const doSomethingAsync = () => new Promise((resolve, reject)
=> {
setTimeout(resolve, 5000);
});

doSomethingAsync()
.then((result) => console.log('Did something'))
.catch((error) => console.error('An error occured'))
// wird in jedem Fall aufgerufen
.finally(() => console.log('Executed in any case'));

Template Literal Revision

Die in ES2015 eingeführten Template Strings und die damit einhergehenden Tag Functions ermöglichen einen deutlich flexibleren Umgang mit Zeichenketten. ES2018 lockert einige Restriktionen, die für solche Zeichenketten noch existieren. Bisher gelten nach dem Backslash einige Zeichensequenzen nämlich als ungültig, beispielsweise wenn nach einem \u kein gültiger Unicode oder nach einem \x kein gültiger Hexcode steht. Völlige Freiheit bei der Definition von Template Strings hat man also bisher nicht – beispielsweise bei der Verarbeitung von LaTeX.

ES2018 hebt einige der syntaktischen Restriktionen im Rahmen der "Template Literal Revision" auf. Die Eigenschaft raw innerhalb einer Tag Function enthält nun wirklich die unverarbeiteten Zeichenketten, ohne dass eine Überprüfung stattfindet. Ist in einer solche Zeichenkette allerdings ein syntaktischer Fehler enthalten, erhält die entsprechend verarbeitete Version den Wert undefined.

function latex(strings, ...values) {
// Verarbeitete Zeichenketten oder jeweils undefined:
console.log(strings);

// Unverarbeitete Zeichenkette:
console.log(strings.raw);
}

const latexDocument = latex`
// Funktioniert:
\newcommand{\fun}{\textbf{Beispiel}}
// Bisher Fehler, da ungültiges Token:
\newcommand{\unicode}{\textbf{Beispiel}}
// Bisher Fehler, da ungültiges Token:
\newcommand{\xerxes}{\textbf{Beispiel}}
`;