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 & blue';
console.log(str.replace(/(red) & (blue)/, '$2 & $1'));
// → blue & red
console.log(str.replace(/(?<red>red) & (?<blue>blue)/, '$<blue> & $<red>'));
// → blue & 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 = /(?<=\$|£|€)\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!
Toller Beitrag!
Eine kurze Anmerkung zu Folgendem
Das gleicht nur ab, was Unicode als „alphabtische“ Zeichen betrachtet. Definition von Wortzeichen in Unicode ist etwas breiter als das
Danke für den Hinweis! Sehr nützlich :)
Eine gute Verwendung für den Objekt-Spread-Operator ist das bedingte Hinzufügen einer Eigenschaft zu einem Objekt als Teil eines Ausdrucks
Praktisch im JSX-Kontext und in Render-Methoden.
Guter Tipp! Danke, Atanas.
TLDR;
Man sollte ES6 im Jahr 2018 kennen
Guter Artikel. Eine Sache noch: Soweit ich weiß, unterstützte der Spread-Operator auch in früheren Versionen Objekte, unter Verwendung eines ES6-Transpilers. Er wird häufig in Redux verwendet.
Spread-Eigenschaften wurden offiziell in ES2018 eingeführt, aber Transpiler unterstützten sie schon lange.
Hey,
Warum die Notwendigkeit von verschachtelten
console.log()s in einigen der Iterator-Beispiele? Wenn es mit den früheren Beispielen übereinstimmen soll, dann sollte es nur das Ergebnis und nicht das Promise-Objekt protokollieren, oder?Außerdem ist dies vielleicht nur mein Problem, aber es ist das erste Mal, dass ich das Tagged Template Literal sehe, und das hier verwendete Beispiel war etwas verwirrend.
Die Methode console.log() protokolliert einfach den Erfüllungswert, sobald das Promise erfüllt ist. Es gibt einige gute Beispiele für getaggte Templates auf MDN. Schauen Sie sie sich an
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
Danke für eine so gründliche und verständliche Überprüfung. Ich werde diesen Artikel zur weiteren Lektüre aufbewahren.
Ich freue mich, dass Sie ihn nützlich fanden. Danke fürs Lesen!
Gut zusammengeführte Arrays mit demselben Wert wählen immer den letzten.
Scheint auf das Hinzufügen von Arrays erweitert worden zu sein.
Ich habe nirgendwo eine bessere Erklärung für asynchrone Iteration gefunden als bei Ihnen. Ich hoffe, in der nächsten Version kommen mehr Funktionen