Zum Hauptinhalt springen

39 Posts getaggt mit "ECMAScript"

Alle Tags anzeigen

Schwache Verweise und Finalizer

· 10 Minuten Lesezeit
Sathya Gunasekaran ([@_gsathya](https://twitter.com/_gsathya)), Mathias Bynens ([@mathias](https://twitter.com/mathias)), Shu-yu Guo ([@_shu](https://twitter.com/_shu)), und Leszek Swirski ([@leszekswirski](https://twitter.com/leszekswirski))

Im Allgemeinen werden Referenzen auf Objekte in JavaScript stark gehalten, was bedeutet, dass das Objekt nicht vom Garbage Collector gesammelt wird, solange eine Referenz darauf existiert.

const ref = { x: 42, y: 51 };
// Solange Sie Zugriff auf `ref` (oder eine andere Referenz auf
// dasselbe Objekt) haben, wird das Objekt nicht vom Garbage Collector gesammelt.

Momentan sind WeakMaps und WeakSets die einzigen Möglichkeiten, ein Objekt in JavaScript schwach zu referenzieren: Das Hinzufügen eines Objekts als Schlüssel zu einer WeakMap oder einem WeakSet verhindert nicht, dass es vom Garbage Collector gesammelt wird.

const wm = new WeakMap();
{
const ref = {};
const metaData = 'foo';
wm.set(ref, metaData);
wm.get(ref);
// → metaData
}
// Wir haben in diesem Block keinen Zugriff mehr auf `ref`, daher kann es
// jetzt vom Garbage Collector gesammelt werden, obwohl es ein Schlüssel in `wm` ist, auf den wir noch zugreifen können.

Stabile `Array.prototype.sort`

· 3 Minuten Lesezeit
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Angenommen, Sie haben ein Array von Hunden, wobei jeder Hund einen Namen und eine Bewertung hat. (Falls dies wie ein seltsames Beispiel klingt, sollten Sie wissen, dass es ein Twitter-Konto gibt, das sich genau darauf spezialisiert hat… Fragen Sie nicht!)

// Beachten Sie, dass das Array nach `name` alphabetisch vorsortiert ist.
const doggos = [
{ name: 'Abby', rating: 12 },
{ name: 'Bandit', rating: 13 },
{ name: 'Choco', rating: 14 },
{ name: 'Daisy', rating: 12 },
{ name: 'Elmo', rating: 12 },
{ name: 'Falco', rating: 13 },
{ name: 'Ghost', rating: 14 },
];
// Sortieren Sie die Hunde nach `rating` in absteigender Reihenfolge.
// (Dies aktualisiert `doggos` direkt.)
doggos.sort((a, b) => b.rating - a.rating);

`Symbol.prototype.description`

· Eine Minute Lesezeit
Mathias Bynens ([@mathias](https://twitter.com/mathias))

JavaScript-Symbols können bei ihrer Erstellung eine Beschreibung erhalten:

const symbol = Symbol('foo');
// ^^^^^

Bisher war die einzige Möglichkeit, programmgesteuert auf diese Beschreibung zuzugreifen, indirekt über Symbol.prototype.toString():

const symbol = Symbol('foo');
// ^^^^^
symbol.toString();
// → 'Symbol(foo)'
// ^^^
symbol.toString().slice(7, -1); // 🤔
// → 'foo'

Der Code wirkt jedoch etwas magisch, ist nicht sehr selbsterklärend und verletzt das Prinzip „Absicht ausdrücken, nicht Implementierung“. Die obige Technik erlaubt es außerdem nicht, zwischen einem Symbol ohne Beschreibung (d.h. Symbol()) und einem Symbol mit leerem String als Beschreibung (d.h. Symbol('')) zu unterscheiden.

`Object.fromEntries`

· 4 Minuten Lesezeit
Mathias Bynens ([@mathias](https://twitter.com/mathias)), JavaScript-Zauberer

Object.fromEntries ist eine nützliche Ergänzung zur eingebauten JavaScript-Bibliothek. Bevor erklärt wird, was es tut, hilft es, die bereits vorhandene API Object.entries zu verstehen.

Object.entries

Die Object.entries-API existiert schon seit einiger Zeit.

Für jedes Schlüssel-Wert-Paar in einem Objekt liefert Object.entries ein Array, bei dem das erste Element der Schlüssel und das zweite Element der Wert ist.

Object.entries ist besonders nützlich in Kombination mit for-of, da es ermöglicht, sehr elegant über alle Schlüssel-Wert-Paare in einem Objekt zu iterieren:

const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]

for (const [key, value] of entries) {
console.log(`Der Wert von ${key} ist ${value}.`);
}
// Ausgabe:
// Der Wert von x ist 42.
// Der Wert von y ist 50.

Leider gibt es keinen einfachen Weg, um aus dem Ergebnis von entries wieder ein äquivalentes Objekt zu erzeugen… bis jetzt!

Object.fromEntries

Die neue API Object.fromEntries führt das Umkehrverfahren von Object.entries durch. Dies erleichtert die Rekonstruktion eines Objekts basierend auf seinen Einträgen:

const object = { x: 42, y: 50 };
const entries = Object.entries(object);
// → [['x', 42], ['y', 50]]

const result = Object.fromEntries(entries);
// → { x: 42, y: 50 }

Ein üblicher Anwendungsfall ist das Transformieren von Objekten. Dies können Sie jetzt tun, indem Sie über deren Einträge iterieren und dann Methoden für Arrays verwenden, die Ihnen möglicherweise schon bekannt sind:

const object = { x: 42, y: 50, abc: 9001 };
const result = Object.fromEntries(
Object.entries(object)
.filter(([ key, value ]) => key.length === 1)
.map(([ key, value ]) => [ key, value * 2 ])
);
// → { x: 84, y: 100 }

In diesem Beispiel filtern wir mit filter das Objekt, um nur Schlüssel der Länge 1 zu erhalten, das heißt, nur die Schlüssel x und y, nicht jedoch den Schlüssel abc. Wir verwenden dann map, um über die verbleibenden Einträge zu iterieren und ein aktualisiertes Schlüssel-Wert-Paar für jeden zurückzugeben. In diesem Beispiel verdoppeln wir jeden Wert, indem wir ihn mit 2 multiplizieren. Das Endergebnis ist ein neues Objekt mit nur den Eigenschaften x und y und den neuen Werten.

Promise-Kombinatoren

· 4 Minuten Lesezeit
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Seit der Einführung von Promises in ES2015 unterstützt JavaScript genau zwei Promise-Kombinatoren: die statischen Methoden Promise.all und Promise.race.

Zwei neue Vorschläge befinden sich derzeit im Standardisierungsprozess: Promise.allSettled und Promise.any. Mit diesen Ergänzungen gibt es insgesamt vier Promise-Kombinatoren in JavaScript, die jeweils unterschiedliche Anwendungsfälle ermöglichen.

`Array.prototype.flat` und `Array.prototype.flatMap`

· 2 Minuten Lesezeit
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Array.prototype.flat

Das Array in diesem Beispiel ist mehrere Ebenen tief: Es enthält ein Array, das wiederum ein weiteres Array enthält.

const array = [1, [2, [3]]];
// ^^^^^^^^^^^^^ äußeres Array
// ^^^^^^^^ inneres Array
// ^^^ innerstes Array

Array#flat gibt eine reduzierte Version eines gegebenen Arrays zurück.

array.flat();
// → [1, 2, [3]]

// …entspricht folgendem:
array.flat(1);
// → [1, 2, [3]]

Die Standardtiefe ist 1, aber Sie können jede Zahl übergeben, um rekursiv bis zu dieser Tiefe zu reduzieren. Um rekursiv zu reduzieren, bis das Ergebnis keine verschachtelten Arrays mehr enthält, geben wir Infinity weiter.

// Rekursiv reduzieren, bis das Array keine verschachtelten Arrays mehr enthält:
array.flat(Infinity);
// → [1, 2, 3]

Warum heißt diese Methode Array.prototype.flat und nicht Array.prototype.flatten? Lesen Sie unseren Bericht zu #SmooshGate, um es herauszufinden!

Array.prototype.flatMap

Hier ist ein weiteres Beispiel. Wir haben eine Funktion duplicate, die einen Wert nimmt und ein Array zurückgibt, das diesen Wert zweimal enthält. Wenn wir duplicate auf jeden Wert in einem Array anwenden, erhalten wir ein verschachteltes Array.

const duplicate = (x) => [x, x];

[2, 3, 4].map(duplicate);
// → [[2, 2], [3, 3], [4, 4]]

Sie können dann flat auf das Ergebnis aufrufen, um das Array zu reduzieren:

[2, 3, 4].map(duplicate).flat(); // 🐌
// → [2, 2, 3, 3, 4, 4]

Da dieses Muster in der funktionalen Programmierung so häufig vorkommt, gibt es jetzt eine eigene Methode flatMap dafür.

[2, 3, 4].flatMap(duplicate); // 🚀
// → [2, 2, 3, 3, 4, 4]

flatMap ist ein wenig effizienter, als map gefolgt von einem separaten flat zu verwenden.

Interessiert an Anwendungsfällen für flatMap? Schauen Sie sich Axel Rauschmayers Erklärung an.

Unterstützung für Array#{flat,flatMap}

Numerische Separatoren

· 2 Minuten Lesezeit
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Große numerische Literale sind für das menschliche Auge schwer schnell zu erkennen, besonders wenn viele sich wiederholende Ziffern vorhanden sind:

1000000000000
1019436871.42

Um die Lesbarkeit zu verbessern, ermöglicht eine neue JavaScript-Sprachfunktion die Verwendung von Unterstrichen als Trennzeichen in numerischen Literalen. Somit kann das oben stehende nun so umgeschrieben werden, dass die Ziffern beispielsweise in Tausendergruppen zusammengefasst werden:

`String.prototype.matchAll`

· 3 Minuten Lesezeit
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Es ist üblich, denselben regulären Ausdruck wiederholt auf einen String anzuwenden, um alle Übereinstimmungen zu erhalten. Bis zu einem gewissen Grad ist dies heute bereits durch die Methode String#match möglich.

In diesem Beispiel finden wir alle Wörter, die nur aus hexadezimalen Ziffern bestehen, und protokollieren dann jede Übereinstimmung:

const string = 'Magische Hex-Zahlen: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.match(regex)) {
console.log(match);
}

// Ausgabe:
//
// 'DEADBEEF'
// 'CAFE'

Dies gibt Ihnen jedoch nur die Substrings, die übereinstimmen. Normalerweise möchten Sie nicht nur die Substrings, sondern auch zusätzliche Informationen wie den Index jedes Substrings oder die Gruppen, die innerhalb jeder Übereinstimmung erfasst wurden.

Es ist bereits möglich, dies zu erreichen, indem man eine eigene Schleife schreibt und die Übereinstimmungsobjekte selbst verfolgt, aber das ist ein wenig lästig und nicht sehr bequem:

const string = 'Magische Hex-Zahlen: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
let match;
while (match = regex.exec(string)) {
console.log(match);
}

// Ausgabe:
//
// [ 'DEADBEEF', index: 19, input: 'Magische Hex-Zahlen: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magische Hex-Zahlen: DEADBEEF CAFE' ]

Die neue API String#matchAll macht dies einfacher als je zuvor: Sie können jetzt eine einfache for-of-Schleife schreiben, um alle Übereinstimmungsobjekte zu erhalten.

const string = 'Magische Hex-Zahlen: DEADBEEF CAFE';
const regex = /\b\p{ASCII_Hex_Digit}+\b/gu;
for (const match of string.matchAll(regex)) {
console.log(match);
}

// Ausgabe:
//
// [ 'DEADBEEF', index: 19, input: 'Magische Hex-Zahlen: DEADBEEF CAFE' ]
// [ 'CAFE', index: 28, input: 'Magische Hex-Zahlen: DEADBEEF CAFE' ]

String#matchAll ist besonders nützlich für reguläre Ausdrücke mit Gruppenerfassung. Es gibt Ihnen vollständige Informationen für jede einzelne Übereinstimmung, einschließlich der erfassten Gruppen.

const string = 'Lieblings-GitHub-Repos: tc39/ecma262 v8/v8.dev';
const regex = /\b(?<owner>[a-z0-9]+)\/(?<repo>[a-z0-9\.]+)\b/g;
for (const match of string.matchAll(regex)) {
console.log(`${match[0]} bei ${match.index} mit '${match.input}'`);
console.log(`→ Besitzer: ${match.groups.owner}`);
console.log(`→ Repo: ${match.groups.repo}`);
}

Modul-Namespace-Exporte

· Eine Minute Lesezeit
Mathias Bynens ([@mathias](https://twitter.com/mathias))

In JavaScript-Modulen war es bereits möglich, die folgende Syntax zu verwenden:

import * as utils from './utils.mjs';

Es existierte jedoch keine symmetrische export-Syntax… bis jetzt:

export * as utils from './utils.mjs';

Dies ist gleichbedeutend mit dem Folgenden:

import * as utils from './utils.mjs';
export { utils };

Öffentliche und private Klassenfelder

· 4 Minuten Lesezeit
Mathias Bynens ([@mathias](https://twitter.com/mathias))

Mehrere Vorschläge erweitern die bestehende JavaScript-Klassensyntax um neue Funktionen. Dieser Artikel erklärt die neue Syntax von öffentlichen Klassenfeldern in V8 v7.2 und Chrome 72 sowie die kommende Syntax von privaten Klassenfeldern.

Hier ist ein Codebeispiel, das eine Instanz einer Klasse namens IncreasingCounter erstellt:

const counter = new IncreasingCounter();
counter.value;
// protokolliert 'Den aktuellen Wert abrufen!'
// → 0
counter.increment();
counter.value;
// protokolliert 'Den aktuellen Wert abrufen!'
// → 1

Beachten Sie, dass der Zugriff auf value einen Code ausführt (d. h., er protokolliert eine Nachricht), bevor das Ergebnis zurückgegeben wird. Überlegen Sie sich nun, wie Sie diese Klasse in JavaScript implementieren würden? 🤔

ES2015-Klassensyntax

So könnte IncreasingCounter unter Verwendung der ES2015-Klassensyntax implementiert werden:

class IncreasingCounter {
constructor() {
this._count = 0;
}
get value() {
console.log('Den aktuellen Wert abrufen!');
return this._count;
}
increment() {
this._count++;
}
}

Die Klasse installiert den value-Getter und eine increment-Methode auf dem Prototyp. Interessanterweise hat die Klasse einen Konstruktor, der eine Instanzeigenschaft _count erstellt und ihren Standardwert auf 0 setzt. Derzeit verwenden wir oft das Unterstrichpräfix, um anzuzeigen, dass _count nicht direkt von Konsumenten der Klasse verwendet werden sollte, aber das ist nur eine Konvention; es ist keine wirklich „private“ Eigenschaft mit speziellen Semantiken, die von der Sprache durchgesetzt werden.