Neue ES2018-Funktionen, die jeder JavaScript-Entwickler kennen sollte

Avatar of Faraz Kelhini
Faraz Kelhini am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Die neunte Ausgabe des ECMAScript-Standards, offiziell bekannt als ECMAScript 2018 (oder kurz ES2018), wurde im Juni 2018 veröffentlicht. Seit ES2016 werden neue Versionen der ECMAScript-Spezifikationen jährlich statt alle paar Jahre veröffentlicht und enthalten weniger Funktionen als frühere Hauptversionen. Die neueste Ausgabe des Standards setzt den jährlichen Veröffentlichungszyklus fort und fügt vier neue RegExp-Funktionen, Rest-/Spread-Eigenschaften, asynchrone Iteration und Promise.prototype.finally hinzu. Darüber hinaus hebt ES2018 die Syntaxbeschränkung für Escape-Sequenzen in getaggten Templates auf.

Diese neuen Änderungen werden in den folgenden Unterabschnitten erläutert.

Rest-/Spread-Eigenschaften

Eine der interessantesten Funktionen, die ES2015 hinzugefügt wurden, war der Spread-Operator. Dieser Operator vereinfacht das Kopieren und Zusammenführen von Arrays erheblich. Anstatt die Methode concat() oder slice() aufzurufen, konnten Sie den ...-Operator verwenden

const arr1 = [10, 20, 30];

// make a copy of arr1
const copy = [...arr1];

console.log(copy);    // → [10, 20, 30]

const arr2 = [40, 50];

// merge arr2 with arr1
const merge = [...arr1, ...arr2];

console.log(merge);    // → [10, 20, 30, 40, 50]

Der Spread-Operator ist auch nützlich in Situationen, in denen ein Array als separate Argumente an eine Funktion übergeben werden muss. Zum Beispiel

const arr = [10, 20, 30]

// equivalent to
// console.log(Math.max(10, 20, 30));
console.log(Math.max(...arr));    // → 30

ES2018 erweitert diese Syntax weiter, indem es Spread-Eigenschaften zu Objektliteralen hinzufügt. Mit den Spread-Eigenschaften können Sie eigene, aufzählbare Eigenschaften eines Objekts auf ein neues Objekt kopieren. Betrachten Sie das folgende Beispiel

const obj1 = {
  a: 10,
  b: 20
};

const obj2 = {
  ...obj1,
  c: 30
};

console.log(obj2);    // → {a: 10, b: 20, c: 30}

In diesem Code wird der ...-Operator verwendet, um die Eigenschaften von obj1 abzurufen und sie obj2 zuzuweisen. Vor ES2018 hätte der Versuch, dies zu tun, einen Fehler verursacht. Wenn mehrere Eigenschaften denselben Namen haben, wird die zuletzt genannte Eigenschaft verwendet

const obj1 = {
  a: 10,
  b: 20
};

const obj2 = {
  ...obj1,
  a: 30
};

console.log(obj2);    // → {a: 30, b: 20}

Spread-Eigenschaften bieten auch eine neue Möglichkeit, zwei oder mehr Objekte zusammenzuführen, was als Alternative zur Methode Object.assign() verwendet werden kann

const obj1 = {a: 10};
const obj2 = {b: 20};
const obj3 = {c: 30};

// ES2018
console.log({...obj1, ...obj2, ...obj3});    // → {a: 10, b: 20, c: 30}

// ES2015
console.log(Object.assign({}, obj1, obj2, obj3));    // → {a: 10, b: 20, c: 30}

Beachten Sie jedoch, dass Spread-Eigenschaften nicht immer dasselbe Ergebnis wie Object.assign() liefern. Betrachten Sie den folgenden Code

Object.defineProperty(Object.prototype, 'a', {
  set(value) {
    console.log('set called!');
  }
});

const obj = {a: 10};

console.log({...obj});    
// → {a: 10}

console.log(Object.assign({}, obj));    
// → set called!
// → {}

In diesem Code führt die Methode Object.assign() die geerbte Setter-Eigenschaft aus. Umgekehrt ignorieren die Spread-Eigenschaften einfach den Setter.

Es ist wichtig zu bedenken, dass Spread-Eigenschaften nur aufzählbare Eigenschaften kopieren. Im folgenden Beispiel wird die Eigenschaft type nicht im kopierten Objekt angezeigt, da ihr Attribut enumerable auf false gesetzt ist

const car = {
  color: 'blue'
};

Object.defineProperty(car, 'type', {
  value: 'coupe',
  enumerable: false
});

console.log({...car});    // → {color: "blue"}

Geerbte Eigenschaften werden ignoriert, auch wenn sie aufzählbar sind

const car = {
  color: 'blue'
};

const car2 = Object.create(car, {
  type: {
    value: 'coupe',
    enumerable: true,
  }
});

console.log(car2.color);                      // → blue
console.log(car2.hasOwnProperty('color'));    // → false

console.log(car2.type);                       // → coupe
console.log(car2.hasOwnProperty('type'));     // → true

console.log({...car2});                       // → {type: "coupe"}

In diesem Code erbt car2 die Eigenschaft color von car. Da Spread-Eigenschaften nur die eigenen Eigenschaften eines Objekts kopieren, ist color nicht im Rückgabewert enthalten.

Beachten Sie, dass Spread-Eigenschaften nur eine flache Kopie eines Objekts erstellen können. Wenn eine Eigenschaft ein Objekt enthält, wird nur die Referenz auf das Objekt kopiert

const obj = {x: {y: 10}};
const copy1 = {...obj};    
const copy2 = {...obj}; 

console.log(copy1.x === copy2.x);    // → true

Die Eigenschaft x in copy1 verweist auf dasselbe Objekt im Speicher wie x in copy2, sodass der strikte Gleichheitsoperator true zurückgibt.

Eine weitere nützliche Funktion, die ES2015 hinzugefügt wurde, waren Rest-Parameter, die es JavaScript-Programmierern ermöglichten, ... zu verwenden, um Werte als Array darzustellen. Zum Beispiel

const arr = [10, 20, 30];
const [x, ...rest] = arr;

console.log(x);       // → 10
console.log(rest);    // → [20, 30]

Hier wird das erste Element in arr x zugewiesen, und die verbleibenden Elemente werden der Variablen rest zugewiesen. Dieses Muster, genannt Array-Destrukturierung, wurde so beliebt, dass das Ecma Technical Committee beschloss, ähnliche Funktionalität für Objekte einzuführen

const obj = {
  a: 10,
  b: 20,
  c: 30
};

const {a, ...rest} = obj;

console.log(a);       // → 10
console.log(rest);    // → {b: 20, c: 30}

Dieser Code verwendet Rest-Eigenschaften in einer Destrukturierungszuweisung, um die verbleibenden eigenen aufzählbaren Eigenschaften in ein neues Objekt zu kopieren. Beachten Sie, dass Rest-Eigenschaften immer am Ende des Objekts stehen müssen, andernfalls wird ein Fehler ausgelöst

const obj = {
  a: 10,
  b: 20,
  c: 30
};

const {...rest, a} = obj;    // → SyntaxError: Rest element must be last element

Beachten Sie auch, dass die Verwendung mehrerer Rest-Syntaxen in einem Objekt zu einem Fehler führt, es sei denn, sie sind verschachtelt

const obj = {
  a: 10,
  b: {
    x: 20,
    y: 30,
    z: 40
  }
};

const {b: {x, ...rest1}, ...rest2} = obj;    // no error

const {...rest, ...rest2} = obj;    // → SyntaxError: Rest element must be last element

Unterstützung für Rest-/Spread-Eigenschaften

Chrome Firefox Safari Edge
60 55 11.1 Nein
Chrome Android Firefox Android iOS Safari Edge Mobile Samsung Internet Android Webview
60 55 11.3 Nein 8.2 60

Node.js

  • 8.0.0 (erfordert das Laufzeit-Flag --harmony)
  • 8.3.0 (vollständige Unterstützung)

Asynchrone Iteration

Die Iteration über eine Datensammlung ist ein wichtiger Teil der Programmierung. Vor ES2015 bot JavaScript Anweisungen wie for, for...in und while sowie Methoden wie map(), filter() und forEach() für diesen Zweck. Um Programmierern die Verarbeitung der Elemente in einer Sammlung einzeln zu ermöglichen, führte ES2015 die Iterator-Schnittstelle ein.

Ein Objekt ist iterierbar, wenn es eine Eigenschaft Symbol.iterator hat. In ES2015 verfügen Strings und Sammlungs-Objekte wie Set, Map und Array über eine Eigenschaft Symbol.iterator und sind somit iterierbar. Der folgende Code gibt ein Beispiel dafür, wie auf die Elemente eines Iterierbaren einzeln zugegriffen werden kann

const arr = [10, 20, 30];
const iterator = arr[Symbol.iterator]();
  
console.log(iterator.next());    // → {value: 10, done: false}
console.log(iterator.next());    // → {value: 20, done: false}
console.log(iterator.next());    // → {value: 30, done: false}
console.log(iterator.next());    // → {value: undefined, done: true}

Symbol.iterator ist ein bekanntes Symbol, das eine Funktion angibt, die einen Iterator zurückgibt. Die primäre Methode zur Interaktion mit einem Iterator ist die Methode next(). Diese Methode gibt ein Objekt mit zwei Eigenschaften zurück: value und done. Die Eigenschaft value enthält den Wert des nächsten Elements in der Sammlung. Die Eigenschaft done enthält entweder true oder false, um anzuzeigen, ob das Ende der Sammlung erreicht wurde.

Standardmäßig ist ein einfaches Objekt nicht iterierbar, kann aber iterierbar werden, wenn Sie eine Eigenschaft Symbol.iterator darauf definieren, wie in diesem Beispiel

const collection = {
  a: 10,
  b: 20,
  c: 30,
  [Symbol.iterator]() {
    const values = Object.keys(this);
    let i = 0;
    return {
      next: () => {
        return {
          value: this[values[i++]],
          done: i > values.length
        }
      }
    };
  }
};

const iterator = collection[Symbol.iterator]();
  
console.log(iterator.next());    // → {value: 10, done: false}
console.log(iterator.next());    // → {value: 20, done: false}
console.log(iterator.next());    // → {value: 30, done: false}
console.log(iterator.next());    // → {value: undefined, done: true}

Dieses Objekt ist iterierbar, da es eine Eigenschaft Symbol.iterator definiert. Der Iterator verwendet die Methode Object.keys(), um ein Array der Eigenschaftsnamen des Objekts zu erhalten, und weist es der Konstante values zu. Er definiert auch eine Zählervariable und gibt ihr einen Anfangswert von 0. Wenn der Iterator ausgeführt wird, gibt er ein Objekt zurück, das eine Methode next() enthält. Jedes Mal, wenn die Methode next() aufgerufen wird, gibt sie ein {value, done}-Paar zurück, wobei value das nächste Element in der Sammlung enthält und done einen booleschen Wert angibt, ob der Iterator das Ende der Sammlung erreicht hat.

Obwohl dieser Code perfekt funktioniert, ist er unnötig kompliziert. Glücklicherweise kann die Verwendung einer Generatorfunktion den Prozess erheblich vereinfachen

const collection = {
  a: 10,
  b: 20,
  c: 30,
  [Symbol.iterator]: function * () {
    for (let key in this) {
      yield this[key];
    }
  }
};

const iterator = collection[Symbol.iterator]();
  
console.log(iterator.next());    // → {value: 10, done: false}
console.log(iterator.next());    // → {value: 20, done: false}
console.log(iterator.next());    // → {value: 30, done: false}
console.log(iterator.next());    // → {value: undefined, done: true}

Innerhalb dieses Generators wird eine for...in-Schleife verwendet, um die Sammlung zu durchlaufen und den Wert jeder Eigenschaft zu "yielden". Das Ergebnis ist genau dasselbe wie im vorherigen Beispiel, aber es ist wesentlich kürzer.

Ein Nachteil von Iteratoren ist, dass sie nicht für die Darstellung asynchroner Datenquellen geeignet sind. Die Lösung von ES2018 dafür sind asynchrone Iteratoren und asynchrone Iterierbare. Ein asynchroner Iterator unterscheidet sich von einem herkömmlichen Iterator dadurch, dass er anstelle eines einfachen Objekts in der Form {value, done} ein Promise zurückgibt, das zu {value, done} erfüllt wird. Ein asynchrones Iterierbares definiert eine Methode Symbol.asyncIterator (anstelle von Symbol.iterator), die einen asynchronen Iterator zurückgibt.

Ein Beispiel sollte dies verdeutlichen

const collection = {
  a: 10,
  b: 20,
  c: 30,
  [Symbol.asyncIterator]() {
    const values = Object.keys(this);
    let i = 0;
    return {
      next: () => {
        return Promise.resolve({
          value: this[values[i++]], 
          done: i > values.length
        });
      }
    };
  }
};

const iterator = collection[Symbol.asyncIterator]();
  
console.log(iterator.next().then(result => {
  console.log(result);    // → {value: 10, done: false}
}));

console.log(iterator.next().then(result => {
  console.log(result);    // → {value: 20, done: false} 
}));

console.log(iterator.next().then(result => {
  console.log(result);    // → {value: 30, done: false} 
}));

console.log(iterator.next().then(result => {
  console.log(result);    // → {value: undefined, done: true} 
}));

Beachten Sie, dass es nicht möglich ist, einen Iterator von Promises zu verwenden, um dasselbe Ergebnis zu erzielen. Obwohl ein normaler, synchroner Iterator Werte asynchron ermitteln kann, muss er den Status "done" immer noch synchron ermitteln.

Auch hier können Sie den Prozess vereinfachen, indem Sie eine Generatorfunktion verwenden, wie unten gezeigt

const collection = {
  a: 10,
  b: 20,
  c: 30,
  [Symbol.asyncIterator]: async function * () {
    for (let key in this) {
      yield this[key];
    }
  }
};

const iterator = collection[Symbol.asyncIterator]();
  
console.log(iterator.next().then(result => {
  console.log(result);    // → {value: 10, done: false}
}));

console.log(iterator.next().then(result => {
  console.log(result);    // → {value: 20, done: false} 
}));

console.log(iterator.next().then(result => {
  console.log(result);    // → {value: 30, done: false} 
}));

console.log(iterator.next().then(result => {
  console.log(result);    // → {value: undefined, done: true} 
}));

Normalerweise gibt eine Generatorfunktion ein Generatorobjekt mit einer Methode next() zurück. Wenn next() aufgerufen wird, gibt sie ein {value, done}-Paar zurück, dessen Eigenschaft value den ge-"yieldete" Wert enthält. Ein asynchroner Generator tut dasselbe, nur dass er ein Promise zurückgibt, das zu {value, done} erfüllt wird.

Eine einfache Möglichkeit, über ein iterierbares Objekt zu iterieren, ist die Verwendung der for...of-Anweisung, aber for...of funktioniert nicht mit asynchronen Iterierbaren, da value und done nicht synchron ermittelt werden. Aus diesem Grund stellt ES2018 die for...await...of-Anweisung bereit. Betrachten wir ein Beispiel

const collection = {
  a: 10,
  b: 20,
  c: 30,
  [Symbol.asyncIterator]: async function * () {
    for (let key in this) {
      yield this[key];
    }
  }
};

(async function () {
  for await (const x of collection) {
    console.log(x);
  }
})();

// logs:
// → 10
// → 20
// → 30

In diesem Code ruft die for...await...of-Anweisung implizit die Methode Symbol.asyncIterator des Sammlungsobjekts auf, um einen asynchronen Iterator zu erhalten. Jedes Mal während der Schleife wird die Methode next() des Iterators aufgerufen, die ein Promise zurückgibt. Sobald das Promise aufgelöst ist, wird die Eigenschaft value des resultierenden Objekts der Variablen x zugewiesen. Die Schleife wird fortgesetzt, bis die Eigenschaft done des zurückgegebenen Objekts den Wert true hat.

Beachten Sie, dass die for...await...of-Anweisung nur innerhalb von asynchronen Generatoren und asynchronen Funktionen gültig ist. Ein Verstoß gegen diese Regel führt zu einem SyntaxError.

Die Methode next() kann ein Promise zurückgeben, das fehlschlägt. Um ein fehlschlagendes Promise ordnungsgemäß zu behandeln, können Sie die for...await...of-Anweisung in eine try...catch-Anweisung einschließen, wie hier gezeigt

const collection = {
  [Symbol.asyncIterator]() {
    return {
      next: () => {
        return Promise.reject(new Error('Something went wrong.'))
      }
    };
  }
};

(async function() {
  try {
    for await (const value of collection) {}
  } catch (error) {
    console.log('Caught: ' + error.message);
  }
})();

// logs:
// → Caught: Something went wrong.

Unterstützung für asynchrone Iteratoren

Chrome Firefox Safari Edge
63 57 12 Nein
Chrome Android Firefox Android iOS Safari Edge Mobile Samsung Internet Android Webview
63 57 12 Nein 8.2 63

Node.js

  • 8.10.0 (erfordert das Flag –harmony_async_iteration)
  • 10.0.0 (vollständige Unterstützung)

Promise.prototype.finally

Eine weitere spannende Ergänzung zu ES2018 ist die Methode finally(). Mehrere JavaScript-Bibliotheken hatten zuvor eine ähnliche Methode implementiert, die sich in vielen Situationen als nützlich erwiesen hat. Dies ermutigte das Ecma Technical Committee, finally() zur Spezifikation hinzuzufügen. Mit dieser Methode können Programmierer einen Codeblock ausführen, unabhängig vom Schicksal des Promises. Betrachten wir ein einfaches Beispiel

fetch('https://www.google.com')
  .then((response) => {
    console.log(response.status);
  })
  .catch((error) => { 
    console.log(error);
  })
  .finally(() => { 
    document.querySelector('#spinner').style.display = 'none';
  });

Die Methode finally() ist nützlich, wenn Sie nach Abschluss einer Operation eine Bereinigung durchführen müssen, unabhängig davon, ob sie erfolgreich war oder nicht. In diesem Code versteckt die Methode finally() einfach den Lade-Spinner, nachdem die Daten abgerufen und verarbeitet wurden. Anstatt die finale Logik in den Methoden then() und catch() zu duplizieren, registriert der Code eine Funktion, die ausgeführt wird, sobald das Promise entweder erfüllt oder abgelehnt wird.

Sie könnten dasselbe Ergebnis erzielen, indem Sie promise.then(func, func) anstelle von promise.finally(func) verwenden, aber Sie müssten denselben Code sowohl im Erfüllungs- als auch im Ablehnungs-Handler wiederholen oder eine Variable dafür deklarieren

fetch('https://www.google.com')
  .then((response) => {
    console.log(response.status);
  })
  .catch((error) => { 
    console.log(error);
  })
  .then(final, final);

function final() {
  document.querySelector('#spinner').style.display = 'none';
}

Wie bei then() und catch() gibt die Methode finally() immer ein Promise zurück, sodass Sie weitere Methoden verketten können. Normalerweise möchten Sie finally() als letzte Kette verwenden, aber in bestimmten Situationen, wie z. B. bei HTTP-Anfragen, ist es eine gute Praxis, eine weitere catch()-Anweisung anzuhängen, um Fehler zu behandeln, die in finally() auftreten können.

Unterstützung für Promise.prototype.finally

Chrome Firefox Safari Edge
63 58 11.1 18
Chrome Android Firefox Android iOS Safari Edge Mobile Samsung Internet Android Webview
63 58 11.1 Nein 8.2 63

Node.js

10.0.0 (vollständige Unterstützung)

Neue RegExp-Funktionen

ES2018 fügt dem RegExp-Objekt vier neue Funktionen hinzu, die die Fähigkeiten der JavaScript-Stringverarbeitung weiter verbessern. Diese Funktionen sind:

  • s (dotAll) Flag
  • Benannte Erfassungsgruppen
  • Lookbehind-Assertions
  • Unicode-Eigenschafts-Escapes

s (dotAll) Flag

Der Punkt (.) ist ein Sonderzeichen in einem regulären Ausdrucksmuster, das jedes Zeichen außer Zeilenumbruchzeichen wie Zeilenvorschub (\n) oder Wagenrücklauf (\r) abgleicht. Ein Workaround, um alle Zeichen, einschließlich Zeilenumbrüche, abzugleichen, ist die Verwendung einer Zeichenklasse mit zwei gegenüberliegenden Shorthands wie [\d\D]. Diese Zeichenklasse weist die reguläre Ausdrucks-Engine an, ein Zeichen zu finden, das entweder eine Ziffer (\d) oder keine Ziffer (\D) ist. Als Ergebnis gleicht sie jedes Zeichen ab

console.log(/one[\d\D]two/.test('one\ntwo'));    // → true

ES2018 führt einen Modus ein, in dem der Punkt verwendet werden kann, um dasselbe Ergebnis zu erzielen. Dieser Modus kann pro regulärem Ausdruck aktiviert werden, indem das s-Flag verwendet wird

console.log(/one.two/.test('one\ntwo'));     // → false
console.log(/one.two/s.test('one\ntwo'));    // → true

Der Vorteil der Verwendung eines Flags, um sich für das neue Verhalten zu entscheiden, ist die Abwärtskompatibilität. Bestehende Muster von regulären Ausdrücken, die das Punktzeichen verwenden, werden also nicht beeinträchtigt.

Benannte Erfassungsgruppen

In einigen Mustern regulärer Ausdrücke kann die Verwendung einer Zahl zur Referenzierung einer Erfassungsgruppe verwirrend sein. Nehmen Sie zum Beispiel den regulären Ausdruck /(\d{4})-(\d{2})-(\d{2})/, der ein Datum abgleicht. Da die Datumsnotation im amerikanischen Englisch anders ist als im britischen Englisch, ist es schwer zu wissen, welche Gruppe sich auf den Tag und welche auf den Monat bezieht

const re = /(\d{4})-(\d{2})-(\d{2})/;
const match= re.exec('2019-01-10');

console.log(match[0]);    // → 2019-01-10
console.log(match[1]);    // → 2019
console.log(match[2]);    // → 01
console.log(match[3]);    // → 10

ES2018 führt benannte Erfassungsgruppen ein, die die Syntax (?<name>...)</name> verwenden. So kann das Muster zum Abgleichen eines Datums auf eine weniger mehrdeutige Weise geschrieben werden

const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/;
const match = re.exec('2019-01-10');

console.log(match.groups);          // → {year: "2019", month: "01", day: "10"}
console.log(match.groups.year);     // → 2019
console.log(match.groups.month);    // → 01
console.log(match.groups.day);      // → 10</day></month></year>

Sie können eine benannte Erfassungsgruppe später im Muster abrufen, indem Sie die Syntax \k<name></name> verwenden. Um beispielsweise aufeinanderfolgende doppelte Wörter in einem Satz zu finden, können Sie /\b(?<dup>\w+)\s+\k<dup>\b/</dup></dup> verwenden

const re = /\b(?<dup>\w+)\s+\k<dup>\b/;
const match = re.exec('Get that that cat off the table!');        

console.log(match.index);    // → 4
console.log(match[0]);       // → that that</dup></dup>

Um eine benannte Erfassungsgruppe in den Ersetzungsstring der Methode replace() einzufügen, müssen Sie das Konstrukt $<name></name> verwenden. Zum Beispiel

const str = 'red &amp; blue';

console.log(str.replace(/(red) &amp; (blue)/, '$2 &amp; $1'));    
// → blue &amp; red

console.log(str.replace(/(?<red>red) &amp; (?<blue>blue)/, '$<blue> &amp; $<red>'));    
// → blue &amp; red</red></blue></blue></red>

Lookbehind-Assertions

ES2018 bringt Lookbehind-Assertions nach JavaScript, die in anderen Regex-Implementierungen seit Jahren verfügbar sind. Zuvor unterstützte JavaScript nur Lookahead-Assertions. Eine Lookbehind-Assertion wird durch (?<=...) bezeichnet und ermöglicht es Ihnen, ein Muster basierend auf dem Substring abzugleichen, der dem Muster vorausgeht. Wenn Sie beispielsweise den Preis eines Produkts in Dollar, Pfund oder Euro abgleichen möchten, ohne das Währungssymbol zu erfassen, können Sie /(?<=\$|£|€)\d+(\.\d*)?/ verwenden

const re = /(?&lt;=\$|£|€)\d+(\.\d*)?/;

console.log(re.exec('199'));     
// → null

console.log(re.exec('$199'));    
// → ["199", undefined, index: 1, input: "$199", groups: undefined]

console.log(re.exec('€50'));     
// → ["50", undefined, index: 1, input: "€50", groups: undefined]

Es gibt auch eine negative Version von Lookbehind, die durch (? Es erlaubt eine negative Lookbehind, ein Muster nur dann abzugleichen, wenn es nicht vom Muster innerhalb des Lookbehind vorangestellt ist. Zum Beispiel gleicht das Muster <code>/(? das Wort available ab, wenn es keinen "un"-Präfix hat:

<code>
<pre rel="JavaScript"><code class="language-javascript">const re = /(?

<h4>Unicode-Eigenschafts-Escapes</h4>
ES2018 bietet eine neue Art von Escape-Sequenz, bekannt als Unicode-Eigenschafts-Escape, die vollständige Unicode-Unterstützung für reguläre Ausdrücke bietet. Angenommen, Sie möchten das Unicode-Zeichen ㉛ in einem String abgleichen. Obwohl ㉛ als Zahl gilt, können Sie es nicht mit der Shorthand-Zeichenklasse \d abgleichen, da diese nur ASCII [0-9]-Zeichen unterstützt. Unicode-Eigenschafts-Escapes können dagegen verwendet werden, um jede Dezimalzahl in Unicode abzugleichen

const str = '㉛';

console.log(/\d/u.test(str));    // → false
console.log(/\p{Number}/u.test(str));     // → true

Ebenso können Sie \p{Alphabetic} verwenden, um jedes Unicode-alphabtische Zeichen abzugleichen. (Im Originaltext stand hier „word“, was von einer überarbeiteten Version mit „alphabetic“ ersetzt wurde.)

const str = 'ض';

console.log(/\p{Alphabetic}/u.test(str));     // → true

// the \w shorthand cannot match ض
  console.log(/\w/u.test(str));    // → false

Es gibt auch eine negierte Version von \p{...}, die durch \P{...} bezeichnet wird

console.log(/\P{Number}/u.test('㉛'));    // → false
console.log(/\P{Number}/u.test('ض'));    // → true

console.log(/\P{Alphabetic}/u.test('㉛'));    // → true
console.log(/\P{Alphabetic}/u.test('ض'));    // → false

Zusätzlich zu Alphabetic und Number gibt es mehrere weitere Eigenschaften, die in Unicode-Eigenschafts-Escapes verwendet werden können. Eine Liste der unterstützten Unicode-Eigenschaften finden Sie in der aktuellen Spezifikationsvorlage.

Unterstützung für neue RegExp-Funktionen

Chrome Firefox Safari Edge
s (dotAll) Flag 62 Nein 11.1 Nein
Benannte Erfassungsgruppen 64 Nein 11.1 Nein
Lookbehind-Assertions 62 Nein Nein Nein
Unicode-Eigenschafts-Escapes 64 Nein 11.1 Nein
Chrome (Android) Firefox (Android) iOS Safari Edge Mobile Samsung Internet Android Webview
s (dotAll) Flag 62 Nein 11.3 Nein 8.2 62
Benannte Erfassungsgruppen 64 Nein 11.3 Nein Nein 64
Lookbehind-Assertions 62 Nein Nein Nein 8.2 62
Unicode-Eigenschafts-Escapes 64 Nein 11.3 Nein Nein 64

Node.js

  • 8.3.0 (erfordert das –harmony-Laufzeit-Flag)
  • 8.10.0 (Unterstützung für s (dotAll)-Flag und Lookbehind-Assertions)
  • 10.0.0 (vollständige Unterstützung)

Überarbeitung von Template-Literalen

Wenn einem Template-Literal unmittelbar ein Ausdruck vorausgeht, nennt man es ein getaggtes Template-Literal. Ein getaggtes Template ist nützlich, wenn Sie ein Template-Literal mit einer Funktion parsen möchten. Betrachten Sie das folgende Beispiel

function fn(string, substitute) {
  if(substitute === 'ES6') {
    substitute = 'ES2015'
  }
  return substitute + string[1];
}

const version = 'ES6';
const result = fn`${version} was a major update`;

console.log(result);    // → ES2015 was a major update

In diesem Code wird ein Tag-Ausdruck – eine normale Funktion – aufgerufen und ihr das Template-Literal übergeben. Die Funktion modifiziert einfach den dynamischen Teil des Strings und gibt ihn zurück.

Vor ES2018 hatten getaggte Template-Literale syntaktische Einschränkungen bezüglich Escape-Sequenzen. Ein Backslash, gefolgt von einer bestimmten Zeichenfolge, wurde als Sonderzeichen behandelt: \x wurde als Hex-Escape interpretiert, \u als Unicode-Escape und \ gefolgt von einer Ziffer als Oktal-Escape. Infolgedessen wurden Strings wie "C:\xxx\uuu" oder "\ubuntu" vom Interpreter als ungültige Escape-Sequenzen betrachtet und lösten einen SyntaxError aus.

ES2018 hebt diese Einschränkungen für getaggte Templates auf und stellt ungültige Escape-Sequenzen stattdessen als undefined dar, anstatt einen Fehler auszulösen

function fn(string, substitute) {
  console.log(substitute);    // → escape sequences:
  console.log(string[1]);     // → undefined
}

const str = 'escape sequences:';
const result = fn`${str} \ubuntu C:\xxx\uuu`;

Beachten Sie, dass die Verwendung illegaler Escape-Sequenzen in einem regulären Template-Literal immer noch einen Fehler verursacht

const result = `\ubuntu`;
// → SyntaxError: Invalid Unicode escape sequence

Unterstützung für überarbeitete Template-Literale

Chrome Firefox Safari Edge
62 56 11 Nein
Chrome Android Firefox Android iOS Safari Edge Mobile Samsung Internet Android Webview
62 56 11 Nein 8.2 62

Node.js

  • 8.3.0 (erfordert das –harmony-Laufzeit-Flag)
  • 8.10.0 (vollständige Unterstützung)

Zusammenfassung

Wir haben uns eingehend mit mehreren wichtigen Funktionen von ES2018 befasst, darunter asynchrone Iteration, Rest-/Spread-Eigenschaften, Promise.prototype.finally() und Ergänzungen zum RegExp-Objekt. Obwohl einige dieser Funktionen von einigen Browserherstellern noch nicht vollständig implementiert sind, können sie dank JavaScript-Transpilern wie Babel noch heute verwendet werden.

ECMAScript entwickelt sich rasant weiter und es werden immer wieder neue Funktionen eingeführt. Besuchen Sie die Liste der abgeschlossenen Vorschläge, um einen vollständigen Überblick über alle Neuerungen zu erhalten. Gibt es neue Funktionen, auf die Sie sich besonders freuen? Teilen Sie sie in den Kommentaren!