Dieses Kapitel sammelt die Überblick-Abschnitte aller Kapitel dieses Buches.
Math-FunktionenNumber-EigenschaftenMath-Methodenletconst...)Objectfor-of-Schleifethen()-AufrufenDie Einleitung der ES6-Spezifikation listet alle neuen Funktionen auf.
Einige der [ECMAScript 6] wichtigsten Erweiterungen umfassen Module, Klassendeklarationen, lexikalische Block-Scopes, Iteratoren und Generatoren, Promises für asynchrone Programmierung, Destrukturierungsmuster und Tail Calls. Die ECMAScript-Bibliothek von integrierten Funktionen wurde erweitert, um zusätzliche Datenabstraktionen zu unterstützen, darunter Maps, Sets und Arrays mit binären numerischen Werten, sowie zusätzliche Unterstützung für Unicode-Zusatzzeichen in Strings und regulären Ausdrücken. Die integrierten Funktionen sind jetzt durch Unterklasseerstellung erweiterbar.
Es gibt drei Hauptkategorien von Funktionen.
Math-Funktionen Sie können jetzt Ganzzahlen in binärer und oktaler Notation angeben.
> 0xFF // ES5: hexadecimal
255
> 0b11 // ES6: binary
3
> 0o10 // ES6: octal
8
Number-Eigenschaften Das globale Objekt Number hat einige neue Eigenschaften erhalten.
Number.EPSILON zum Vergleichen von Gleitkommazahlen mit einer Toleranz für Rundungsfehler.Number.isInteger(num) prüft, ob num eine Ganzzahl ist (eine Zahl ohne Dezimalbruch). > Number.isInteger(1.05)
false
> Number.isInteger(1)
true
> Number.isInteger(-3.1)
false
> Number.isInteger(-3)
true
Number.isSafeInteger(number)Number.MIN_SAFE_INTEGERNumber.MAX_SAFE_INTEGERNumber.isNaN(num) prüft, ob num der Wert NaN ist. Im Gegensatz zur globalen Funktion isNaN() wird ihr Argument nicht in eine Zahl konvertiert und ist daher sicherer für Nicht-Zahlen. > isNaN('???')
true
> Number.isNaN('???')
false
Number sind größtenteils äquivalent zu den globalen Funktionen mit denselben Namen: Number.isFinite, Number.parseFloat, Number.parseInt.Math-Methoden Das globale Objekt Math hat neue Methoden für numerische, trigonometrische und bitweise Operationen. Betrachten wir vier Beispiele.
Math.sign() gibt das Vorzeichen einer Zahl zurück.
> Math.sign(-8)
-1
> Math.sign(0)
0
> Math.sign(3)
1
Math.trunc() entfernt den Dezimalbruch einer Zahl.
> Math.trunc(3.1)
3
> Math.trunc(3.9)
3
> Math.trunc(-3.1)
-3
> Math.trunc(-3.9)
-3
Math.log10() berechnet den Logarithmus zur Basis 10.
> Math.log10(100)
2
Math.hypot() berechnet die Quadratwurzel der Summe der Quadrate seiner Argumente (Satz des Pythagoras).
> Math.hypot(3, 4)
5
Neue String-Methoden
> 'hello'.startsWith('hell')
true
> 'hello'.endsWith('ello')
true
> 'hello'.includes('ell')
true
> 'doo '.repeat(3)
'doo doo doo '
ES6 hat eine neue Art von String-Literalen, die *Template-Literale*.
// String interpolation via template literals (in backticks)
const first = 'Jane';
const last = 'Doe';
console.log(`Hello ${first} ${last}!`);
// Hello Jane Doe!
// Template literals also let you create strings with multiple lines
const multiLine = `
This is
a string
with multiple
lines`;
Symbole sind ein neuer primitiver Datentyp in ECMAScript 6. Sie werden über eine Factory-Funktion erstellt.
const mySymbol = Symbol('mySymbol');
Jedes Mal, wenn Sie die Factory-Funktion aufrufen, wird ein neues und eindeutiges Symbol erstellt. Der optionale Parameter ist ein beschreibender String, der beim Drucken des Symbols angezeigt wird (er hat keinen anderen Zweck).
> mySymbol
Symbol(mySymbol)
Symbole werden hauptsächlich als eindeutige Eigenschaftsschlüssel verwendet – ein Symbol kollidiert niemals mit einem anderen Eigenschaftsschlüssel (Symbol oder String). Sie können beispielsweise ein Objekt *iterierbar* machen (verwendbar über die for-of-Schleife und andere Sprachmechanismen), indem Sie das in Symbol.iterator gespeicherte Symbol als Schlüssel einer Methode verwenden (mehr Informationen zu Iterierbaren finden Sie im Kapitel über Iteration).
const iterableObject = {
[Symbol.iterator]() { // (A)
···
}
}
for (const x of iterableObject) {
console.log(x);
}
// Output:
// hello
// world
In Zeile A wird ein Symbol als Schlüssel der Methode verwendet. Dieser eindeutige Marker macht das Objekt iterierbar und ermöglicht uns die Verwendung der for-of-Schleife.
In ECMAScript 5 haben Sie möglicherweise Strings verwendet, um Konzepte wie Farben darzustellen. In ES6 können Sie Symbole verwenden und sicher sein, dass sie immer eindeutig sind.
const COLOR_RED = Symbol('Red');
const COLOR_ORANGE = Symbol('Orange');
const COLOR_YELLOW = Symbol('Yellow');
const COLOR_GREEN = Symbol('Green');
const COLOR_BLUE = Symbol('Blue');
const COLOR_VIOLET = Symbol('Violet');
function getComplement(color) {
switch (color) {
case COLOR_RED:
return COLOR_GREEN;
case COLOR_ORANGE:
return COLOR_BLUE;
case COLOR_YELLOW:
return COLOR_VIOLET;
case COLOR_GREEN:
return COLOR_RED;
case COLOR_BLUE:
return COLOR_ORANGE;
case COLOR_VIOLET:
return COLOR_YELLOW;
default:
throw new Exception('Unknown color: '+color);
}
}
Jedes Mal, wenn Sie Symbol('Red') aufrufen, wird ein neues Symbol erstellt. Daher kann COLOR_RED niemals mit einem anderen Wert verwechselt werden. Das wäre anders, wenn es der String 'Red' wäre.
Das Konvertieren (implizites Umwandeln) von Symbolen in Strings führt zu Fehlern.
const sym = Symbol('desc');
const str1 = '' + sym; // TypeError
const str2 = `${sym}`; // TypeError
Die einzige Lösung ist die explizite Konvertierung.
const str2 = String(sym); // 'Symbol(desc)'
const str3 = sym.toString(); // 'Symbol(desc)'
Das Verhindern von Konvertierungen verhindert einige Fehler, macht aber auch die Arbeit mit Symbolen komplizierter.
Die folgenden Operationen sind sich Symbole als Eigenschaftsschlüssel bewusst.
Reflect.ownKeys()[]Object.assign()Die folgenden Operationen ignorieren Symbole als Eigenschaftsschlüssel.
Object.keys()Object.getOwnPropertyNames()for-in-SchleifeES6 hat zwei neue Arten von Literalen: *Template-Literale* und *Tagged Template Literale*. Diese beiden Literale haben ähnliche Namen und sehen ähnlich aus, sind aber sehr unterschiedlich. Daher ist es wichtig, sie zu unterscheiden.
*Template-Literale* sind String-Literale, die sich über mehrere Zeilen erstrecken und interpolierte Ausdrücke enthalten können (eingefügt über ${···}).
const firstName = 'Jane';
console.log(`Hello ${firstName}!
How are you
today?`);
// Output:
// Hello Jane!
// How are you
// today?
*Tagged Template Literale* (kurz: *Tagged Templates*) werden erstellt, indem eine Funktion vor einem Template-Literal genannt wird.
> String.raw`A \tagged\ template`
'A \\tagged\\ template'
Tagged Templates sind Funktionsaufrufe. Im vorherigen Beispiel wird die Methode String.raw aufgerufen, um das Ergebnis des Tagged Templates zu erzeugen.
ES6 bietet zwei neue Möglichkeiten, Variablen zu deklarieren: let und const, die die ES5-Methode zur Variablendeklaration, var, weitgehend ersetzen.
let let funktioniert ähnlich wie var, aber die von ihm deklarierte Variable ist *block-scoped*, sie existiert nur innerhalb des aktuellen Blocks. var ist *function-scoped*.
Im folgenden Code können Sie sehen, dass die mit let deklarierte Variable tmp nur innerhalb des Blocks existiert, der in Zeile A beginnt.
function order(x, y) {
if (x > y) { // (A)
let tmp = x;
x = y;
y = tmp;
}
console.log(tmp===x); // ReferenceError: tmp is not defined
return [x, y];
}
const const funktioniert wie let, aber die von Ihnen deklarierte Variable muss sofort mit einem Wert initialisiert werden, der anschließend nicht mehr geändert werden kann.
const foo;
// SyntaxError: missing = in const declaration
const bar = 123;
bar = 456;
// TypeError: `bar` is read-only
Da for-of für jede Schleifeniteration ein *Binding* (Speicherplatz für eine Variable) erstellt, ist es in Ordnung, die Schleifenvariable mit const zu deklarieren.
for (const x of ['a', 'b']) {
console.log(x);
}
// Output:
// a
// b
Die folgende Tabelle gibt einen Überblick über sechs Möglichkeiten, wie Variablen in ES6 deklariert werden können (inspiriert von einer Tabelle von kangax).
| Hoisting | Geltungsbereich | Erzeugt globale Eigenschaften. | |
|---|---|---|---|
var |
Deklaration | Funktion | Ja |
let |
Temporäre tote Zone | Block | Nein |
const |
Temporäre tote Zone | Block | Nein |
Funktion |
Vollständig | Block | Ja |
class |
Nein | Block | Nein |
import |
Vollständig | Modul-global | Nein |
*Destrukturierung* ist eine bequeme Möglichkeit, mehrere Werte aus Daten zu extrahieren, die in (möglicherweise verschachtelten) Objekten und Arrays gespeichert sind. Sie kann an Stellen verwendet werden, die Daten empfangen (z. B. auf der linken Seite einer Zuweisung). Wie die Werte extrahiert werden, wird über Muster angegeben (lesen Sie weiter für Beispiele).
Objekte destrukturieren.
const obj = { first: 'Jane', last: 'Doe' };
const {first: f, last: l} = obj;
// f = 'Jane'; l = 'Doe'
// {prop} is short for {prop: prop}
const {first, last} = obj;
// first = 'Jane'; last = 'Doe'
Destrukturierung hilft bei der Verarbeitung von Rückgabewerten.
const obj = { foo: 123 };
const {writable, configurable} =
Object.getOwnPropertyDescriptor(obj, 'foo');
console.log(writable, configurable); // true true
Array-Destrukturierung (funktioniert für alle iterierbaren Werte).
const iterable = ['a', 'b'];
const [x, y] = iterable;
// x = 'a'; y = 'b'
Destrukturierung hilft bei der Verarbeitung von Rückgabewerten.
const [all, year, month, day] =
/^(\d\d\d\d)-(\d\d)-(\d\d)$/
.exec('2999-12-31');
Destrukturierung kann an den folgenden Stellen verwendet werden (ich zeige Array-Muster zur Veranschaulichung; Objekt-Muster funktionieren genauso gut).
// Variable declarations:
const [x] = ['a'];
let [x] = ['a'];
var [x] = ['a'];
// Assignments:
[x] = ['a'];
// Parameter definitions:
function f([x]) { ··· }
f(['a']);
Sie können auch in einer for-of-Schleife destrukturieren.
const arr = ['a', 'b'];
for (const [index, element] of arr.entries()) {
console.log(index, element);
}
// Output:
// 0 a
// 1 b
Die Parameterbehandlung wurde in ECMAScript 6 erheblich verbessert. Sie unterstützt jetzt Standardparameterwerte, Rest-Parameter (Varargs) und Destrukturierung.
Zusätzlich hilft der Spread-Operator bei Funktions-/Methoden-/Konstruktoraufrufen und Array-Literalen.
Ein *Standardparameterwert* wird einem Parameter über ein Gleichheitszeichen (=) zugewiesen. Wenn ein Aufrufer keinen Wert für den Parameter angibt, wird der Standardwert verwendet. Im folgenden Beispiel ist der Standardparameterwert von y 0.
function func(x, y=0) {
return [x, y];
}
func(1, 2); // [1, 2]
func(1); // [1, 0]
func(); // [undefined, 0]
Wenn Sie einem Parameternamen den Rest-Operator (...) voranstellen, erhält dieser Parameter alle verbleibenden Parameter als Array.
function format(pattern, ...params) {
return {pattern, params};
}
format(1, 2, 3);
// { pattern: 1, params: [ 2, 3 ] }
format();
// { pattern: undefined, params: [] }
Sie können benannte Parameter simulieren, wenn Sie mit einem Objektmuster in der Parameterliste destrukturieren.
function selectEntries({ start=0, end=-1, step=1 } = {}) { // (A)
// The object pattern is an abbreviation of:
// { start: start=0, end: end=-1, step: step=1 }
// Use the variables `start`, `end` and `step` here
···
}
selectEntries({ start: 10, end: 30, step: 2 });
selectEntries({ step: 3 });
selectEntries({});
selectEntries();
Das = {} in Zeile A ermöglicht es Ihnen, selectEntries() ohne Parameter aufzurufen.
...) Bei Funktions- und Konstruktoraufrufen wandelt der Spread-Operator iterierbare Werte in Argumente um.
> Math.max(-1, 5, 11, 3)
11
> Math.max(...[-1, 5, 11, 3])
11
> Math.max(-1, ...[-5, 11], 3)
11
In Array-Literalen wandelt der Spread-Operator iterierbare Werte in Array-Elemente um.
> [1, ...[2,3], 4]
[1, 2, 3, 4]
In ES5 spielte eine einzelne Konstruktion, die (traditionelle) Funktion, drei Rollen:
In ES6 gibt es mehr Spezialisierung. Die drei Aufgaben werden nun wie folgt behandelt. Bezüglich Funktionsdefinitionen und Klassendefinitionen ist eine Definition entweder eine Deklaration oder ein Ausdruck.
Besonders für Callbacks sind Pfeilfunktionen praktisch, da sie nicht den this des umgebenden Geltungsbereichs überschatten.
Für längere Callbacks und eigenständige Funktionen können traditionelle Funktionen in Ordnung sein. Einige APIs verwenden this als impliziten Parameter. In diesem Fall haben Sie keine andere Wahl, als traditionelle Funktionen zu verwenden.
Beachten Sie, dass ich unterscheide:
Auch wenn sich ihr Verhalten unterscheidet (wie später erklärt wird), sind all diese Entitäten Funktionen. Zum Beispiel:
> typeof (() => {}) // arrow function
'function'
> typeof function* () {} // generator function
'function'
> typeof class {} // class
'function'
Es gibt zwei Vorteile von Pfeilfunktionen.
Erstens sind sie weniger aufwendig als traditionelle Funktionsausdrücke.
const arr = [1, 2, 3];
const squares = arr.map(x => x * x);
// Traditional function expression:
const squares = arr.map(function (x) { return x * x });
Zweitens wird ihr this aus der Umgebung übernommen (*lexikalisch*). Daher benötigen Sie bind() oder that = this nicht mehr.
function UiComponent() {
const button = document.getElementById('myButton');
button.addEventListener('click', () => {
console.log('CLICK');
this.handleClick(); // lexical `this`
});
}
Die folgenden Variablen sind in Pfeilfunktionen alle lexikalisch.
argumentssuperthisnew.targetMethodendefinitionen
const obj = {
myMethod(x, y) {
···
}
};
Eigenschaftswert-Kurzschriften
const first = 'Jane';
const last = 'Doe';
const obj = { first, last };
// Same as:
const obj = { first: first, last: last };
Berechenbare Eigenschaftsschlüssel
const propKey = 'foo';
const obj = {
[propKey]: true,
['b'+'ar']: 123
};
Diese neue Syntax kann auch für Methodendefinitionen verwendet werden.
const obj = {
['h'+'ello']() {
return 'hi';
}
};
console.log(obj.hello()); // hi
Der Haupteinsatzfall für berechenbare Eigenschaftsschlüssel ist die einfache Verwendung von Symbolen als Eigenschaftsschlüssel.
Object Die wichtigste neue Methode von Object ist assign(). Traditionell wurde diese Funktionalität in der JavaScript-Welt extend() genannt. Im Gegensatz zur Funktionsweise dieser klassischen Operation berücksichtigt Object.assign() nur *eigene* (nicht vererbte) Eigenschaften.
const obj = { foo: 123 };
Object.assign(obj, { bar: true });
console.log(JSON.stringify(obj));
// {"foo":123,"bar":true}
Eine Klasse und eine Unterklasse.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
toString() {
return `(${this.x}, ${this.y})`;
}
}
class ColorPoint extends Point {
constructor(x, y, color) {
super(x, y);
this.color = color;
}
toString() {
return super.toString() + ' in ' + this.color;
}
}
Verwendung der Klassen.
> const cp = new ColorPoint(25, 8, 'green');
> cp.toString();
'(25, 8) in green'
> cp instanceof ColorPoint
true
> cp instanceof Point
true
Unter der Haube sind ES6-Klassen nichts radikal Neues: Sie bieten hauptsächlich eine bequemere Syntax zur Erstellung von Oldschool-Konstruktorfunktionen. Das sehen Sie, wenn Sie typeof verwenden.
> typeof Point
'function'
JavaScript hat seit langem Module. Sie wurden jedoch über Bibliotheken implementiert und nicht in die Sprache integriert. ES6 ist das erste Mal, dass JavaScript eingebaute Module hat.
ES6-Module werden in Dateien gespeichert. Es gibt genau ein Modul pro Datei und eine Datei pro Modul. Sie haben zwei Möglichkeiten, Dinge aus einem Modul zu exportieren. Diese beiden Wege können gemischt werden, aber es ist normalerweise besser, sie getrennt zu verwenden.
Es kann mehrere *benannte Exporte* geben.
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
//------ main.js ------
import { square, diag } from 'lib';
console.log(square(11)); // 121
console.log(diag(4, 3)); // 5
Sie können auch das vollständige Modul importieren.
//------ main.js ------
import * as lib from 'lib';
console.log(lib.square(11)); // 121
console.log(lib.diag(4, 3)); // 5
Es kann einen einzelnen *Standardexport* geben. Zum Beispiel eine Funktion.
//------ myFunc.js ------
export default function () { ··· } // no semicolon!
//------ main1.js ------
import myFunc from 'myFunc';
myFunc();
Oder eine Klasse.
//------ MyClass.js ------
export default class { ··· } // no semicolon!
//------ main2.js ------
import MyClass from 'MyClass';
const inst = new MyClass();
Beachten Sie, dass am Ende kein Semikolon steht, wenn Sie eine Funktion oder eine Klasse (die anonyme Deklarationen sind) als Standard exportieren.
| Skripte | Module | |
|---|---|---|
| HTML-Element | <script> |
<script type="module"> |
| Standardmodus | nicht-strict | strict |
| Top-Level-Variablen sind | global | lokal für das Modul |
Wert von this auf Top-Level |
Fenster |
undefined |
| Ausgeführt | synchron | asynchron |
Deklarative Importe (import-Anweisung) |
nein | ja |
| Programmatische Importe (Promise-basierte API) | ja | ja |
| Dateierweiterung | .js |
.js |
for-of-Schleife for-of ist eine neue Schleife in ES6, die sowohl for-in als auch forEach() ersetzt und das neue Iterationsprotokoll unterstützt.
Verwenden Sie sie, um über *iterierbare* Objekte (Arrays, Strings, Maps, Sets usw.; siehe Kap. „Iterierbare Objekte und Iteratoren“) zu iterieren.
const iterable = ['a', 'b'];
for (const x of iterable) {
console.log(x);
}
// Output:
// a
// b
break und continue funktionieren in for-of-Schleifen.
for (const x of ['a', '', 'b']) {
if (x.length === 0) break;
console.log(x);
}
// Output:
// a
Greifen Sie sowohl auf Elemente als auch auf deren Indizes zu, während Sie über ein Array iterieren (die eckigen Klammern vor of bedeuten, dass wir Destrukturierung verwenden).
const arr = ['a', 'b'];
for (const [index, element] of arr.entries()) {
console.log(`${index}. ${element}`);
}
// Output:
// 0. a
// 1. b
Iteration über die [Schlüssel, Wert]-Einträge in einer Map (die eckigen Klammern vor of bedeuten, dass wir Destrukturierung verwenden).
const map = new Map([
[false, 'no'],
[true, 'yes'],
]);
for (const [key, value] of map) {
console.log(`${key} => ${value}`);
}
// Output:
// false => no
// true => yes
Neue statische Array-Methoden.
Array.from(arrayLike, mapFunc?, thisArg?)Array.of(...items)Neue Array.prototype-Methoden.
Array.prototype.entries()Array.prototype.keys()Array.prototype.values()Array.prototype.find(predicate, thisArg?)Array.prototype.findIndex(predicate, thisArg?)Array.prototype.copyWithin(target, start, end=this.length)Array.prototype.fill(value, start=0, end=this.length)Unter anderem sind die folgenden vier Datenstrukturen in ECMAScript 6 neu: Map, WeakMap, Set und WeakSet.
Die Schlüssel einer Map können beliebige Werte sein.
> const map = new Map(); // create an empty Map
> const KEY = {};
> map.set(KEY, 123);
> map.get(KEY)
123
> map.has(KEY)
true
> map.delete(KEY);
true
> map.has(KEY)
false
Sie können ein Array (oder jedes iterierbare Objekt) mit [Schlüssel, Wert]-Paaren verwenden, um die anfänglichen Daten in der Map einzurichten.
const map = new Map([
[ 1, 'one' ],
[ 2, 'two' ],
[ 3, 'three' ], // trailing comma is ignored
]);
Ein Set ist eine Sammlung von eindeutigen Elementen.
const arr = [5, 1, 5, 7, 7, 5];
const unique = [...new Set(arr)]; // [ 5, 1, 7 ]
Wie Sie sehen können, können Sie ein Set mit Elementen initialisieren, indem Sie dem Konstruktor ein iterierbares Objekt (arr im Beispiel) mit diesen Elementen übergeben.
Eine WeakMap ist eine Map, die ihre Schlüssel nicht vor der Garbage Collection schützt. Das bedeutet, dass Sie Objekten Daten zuordnen können, ohne sich um Speicherlecks sorgen zu müssen. Zum Beispiel:
//----- Manage listeners
const _objToListeners = new WeakMap();
function addListener(obj, listener) {
if (! _objToListeners.has(obj)) {
_objToListeners.set(obj, new Set());
}
_objToListeners.get(obj).add(listener);
}
function triggerListeners(obj) {
const listeners = _objToListeners.get(obj);
if (listeners) {
for (const listener of listeners) {
listener();
}
}
}
//----- Example: attach listeners to an object
const obj = {};
addListener(obj, () => console.log('hello'));
addListener(obj, () => console.log('world'));
//----- Example: trigger listeners
triggerListeners(obj);
// Output:
// hello
// world
Ganzzahlige Arrays (Typed Arrays) sind eine ECMAScript 6 API zur Handhabung binärer Daten.
Codebeispiel.
const typedArray = new Uint8Array([0,1,2]);
console.log(typedArray.length); // 3
typedArray[0] = 5;
const normalArray = [...typedArray]; // [5,1,2]
// The elements are stored in typedArray.buffer.
// Get a different view on the same data:
const dataView = new DataView(typedArray.buffer);
console.log(dataView.getUint8(0)); // 5
Instanzen von ArrayBuffer speichern die zu verarbeitenden Binärdaten. Zwei Arten von *Views* werden verwendet, um auf die Daten zuzugreifen:
Uint8Array, Int16Array, Float32Array usw.) interpretieren den ArrayBuffer als indizierte Sequenz von Elementen eines einzelnen Typs.DataView ermöglichen den Zugriff auf Daten als Elemente verschiedener Typen (Uint8, Int16, Float32 usw.) an jedem Byte-Offset innerhalb eines ArrayBuffer.Die folgenden Browser-APIs unterstützen Ganzzahlige Arrays (Details werden in einem eigenen Abschnitt erwähnt).
ES6 führt einen neuen Mechanismus für die Traversierung von Daten ein: *Iteration*. Zwei Konzepte sind zentral für die Iteration:
Symbol.iterator ist. Diese Methode ist eine Factory für *Iteratoren*.Als Schnittstellen in TypeScript-Notation ausgedrückt, sehen diese Rollen wie folgt aus:
interface Iterable {
[Symbol.iterator]() : Iterator;
}
interface Iterator {
next() : IteratorResult;
}
interface IteratorResult {
value: any;
done: boolean;
}
Die folgenden Werte sind iterierbar:
Einfache Objekte sind nicht iterierbar (warum, wird in einem eigenen Abschnitt erklärt).
Sprachkonstrukte, die über Iteration auf Daten zugreifen:
const [a,b] = new Set(['a', 'b', 'c']);
for-of-Schleife for (const x of ['a', 'b', 'c']) {
console.log(x);
}
Array.from():
const arr = Array.from(new Set(['a', 'b', 'c']));
...) const arr = [...new Set(['a', 'b', 'c'])];
const map = new Map([[false, 'no'], [true, 'yes']]);
const set = new Set(['a', 'b', 'c']);
Promise.all(), Promise.race() Promise.all(iterableOverPromises).then(···);
Promise.race(iterableOverPromises).then(···);
yield*:
yield* anIterable;
Sie können sich Generatoren als Prozesse (Code-Teile) vorstellen, die Sie anhalten und fortsetzen können.
function* genFunc() {
// (A)
console.log('First');
yield;
console.log('Second');
}
Beachten Sie die neue Syntax: function* ist ein neues „Schlüsselwort“ für *Generatorfunktionen* (es gibt auch *Generatormethoden*). yield ist ein Operator, mit dem sich ein Generator selbst anhalten kann. Darüber hinaus können Generatoren auch Eingaben empfangen und Ausgaben über yield senden.
Wenn Sie eine Generatorfunktion genFunc() aufrufen, erhalten Sie ein *Generatorobjekt* genObj, mit dem Sie den Prozess steuern können.
const genObj = genFunc();
Der Prozess wird anfangs in Zeile A angehalten. genObj.next() setzt die Ausführung fort, ein yield in genFunc() pausiert die Ausführung.
genObj.next();
// Output: First
genObj.next();
// output: Second
Es gibt vier Arten von Generatoren:
function* genFunc() { ··· }
const genObj = genFunc();
const genFunc = function* () { ··· };
const genObj = genFunc();
const obj = {
* generatorMethod() {
···
}
};
const genObj = obj.generatorMethod();
class MyClass {
* generatorMethod() {
···
}
}
const myInst = new MyClass();
const genObj = myInst.generatorMethod();
Die von Generatoren zurückgegebenen Objekte sind iterierbar; jedes yield trägt zur Sequenz der iterierten Werte bei. Daher können Sie Generatoren verwenden, um iterierbare Objekte zu implementieren, die von verschiedenen ES6-Sprachmechanismen konsumiert werden können: for-of-Schleife, Spread-Operator (...) usw.
Die folgende Funktion gibt ein iterierbares Objekt über die Eigenschaften eines Objekts zurück, ein [Schlüssel, Wert]-Paar pro Eigenschaft.
function* objectEntries(obj) {
const propKeys = Reflect.ownKeys(obj);
for (const propKey of propKeys) {
// `yield` returns a value and then pauses
// the generator. Later, execution continues
// where it was previously paused.
yield [propKey, obj[propKey]];
}
}
objectEntries() wird wie folgt verwendet:
const jane = { first: 'Jane', last: 'Doe' };
for (const [key,value] of objectEntries(jane)) {
console.log(`${key}: ${value}`);
}
// Output:
// first: Jane
// last: Doe
Wie genau objectEntries() funktioniert, wird in einem eigenen Abschnitt erklärt. Die Implementierung derselben Funktionalität ohne Generatoren ist wesentlich aufwendiger.
Sie können Generatoren verwenden, um die Arbeit mit Promises erheblich zu vereinfachen. Betrachten wir eine Promise-basierte Funktion fetchJson() und wie sie durch Generatoren verbessert werden kann.
function fetchJson(url) {
return fetch(url)
.then(request => request.text())
.then(text => {
return JSON.parse(text);
})
.catch(error => {
console.log(`ERROR: ${error.stack}`);
});
}
Mit der Bibliothek co und einem Generator sieht dieser asynchrone Code synchron aus.
const fetchJson = co.wrap(function* (url) {
try {
let request = yield fetch(url);
let text = yield request.text();
return JSON.parse(text);
}
catch (error) {
console.log(`ERROR: ${error.stack}`);
}
});
ECMAScript 2017 wird asynchrone Funktionen haben, die intern auf Generatoren basieren. Mit ihnen sieht der Code so aus:
async function fetchJson(url) {
try {
let request = await fetch(url);
let text = await request.text();
return JSON.parse(text);
}
catch (error) {
console.log(`ERROR: ${error.stack}`);
}
}
Alle Versionen können wie folgt aufgerufen werden:
fetchJson('http://example.com/some_file.json')
.then(obj => console.log(obj));
Generatoren können über next() Eingaben über yield empfangen. Das bedeutet, dass Sie einen Generator wecken können, sobald neue Daten asynchron eintreffen, und für den Generator fühlt es sich an, als ob er die Daten synchron empfängt.
Die folgenden Funktionen für reguläre Ausdrücke sind in ECMAScript 6 neu:
/y (sticky) verankert jede Übereinstimmung eines regulären Ausdrucks am Ende der vorherigen Übereinstimmung./u (unicode) behandelt Ersatzpaare (wie \uD83D\uDE80) als Code-Punkte und ermöglicht die Verwendung von Unicode-Code-Punkt-Escapes (wie \u{1F680}) in regulären Ausdrücken.flags gibt Ihnen Zugriff auf die Flags eines regulären Ausdrucks, so wie source Ihnen bereits Zugriff auf das Muster in ES5 gibt. > /abc/ig.source // ES5
'abc'
> /abc/ig.flags // ES6
'gi'
RegExp() verwenden, um eine Kopie eines regulären Ausdrucks zu erstellen > new RegExp(/abc/ig).flags
'gi'
> new RegExp(/abc/ig, 'i').flags // change flags
'i'
Promises sind eine Alternative zu Callbacks für die Bereitstellung der Ergebnisse einer asynchronen Berechnung. Sie erfordern mehr Aufwand von den Implementierern asynchroner Funktionen, bieten aber mehrere Vorteile für die Benutzer dieser Funktionen.
Die folgende Funktion gibt ein Ergebnis asynchron über ein Promise zurück
function asyncFunc() {
return new Promise(
function (resolve, reject) {
···
resolve(result);
···
reject(error);
});
}
Sie rufen asyncFunc() wie folgt auf
asyncFunc()
.then(result => { ··· })
.catch(error => { ··· });
then()-Aufrufen then() gibt immer ein Promise zurück, was Ihnen ermöglicht, Methodenaufrufe zu verketten
asyncFunc1()
.then(result1 => {
// Use result1
return asyncFunction2(); // (A)
})
.then(result2 => { // (B)
// Use result2
})
.catch(error => {
// Handle errors of asyncFunc1() and asyncFunc2()
});
Wie das von then() zurückgegebene Promise P erfüllt wird, hängt davon ab, was sein Callback tut
asyncFunction2 aufgreifen.Darüber hinaus ist zu beachten, wie catch() die Fehler von zwei asynchronen Funktionsaufrufen (asyncFunction1() und asyncFunction2()) behandelt. Das heißt, unbehandelte Fehler werden weitergegeben, bis ein Fehlerhandler vorhanden ist.
Wenn Sie asynchrone Funktionsaufrufe über then() verketten, werden sie nacheinander ausgeführt, einer nach dem anderen
asyncFunc1()
.then(() => asyncFunc2());
Wenn Sie das nicht tun und alle sofort aufrufen, werden sie im Grunde parallel ausgeführt (ein Fork in der Unix-Prozess-Terminologie)
asyncFunc1();
asyncFunc2();
Promise.all() ermöglicht es Ihnen, benachrichtigt zu werden, sobald alle Ergebnisse vorliegen (ein Join in der Unix-Prozess-Terminologie). Seine Eingabe ist ein Array von Promises, seine Ausgabe ein einzelnes Promise, das mit einem Array der Ergebnisse erfüllt wird.
Promise.all([
asyncFunc1(),
asyncFunc2(),
])
.then(([result1, result2]) => {
···
})
.catch(err => {
// Receives first rejection among the Promises
···
});
Die Promise-API befasst sich mit der asynchronen Bereitstellung von Ergebnissen. Ein Promise-Objekt (kurz: Promise) ist ein Platzhalter für das Ergebnis, das über dieses Objekt geliefert wird.
Zustände
Reagieren auf Zustandsänderungen
then() registrieren, um über eine Erfüllung oder Ablehnung benachrichtigt zu werden.then()-Methode hat. Immer wenn die API nur daran interessiert ist, über Erfüllungen benachrichtigt zu werden, verlangt sie nur Thenables (z. B. die von then() und catch() zurückgegebenen Werte; oder die an Promise.all() und Promise.race() übergebenen Werte).Zustände ändern: Es gibt zwei Operationen zum Ändern des Zustands eines Promise. Nachdem Sie eine davon einmal aufgerufen haben, haben weitere Aufrufe keine Auswirkung.
Proxies ermöglichen es Ihnen, Operationen auf Objekten abzufangen und anzupassen (wie z. B. das Abrufen von Eigenschaften). Sie sind ein Metaprogrammierungs-Feature.
Im folgenden Beispiel ist proxy das Objekt, dessen Operationen wir abfangen, und handler ist das Objekt, das die Abfangvorgänge handhabt. In diesem Fall fangen wir nur eine einzige Operation ab, das get (Abrufen von Eigenschaften).
const target = {};
const handler = {
get(target, propKey, receiver) {
console.log('get ' + propKey);
return 123;
}
};
const proxy = new Proxy(target, handler);
Wenn wir die Eigenschaft proxy.foo abrufen, fängt der Handler diese Operation ab
> proxy.foo
get foo
123
Konsultieren Sie die Referenz für die vollständige API für eine Liste der abfangbaren Operationen.