Tiefe Kopien von Objekten erstellen

Node.js-Modul: immutability-helper
Keine Kommentare

Immutability ist seit einiger Zeit in aller Munde und das aus gutem Grund. Der Einzug funktionaler Programmierparadigmen in JavaScript hat immutable Datenstrukturen noch mehr an Relevanz gewinnen lassen.

In der funktionalen Programmierung wird das Modifizieren von Datenstrukturen als unerwünschter Seiteneffekt angesehen, der zu vermeiden ist. Aber nicht nur wegen der funktionalen Programmierung sollten Sie sich mit Immutability in JavaScript auseinandersetzen. Auch viele moderne Frameworks verlassen sich bei der Erkennung von Änderungen auf immutable Datenstrukturen. Der Grund hierfür ist einfach erklärt: Nehmen Sie an, Sie haben eine verschachtelte Objektstruktur und führen eine Änderung an einer Eigenschaft eines untergeordneten Objekts durch. Um diese Änderung erkennen zu können, müssten Sie die gesamte Objektstruktur auf Änderungen überprüfen. Mit Immutability erzeugen Sie eine Kopie des ursprünglichen Objekts und führen auf dieser die Änderung durch. Für den Algorithmus bedeutet das, dass sich die Referenz auf das Objekt geändert hat, also eine Änderung vorliegen muss.

Erzeugung von Kopien und Objekten

Sie müssen sich jedoch nicht mit den Implementierungsdetails moderner Frameworks auseinandersetzen, um die Vorteile von Immutability zu erkennen. Gehen Sie von einer Funktion aus, der ein Objekt übergeben wird, auf dem die Funktion eine Operation vornehmen soll. Nun haben Sie zwei Möglichkeiten: Da Objekte und Arrays in JavaScript per Referenz übergeben werden, können Sie das Objekt direkt manipulieren oder Sie erzeugen eine Kopie des Objekts, verändern diese und geben das veränderte Objekt zurück. Bei der ersten Option haben Sie den klassischen Fall eines unerwünschten Seiteneffekts. Je nachdem, wie viele Teile Ihrer Applikation mit diesem Objekt arbeiten, können Sie schwerwiegende Effekte hervorrufen, die die gesamte Applikation betreffen.

Als konkretes Beispiel verwenden wir eine Funktion (Listing 1), die ein Benutzerobjekt erhält und den Nachnamen des Benutzers in Großbuchstaben ändert. Die initiale Version der uppercaseLastname-Funktion arbeitet auf der Referenz des Benutzerobjekts.

const user = {
  firstname: 'Klaus',
  lastname: 'Müller'
};

function uppercaseLastname(user) {
  user.lastname = user.lastname.toUpperCase();
}

uppercaseLastname(user);

console.log(user.lastname);

Zur Erzeugung von Kopien von Objekten stehen Ihnen in JavaScript eine Reihe von Optionen zur Verfügung. Eine der gebräuchlichsten Varianten ist der Einsatz des Spread-Operators, mit dem Sie ein neues Objekt erzeugen können (Listing 2).

const user = {
  firstname: 'Klaus',
  lastname: 'Müller'
};

function uppercaseLastname(user) {
  return {...user, lastname: user.lastname.toUpperCase()};
}

const ucUser = uppercaseLastname(user);

console.log(ucUser.lastname);

Alternativ dazu können Sie auch Object.assign nutzen. Beide Varianten haben jedoch den Nachteil, dass sie bei Eigenschaften, die auf ein Objekt oder ein Array verweisen, nur die Referenz kopieren, nicht den eigentlichen Wert. Eine zugegebenermaßen etwas eigenwillige Lösung für dieses Problem ist eine Kombination aus JSON.stringify und JSON.parse. Für einen sauberen Umgang mit solchen tiefen Kopien von Objekten sollten Sie auf eine spezialisierte Bibliothek zurückgreifen. Eine der am weitesten verbreiteten ist Immutable.js. Eine leichtgewichtige Alternative zu den etablierten Bibliotheken stellt der immutability-helper dar. Diese Bibliothek können Sie sowohl im Frontend im Browser als auch serverseitig in Node.js einsetzen.

immutability-helper – die ersten Schritte

Wie nahezu alle JavaScript Libraries lässt sich auch der immutability-helper über die gängigen Paketmanager installieren. Mit einem npm install immutability-helper oder yarn add immutability-helper installieren Sie die Bibliothek in Ihrer Applikation.

International PHP Conference

Migrating to PHP 7

by Stefan Priebsch (thePHP.cc)

A practical introduction to Kubernetes

by Robert Lemke (Flownative GmbH)

Sie können den immutability-helper sowohl über das JavaScript-Modulsystem als auch über CommonJS einbinden. Das Beispiel mit der uppercaseLastname-Funktion sieht mit dem immutability-helper wie im Listing 3 zu sehen aus.

import update from 'immutability-helper';

const user = {
  firstname: 'Klaus',
  lastname: 'Müller'
};

function uppercaseLastname(user) {
  return update(user, {lastname: {$set: user.lastname.toUpperCase()}});
}

const ucUser = uppercaseLastname(user);

console.log(ucUser.lastname);

Bei dieser Lösung sparen Sie sich zwar keinen Quellcode, jedoch können Sie sicher sein, dass das ursprüngliche Objekt komplett geklont wird, auch über mehrere Objektebenen hinweg. Der Aufruf der update-Funktion ist etwas erklärungsbedürftig: Als erstes Argument übergeben Sie das Objekt, das Sie modifizieren möchten. Mit dem zweiten Argument beschreiben Sie die Veränderung. In unserem Fall ist die Eigenschaft lastname betroffen und wir möchten den Wert überschreiben und neu setzen. In diesem Fall kommt die $set-Eigenschaft zum Einsatz. Der Wert ist der neue Wert der Eigenschaft. Der Rückgabewert der update-Funktion ist schließlich das geklonte Objekt.

Die $set-Eigenschaft heißt beim immutability-helper Kommando und ist nicht die einzige verfügbare Operation. Die folgende Liste beschreibt alle verfügbaren Kommandos mit einer kurzen Erklärung:

  • $push: fügt neue Elemente am Ende eines Arrays hinzu
  • $unshift: fügt neue Elemente am Anfang eines Arrays hinzu
  • $splice: ersetzt Elemente eines Arrays
  • $set: überschreibt eine bestimmte Eigenschaft
  • $toggle: ändert einen boolschen Wert
  • $unset: entfernt Schlüssel aus einem Array
  • $merge: führt zwei Datenstrukturen zusammen
  • $apply: wendet eine Funktion an
  • $add: fügt einen Wert zu einer Map oder einem Set hinzu
  • $remove: entfernt einen Wert aus einer Map oder einem Set

Tiefere Objekthierarchien

Wie schon erwähnt, spielt der immutability-helper seine wahre Stärke erst bei verschachtelten Objektstrukturen aus. Nehmen Sie eine Funktion move an, der Sie ein Benutzerobjekt und den Namen einer Stadt übergeben. Die Funktion sorgt dafür, dass die city-Eigenschaft im address-Unterobjekt des Benutzers angepasst wird. Beim Zugriff auf tiefere Objekthierarchien, schachteln Sie die Zugriffe einfach ineinander. Zuerst definieren Sie die Eigenschaft des äußeren Objekts, dann die des inneren und schließlich wenden Sie das gewünschte Kommando an. Bei der Verwendung des immutability-helpers sind Sie nicht auf ein einzelnes Kommando pro Aufruf beschränkt, so können Sie beispielsweise mehrere $set-Kommandos zum Setzen unterschiedlicher Eigenschaften mit dem $toggle– oder $push-Kommando kombinieren.

Im Codebeispiel (Listing 4) wird neben der city-Eigenschaft außerdem die Eigenschaft moved des Adress-Objekts mit dem $toggle-Kommando auf den Wert true geändert.

import update from 'immutability-helper';

const user = {
  firstname: 'Klaus',
  address: {
    city: 'München',
    moved: false
  }
};

function move(user, city) {
  return update(user, {address: {city: {$set: city}, $toggle: ['moved']}});
}

console.log(move(user, 'Berlin'));

Wie Sie im Beispiel sehen, unterscheiden sich die verschiedenen Kommandos etwas voneinander. So nutzen Sie das $set-Kommando innerhalb eines Objekts. Beim $toggle-Kommando übergeben Sie ein Array von Eigenschaftsnamen, deren Werte verändert werden sollen. Für die genaue Syntax der Kommandos empfehle ich Ihnen einen Blick in die Dokumentation des immutability-helpers unter https://github.com/kolodny/immutability-helper.

Arbeiten mit Arrays

Die Bibliothek arbeitet nicht nur mit Objektstrukturen, sondern kann auch mit Arrays problemlos umgehen. Im zu sehenden Beispiel (Listing 5) setzen Sie den Wert des zweiten Elements eines Arrays auf einen neuen Wert.

import update from 'immutability-helper';

const arr = ['Peter', 'Paul', 'Mary'];

const clone = update(arr, {1: {$set: 'Klaus'}});

console.log(clone);

Fazit

Der immutability-helper ist eine leichtgewichtige und hoch spezialisierte Bibliothek. Mit ihm können Sie tiefe Kopien von Objekten erstellen und eine Reihe von Operationen auf diesen Objekten durchführen. Gerade im Kontext von React-Applikationen erfreut sich diese Lösung stetig wachsender Beliebtheit. Aber auch abseits von React, beispielsweise in Node.js, können Sie den immutability-helper in Ihrer Applikation einsetzen.

PHP Magazin

Entwickler MagazinDieser Artikel ist im PHP Magazin erschienen. Das PHP Magazin deckt ein breites Spektrum an Themen ab, die für die erfolgreiche Webentwicklung unerlässlich sind.

Natürlich können Sie das PHP Magazin über den entwickler.kiosk auch digital im Browser oder auf Ihren Android- und iOS-Devices lesen. In unserem Shop ist das Entwickler Magazin ferner im Abonnement oder als Einzelheft erhältlich.

Unsere Redaktion empfiehlt:

Relevante Beiträge

Hinterlasse einen Kommentar

Hinterlasse den ersten Kommentar!

avatar
400
  Subscribe  
Benachrichtige mich zu:
X
- Gib Deinen Standort ein -
- or -