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

10 Objekte vor Änderungen schützen



In diesem Kapitel sehen wir uns an, wie Objekte vor Änderungen geschützt werden können. Beispiele hierfür sind: Verhindern des Hinzufügens von Eigenschaften und Verhindern des Änderns von Eigenschaften.

  Erforderliches Wissen: Eigenschaftsattribute

Für dieses Kapitel sollten Sie mit Eigenschaftsattributen vertraut sein. Wenn nicht, lesen Sie bitte §9 „Eigenschaftsattribute: eine Einführung“.

10.1 Schutzniveaus: Verhindern von Erweiterungen, Versiegeln, Einfrieren

JavaScript bietet drei Niveaus zum Schutz von Objekten

10.2 Erweiterungen von Objekten verhindern

Object.preventExtensions<T>(obj: T): T

Diese Methode funktioniert wie folgt

Verwenden wir Object.preventExtensions() in einem Beispiel

const obj = { first: 'Jane' };
Object.preventExtensions(obj);
assert.throws(
  () => obj.last = 'Doe',
  /^TypeError: Cannot add property last, object is not extensible$/);

Wir können aber immer noch Eigenschaften löschen

assert.deepEquals(
  Object.keys(obj), ['first']);
delete obj.first;
assert.deepEquals(
  Object.keys(obj), []);

10.2.1 Prüfen, ob ein Objekt erweiterbar ist

Object.isExtensible(obj: any): boolean

Prüft, ob obj erweiterbar ist – zum Beispiel

> const obj = {};
> Object.isExtensible(obj)
true
> Object.preventExtensions(obj)
{}
> Object.isExtensible(obj)
false

10.3 Objekte versiegeln

Object.seal<T>(obj: T): T

Beschreibung dieser Methode

Das folgende Beispiel zeigt, dass das Versiegeln das Objekt nicht erweiterbar macht und seine Eigenschaften nicht konfigurierbar.

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

// Before sealing
assert.equal(Object.isExtensible(obj), true);
assert.deepEqual(
  Object.getOwnPropertyDescriptors(obj),
  {
    first: {
      value: 'Jane',
      writable: true,
      enumerable: true,
      configurable: true
    },
    last: {
      value: 'Doe',
      writable: true,
      enumerable: true,
      configurable: true
    }
  });

Object.seal(obj);

// After sealing
assert.equal(Object.isExtensible(obj), false);
assert.deepEqual(
  Object.getOwnPropertyDescriptors(obj),
  {
    first: {
      value: 'Jane',
      writable: true,
      enumerable: true,
      configurable: false
    },
    last: {
      value: 'Doe',
      writable: true,
      enumerable: true,
      configurable: false
    }
  });

Wir können den Wert der Eigenschaft .first immer noch ändern

obj.first = 'John';
assert.deepEqual(
  obj, {first: 'John', last: 'Doe'});

Aber wir können ihre Attribute nicht ändern

assert.throws(
  () => Object.defineProperty(obj, 'first', { enumerable: false }),
  /^TypeError: Cannot redefine property: first$/);

10.3.1 Prüfen, ob ein Objekt versiegelt ist

Object.isSealed(obj: any): boolean

Prüft, ob obj versiegelt ist – zum Beispiel

> const obj = {};
> Object.isSealed(obj)
false
> Object.seal(obj)
{}
> Object.isSealed(obj)
true

10.4 Objekte einfrieren

Object.freeze<T>(obj: T): T;
const point = { x: 17, y: -5 };
Object.freeze(point);

assert.throws(
  () => point.x = 2,
  /^TypeError: Cannot assign to read only property 'x'/);

assert.throws(
  () => Object.defineProperty(point, 'x', {enumerable: false}),
  /^TypeError: Cannot redefine property: x$/);

assert.throws(
  () => point.z = 4,
  /^TypeError: Cannot add property z, object is not extensible$/);

10.4.1 Prüfen, ob ein Objekt eingefroren ist

Object.isFrozen(obj: any): boolean

Prüft, ob obj eingefroren ist – zum Beispiel

> const point = { x: 17, y: -5 };
> Object.isFrozen(point)
false
> Object.freeze(point)
{ x: 17, y: -5 }
> Object.isFrozen(point)
true

10.4.2 Einfrieren ist flach (shallow)

Object.freeze(obj) friert nur obj und seine Eigenschaften ein. Es friert nicht die Werte dieser Eigenschaften ein – zum Beispiel

const teacher = {
  name: 'Edna Krabappel',
  students: ['Bart'],
};
Object.freeze(teacher);

// We can’t change own properties:
assert.throws(
  () => teacher.name = 'Elizabeth Hoover',
  /^TypeError: Cannot assign to read only property 'name'/);

// Alas, we can still change values of own properties:
teacher.students.push('Lisa');
assert.deepEqual(
  teacher, {
    name: 'Edna Krabappel',
    students: ['Bart', 'Lisa'],
  });

10.4.3 Tiefes Einfrieren implementieren

Wenn wir ein tiefes Einfrieren wünschen, müssen wir es selbst implementieren

function deepFreeze(value) {
  if (Array.isArray(value)) {
    for (const element of value) {
      deepFreeze(element);
    }
    Object.freeze(value);
  } else if (typeof value === 'object' && value !== null) {
    for (const v of Object.values(value)) {
      deepFreeze(v);
    }
    Object.freeze(value);
  } else {
    // Nothing to do: primitive values are already immutable
  } 
  return value;
}

Wenn wir das Beispiel aus dem vorherigen Abschnitt erneut betrachten, können wir prüfen, ob deepFreeze() wirklich tief einfriert

const teacher = {
  name: 'Edna Krabappel',
  students: ['Bart'],
};
deepFreeze(teacher);

assert.throws(
  () => teacher.students.push('Lisa'),
  /^TypeError: Cannot add property 1, object is not extensible$/);

10.5 Weiterführende Lektüre