JavaScript für ungeduldige Programmierer (ES2022-Ausgabe)
Bitte unterstützen Sie dieses Buch: kaufen Sie es oder spenden Sie
(Werbung, bitte nicht blockieren.)

37 Destrukturierung



37.1 Ein erster Einblick in die Destrukturierung

Bei normaler Zuweisung extrahieren Sie jeweils ein Datenelement – zum Beispiel

const arr = ['a', 'b', 'c'];
const x = arr[0]; // extract
const y = arr[1]; // extract

Mit Destrukturierung können Sie mehrere Datenelemente gleichzeitig über Muster an Stellen extrahieren, an denen Daten empfangen werden. Die linke Seite von = im vorherigen Code ist eine solche Stelle. Im folgenden Code sind die eckigen Klammern in Zeile A ein Destrukturierungsmuster.

const arr = ['a', 'b', 'c'];
const [x, y] = arr; // (A)
assert.equal(x, 'a');
assert.equal(y, 'b');

Dieser Code macht dasselbe wie der vorherige Code.

Beachten Sie, dass das Muster „kleiner“ als die Daten ist: Wir extrahieren nur das, was wir brauchen.

37.2 Konstruktion vs. Extraktion

Um zu verstehen, was Destrukturierung ist, betrachten Sie, dass JavaScript zwei gegensätzliche Arten von Operationen hat

Das Konstruieren von Daten sieht folgendermaßen aus

// Constructing: one property at a time
const jane1 = {};
jane1.first = 'Jane';
jane1.last = 'Doe';

// Constructing: multiple properties
const jane2 = {
  first: 'Jane',
  last: 'Doe',
};

assert.deepEqual(jane1, jane2);

Das Extrahieren von Daten sieht folgendermaßen aus

const jane = {
  first: 'Jane',
  last: 'Doe',
};

// Extracting: one property at a time
const f1 = jane.first;
const l1 = jane.last;
assert.equal(f1, 'Jane');
assert.equal(l1, 'Doe');

// Extracting: multiple properties (NEW!)
const {first: f2, last: l2} = jane; // (A)
assert.equal(f2, 'Jane');
assert.equal(l2, 'Doe');

Die Operation in Zeile A ist neu: Wir deklarieren zwei Variablen f2 und l2 und initialisieren sie durch *Destrukturierung* (Mehrwert-Extraktion).

Der folgende Teil von Zeile A ist ein *Destrukturierungsmuster*

{first: f2, last: l2}

Destrukturierungsmuster sind syntaktisch ähnlich zu den Literalen, die für die Mehrwert-Konstruktion verwendet werden. Aber sie erscheinen dort, wo Daten empfangen werden (z. B. auf der linken Seite von Zuweisungen), nicht dort, wo Daten erstellt werden (z. B. auf der rechten Seite von Zuweisungen).

37.3 Wo kann man destrukturieren?

Destrukturierungsmuster können an „Datensenken-Positionen“ verwendet werden, wie zum Beispiel

Beachten Sie, dass Variablendeklarationen const und let-Deklarationen in for-of-Schleifen beinhalten

const arr = ['a', 'b'];
for (const [index, element] of arr.entries()) {
    console.log(index, element);
}
// Output:
// 0, 'a'
// 1, 'b'

In den nächsten beiden Abschnitten werden wir uns die beiden Arten der Destrukturierung genauer ansehen: Objekt-Destrukturierung und Array-Destrukturierung.

37.4 Objekt-Destrukturierung

*Objekt-Destrukturierung* ermöglicht es Ihnen, Werte von Eigenschaften stapelweise über Muster zu extrahieren, die Objekt-Literalen ähneln

const address = {
  street: 'Evergreen Terrace',
  number: '742',
  city: 'Springfield',
  state: 'NT',
  zip: '49007',
};

const { street: s, city: c } = address;
assert.equal(s, 'Evergreen Terrace');
assert.equal(c, 'Springfield');

Sie können sich das Muster als transparente Folie vorstellen, die Sie über die Daten legen: Der Muster-Schlüssel 'street' hat eine Übereinstimmung in den Daten. Daher wird der Datenwert 'Evergreen Terrace' der Muster-Variable s zugewiesen.

Sie können auch primitive Werte mit Objekt-Destrukturierung destrukturieren

const {length: len} = 'abc';
assert.equal(len, 3);

Und Sie können Arrays mit Objekt-Destrukturierung destrukturieren

const {0:x, 2:y} = ['a', 'b', 'c'];
assert.equal(x, 'a');
assert.equal(y, 'c');

Warum funktioniert das? Array-Indizes sind auch Eigenschaften.

37.4.1 Property-Value-Shorthands

Objekt-Literale unterstützen Property-Value-Shorthands, und Objektmuster ebenso

const { street, city } = address;
assert.equal(street, 'Evergreen Terrace');
assert.equal(city, 'Springfield');

  Übung: Objekt-Destrukturierung

exercises/destructuring/object_destructuring_exrc.mjs

37.4.2 Rest-Properties

In Objekt-Literalen können Sie Spread-Properties haben. In Objektmustern können Sie Rest-Properties haben (diese müssen am Ende stehen)

const obj = { a: 1, b: 2, c: 3 };
const { a: propValue, ...remaining } = obj; // (A)

assert.equal(propValue, 1);
assert.deepEqual(remaining, {b:2, c:3});

Einer Rest-Property-Variable, wie z. B. remaining (Zeile A), wird ein Objekt mit allen Dateneigenschaften zugewiesen, deren Schlüssel nicht im Muster erwähnt werden.

remaining kann auch als Ergebnis des nicht-destruktiven Entfernens der Eigenschaft a aus obj betrachtet werden.

37.4.3 Syntax-Fallstrick: Zuweisung durch Objekt-Destrukturierung

Wenn wir eine Zuweisung mit Objekt-Destrukturierung durchführen, stoßen wir auf einen Fallstrick, der durch syntaktische Mehrdeutigkeit verursacht wird – Sie können eine Anweisung nicht mit einer geschweiften Klammer beginnen, da JavaScript dann denkt, Sie beginnen einen Block

let prop;
assert.throws(
  () => eval("{prop} = { prop: 'hello' };"),
  {
    name: 'SyntaxError',
    message: "Unexpected token '='",
  });

  Warum eval()?

eval() verzögert das Parsen (und damit den SyntaxError) bis zum Rückruf von assert.throws() ausgeführt wird. Hätten wir es nicht verwendet, hätten wir bereits einen Fehler erhalten, als dieser Code geparst wurde, und assert.throws() wäre gar nicht erst ausgeführt worden.

Die Abhilfe besteht darin, die gesamte Zuweisung in Klammern zu setzen

let prop;
({prop} = { prop: 'hello' });
assert.equal(prop, 'hello');

37.5 Array-Destrukturierung

*Array-Destrukturierung* ermöglicht es Ihnen, Werte von Array-Elementen stapelweise über Muster zu extrahieren, die Array-Literalen ähneln

const [x, y] = ['a', 'b'];
assert.equal(x, 'a');
assert.equal(y, 'b');

Sie können Elemente überspringen, indem Sie Lücken innerhalb von Array-Mustern erwähnen

const [, x, y] = ['a', 'b', 'c']; // (A)
assert.equal(x, 'b');
assert.equal(y, 'c');

Das erste Element des Array-Musters in Zeile A ist eine Lücke, weshalb das Array-Element mit Index 0 ignoriert wird.

37.5.1 Array-Destrukturierung funktioniert mit jedem Iterable

Array-Destrukturierung kann auf jeden Wert angewendet werden, der iterierbar ist, nicht nur auf Arrays

// Sets are iterable
const mySet = new Set().add('a').add('b').add('c');
const [first, second] = mySet;
assert.equal(first, 'a');
assert.equal(second, 'b');

// Strings are iterable
const [a, b] = 'xyz';
assert.equal(a, 'x');
assert.equal(b, 'y');

37.5.2 Rest-Elemente

In Array-Literalen können Sie Spread-Elemente haben. In Array-Mustern können Sie Rest-Elemente haben (diese müssen am Ende stehen)

const [x, y, ...remaining] = ['a', 'b', 'c', 'd']; // (A)

assert.equal(x, 'a');
assert.equal(y, 'b');
assert.deepEqual(remaining, ['c', 'd']);

Einer Rest-Element-Variable, wie z. B. remaining (Zeile A), wird ein Array mit allen Elementen des destrukturierten Werts zugewiesen, die noch nicht erwähnt wurden.

37.6 Beispiele für Destrukturierung

37.6.1 Array-Destrukturierung: Variablenwerte tauschen

Sie können Array-Destrukturierung verwenden, um die Werte zweier Variablen zu tauschen, ohne eine temporäre Variable zu benötigen

let x = 'a';
let y = 'b';

[x,y] = [y,x]; // swap

assert.equal(x, 'b');
assert.equal(y, 'a');

37.6.2 Array-Destrukturierung: Operationen, die Arrays zurückgeben

Array-Destrukturierung ist nützlich, wenn Operationen Arrays zurückgeben, wie zum Beispiel die Methode .exec() regulärer Ausdrücke

// Skip the element at index 0 (the whole match):
const [, year, month, day] =
  /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/
  .exec('2999-12-31');

assert.equal(year, '2999');
assert.equal(month, '12');
assert.equal(day, '31');

37.6.3 Objekt-Destrukturierung: Mehrere Rückgabewerte

Destrukturierung ist sehr nützlich, wenn eine Funktion mehrere Werte zurückgibt – entweder als Array oder als Objekt verpackt.

Betrachten Sie eine Funktion findElement(), die Elemente in einem Array findet

findElement(array, (value, index) => «boolean expression»)

Ihr zweiter Parameter ist eine Funktion, die den Wert und Index eines Elements empfängt und einen booleschen Wert zurückgibt, der angibt, ob dies das gesuchte Element ist.

Wir stehen nun vor einem Dilemma: Sollte findElement() den Wert des gefundenen Elements oder den Index zurückgeben? Eine Lösung wäre, zwei separate Funktionen zu erstellen, aber das würde zu Duplizierung von Code führen, da beide Funktionen sehr ähnlich wären.

Die folgende Implementierung vermeidet Duplizierung, indem sie ein Objekt zurückgibt, das sowohl den Index als auch den Wert des gefundenen Elements enthält

function findElement(arr, predicate) {
  for (let index=0; index < arr.length; index++) {
    const value = arr[index];
    if (predicate(value)) {
      // We found something:
      return { value, index };
    }
  }
  // We didn’t find anything:
  return { value: undefined, index: -1 };
}

Destrukturierung hilft uns bei der Verarbeitung des Ergebnisses von findElement()

const arr = [7, 8, 6];

const {value, index} = findElement(arr, x => x % 2 === 0);
assert.equal(value, 8);
assert.equal(index, 1);

Da wir mit Eigenschaftsschlüsseln arbeiten, spielt die Reihenfolge, in der wir value und index erwähnen, keine Rolle

const {index, value} = findElement(arr, x => x % 2 === 0);

Der Clou ist, dass Destrukturierung uns auch dann gute Dienste leistet, wenn wir nur an einem der beiden Ergebnisse interessiert sind

const arr = [7, 8, 6];

const {value} = findElement(arr, x => x % 2 === 0);
assert.equal(value, 8);

const {index} = findElement(arr, x => x % 2 === 0);
assert.equal(index, 1);

All diese Annehmlichkeiten zusammen machen diese Art der Handhabung mehrerer Rückgabewerte recht vielseitig.

37.7 Was passiert, wenn ein Musterteil nicht übereinstimmt?

Was passiert, wenn für einen Teil eines Musters keine Übereinstimmung gefunden wird? Dasselbe, was passiert, wenn Sie Nicht-Batch-Operatoren verwenden: Sie erhalten undefined.

37.7.1 Objekt-Destrukturierung und fehlende Eigenschaften

Wenn eine Eigenschaft in einem Objektmuster auf der rechten Seite keine Übereinstimmung hat, erhalten Sie undefined

const {prop: p} = {};
assert.equal(p, undefined);

37.7.2 Array-Destrukturierung und fehlende Elemente

Wenn ein Element in einem Array-Muster auf der rechten Seite keine Übereinstimmung hat, erhalten Sie undefined

const [x] = [];
assert.equal(x, undefined);

37.8 Welche Werte können nicht destrukturiert werden?

37.8.1 Sie können undefined und null nicht mit Objekt-Destrukturierung

Objekt-Destrukturierung schlägt nur fehl, wenn der zu destrukturierende Wert entweder undefined oder null ist. Das heißt, sie schlägt fehl, wann immer der Zugriff auf eine Eigenschaft über den Punktoperator ebenfalls fehlschlagen würde.

> const {prop} = undefined
TypeError: Cannot destructure property 'prop' of 'undefined'
as it is undefined.

> const {prop} = null
TypeError: Cannot destructure property 'prop' of 'null'
as it is null.

37.8.2 Sie können nicht-iterierbare Werte nicht mit Array-Destrukturierung

Array-Destrukturierung setzt voraus, dass der zu destrukturierende Wert iterierbar ist. Daher können Sie undefined und null nicht mit Array-Destrukturierung destrukturieren. Aber Sie können auch nicht-iterierbare Objekte nicht mit Array-Destrukturierung destrukturieren

> const [x] = {}
TypeError: {} is not iterable

  Quiz: Grundlagen

Siehe Quiz-App.

37.9 (Fortgeschritten)

Alle verbleibenden Abschnitte sind fortgeschritten.

37.10 Standardwerte

Normalerweise, wenn ein Muster keine Übereinstimmung hat, wird die entsprechende Variable auf undefined gesetzt

const {prop: p} = {};
assert.equal(p, undefined);

Wenn Sie einen anderen Wert verwenden möchten, müssen Sie einen *Standardwert* (über =) angeben

const {prop: p = 123} = {}; // (A)
assert.equal(p, 123);

In Zeile A geben wir den Standardwert für p als 123 an. Dieser Standardwert wird verwendet, da die Daten, die wir destrukturieren, keine Eigenschaft namens prop haben.

37.10.1 Standardwerte in der Array-Destrukturierung

Hier haben wir zwei Standardwerte, die den Variablen x und y zugewiesen werden, da die entsprechenden Elemente im destrukturierten Array nicht vorhanden sind.

const [x=1, y=2] = [];

assert.equal(x, 1);
assert.equal(y, 2);

Der Standardwert für das erste Element des Array-Musters ist 1; der Standardwert für das zweite Element ist 2.

37.10.2 Standardwerte in der Objekt-Destrukturierung

Sie können auch Standardwerte für die Objekt-Destrukturierung angeben

const {first: f='', last: l=''} = {};
assert.equal(f, '');
assert.equal(l, '');

Weder der Eigenschaftsschlüssel first noch der Eigenschaftsschlüssel last existieren im destrukturierten Objekt. Daher werden die Standardwerte verwendet.

Mit Property-Value-Shorthands wird dieser Code einfacher

const {first='', last=''} = {};
assert.equal(first, '');
assert.equal(last, '');

37.11 Parameterdefinitionen ähneln der Destrukturierung

Betrachtet man, was wir in diesem Kapitel gelernt haben, so haben Parameterdefinitionen viel mit einem Array-Muster gemeinsam (Rest-Elemente, Standardwerte usw.). Tatsächlich sind die folgenden beiden Funktionsdeklarationen äquivalent

function f1(«pattern1», «pattern2») {
  // ···
}

function f2(...args) {
  const [«pattern1», «pattern2»] = args;
  // ···
}

37.12 Verschachtelte Destrukturierung

Bis jetzt haben wir nur Variablen als *Zuweisungsziele* (Datensenken) innerhalb von Destrukturierungsmustern verwendet. Aber Sie können auch Muster als Zuweisungsziele verwenden, was es Ihnen ermöglicht, Muster bis zu beliebiger Tiefe zu verschachteln

const arr = [
  { first: 'Jane', last: 'Bond' },
  { first: 'Lars', last: 'Croft' },
];
const [, {first}] = arr; // (A)
assert.equal(first, 'Lars');

Innerhalb des Array-Musters in Zeile A befindet sich ein verschachteltes Objektmuster an Index 1.

Verschachtelte Muster können schwer verständlich werden, daher sollten sie mit Bedacht eingesetzt werden.

  Quiz: Fortgeschritten

Siehe Quiz-App.