Deep JavaScript
Bitte unterstützen Sie dieses Buch: kaufen Sie es oder spenden Sie
(Werbung, bitte nicht blockieren.)

7 Daten destruktiv und nicht-destruktiv aktualisieren



In diesem Kapitel lernen wir zwei verschiedene Arten kennen, Daten zu aktualisieren

Letzteres ähnelt dem erstmaligen Erstellen einer Kopie und anschließenden destruktiven Änderung, aber es geschieht beides gleichzeitig.

7.1 Beispiele: Ein Objekt destruktiv und nicht-destruktiv aktualisieren

Der folgende Code zeigt eine Funktion, die Objekt-Eigenschaften destruktiv aktualisiert und diese auf einem Objekt anwendet.

function setPropertyDestructively(obj, key, value) {
  obj[key] = value;
  return obj;
}

const obj = {city: 'Berlin', country: 'Germany'};
setPropertyDestructively(obj, 'city', 'Munich');
assert.deepEqual(obj, {city: 'Munich', country: 'Germany'});

Der folgende Code demonstriert die nicht-destruktive Aktualisierung eines Objekts

function setPropertyNonDestructively(obj, key, value) {
  const updatedObj = {};
  for (const [k, v] of Object.entries(obj)) {
    updatedObj[k] = (k === key ? value : v);
  }
  return updatedObj;
}

const obj = {city: 'Berlin', country: 'Germany'};
const updatedObj = setPropertyNonDestructively(obj, 'city', 'Munich');

// We have created an updated object:
assert.deepEqual(updatedObj, {city: 'Munich', country: 'Germany'});

// But we didn’t change the original:
assert.deepEqual(obj, {city: 'Berlin', country: 'Germany'});

Spread macht setPropertyNonDestructively() prägnanter

function setPropertyNonDestructively(obj, key, value) {
  return {...obj, [key]: value};
}

Beide Versionen von setPropertyNonDestructively() aktualisieren oberflächlich: Sie ändern nur die oberste Ebene eines Objekts.

7.2 Beispiele: Ein Array destruktiv und nicht-destruktiv aktualisieren

Der folgende Code zeigt eine Funktion, die Array-Elemente destruktiv aktualisiert und diese auf einem Array anwendet.

function setElementDestructively(arr, index, value) {
  arr[index] = value;
}

const arr = ['a', 'b', 'c', 'd', 'e'];
setElementDestructively(arr, 2, 'x');
assert.deepEqual(arr, ['a', 'b', 'x', 'd', 'e']);

Der folgende Code demonstriert die nicht-destruktive Aktualisierung eines Arrays

function setElementNonDestructively(arr, index, value) {
  const updatedArr = [];
  for (const [i, v] of arr.entries()) {
    updatedArr.push(i === index ? value : v);
  }
  return updatedArr;
}

const arr = ['a', 'b', 'c', 'd', 'e'];
const updatedArr = setElementNonDestructively(arr, 2, 'x');
assert.deepEqual(updatedArr, ['a', 'b', 'x', 'd', 'e']);
assert.deepEqual(arr, ['a', 'b', 'c', 'd', 'e']);

.slice() und Spread machen setElementNonDestructively() prägnanter

function setElementNonDestructively(arr, index, value) {
  return [
    ...arr.slice(0, index), value, ...arr.slice(index+1)];
}

Beide Versionen von setElementNonDestructively() aktualisieren oberflächlich: Sie ändern nur die oberste Ebene eines Arrays.

7.3 Manuelles Deep-Updating

Bisher haben wir Daten nur oberflächlich aktualisiert. Widmen wir uns nun dem Deep-Updating. Der folgende Code zeigt, wie man es manuell macht. Wir ändern Name und Arbeitgeber.

const original = {name: 'Jane', work: {employer: 'Acme'}};
const updatedOriginal = {
  ...original,
  name: 'John',
  work: {
    ...original.work,
    employer: 'Spectre'
  },
};

assert.deepEqual(
  original, {name: 'Jane', work: {employer: 'Acme'}});
assert.deepEqual(
  updatedOriginal, {name: 'John', work: {employer: 'Spectre'}});

7.4 Generisches Deep-Updating implementieren

Die folgende Funktion implementiert generisches Deep-Updating.

function deepUpdate(original, keys, value) {
  if (keys.length === 0) {
    return value;
  }
  const currentKey = keys[0];
  if (Array.isArray(original)) {
    return original.map(
      (v, index) => index === currentKey
        ? deepUpdate(v, keys.slice(1), value) // (A)
        : v); // (B)
  } else if (typeof original === 'object' && original !== null) {
    return Object.fromEntries(
      Object.entries(original).map(
        (keyValuePair) => {
          const [k,v] = keyValuePair;
          if (k === currentKey) {
            return [k, deepUpdate(v, keys.slice(1), value)]; // (C)
          } else {
            return keyValuePair; // (D)
          }
        }));
  } else {
    // Primitive value
    return original;
  }
}

Wenn wir value als Wurzel eines Baumes betrachten, den wir aktualisieren, dann verändert deepUpdate() nur einen einzelnen Zweig tiefgreifend (Zeile A und C). Alle anderen Zweige werden oberflächlich kopiert (Zeile B und D).

So sieht die Verwendung von deepUpdate() aus

const original = {name: 'Jane', work: {employer: 'Acme'}};

const copy = deepUpdate(original, ['work', 'employer'], 'Spectre');
assert.deepEqual(copy, {name: 'Jane', work: {employer: 'Spectre'}});
assert.deepEqual(original, {name: 'Jane', work: {employer: 'Acme'}});