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“.
JavaScript bietet drei Niveaus zum Schutz von Objekten
Object.preventExtensions(obj)Object.seal(obj)Object.freeze(obj)Diese Methode funktioniert wie folgt
obj kein Objekt ist, wird es zurückgegeben.obj so geändert, dass wir keine Eigenschaften mehr hinzufügen können, und es wird zurückgegeben.<T> drückt aus, dass das Ergebnis denselben Typ wie der Parameter hat.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), []);Prüft, ob obj erweiterbar ist – zum Beispiel
> const obj = {};
> Object.isExtensible(obj)
true
> Object.preventExtensions(obj)
{}
> Object.isExtensible(obj)
falseBeschreibung dieser Methode
obj kein Objekt ist, wird es zurückgegeben.obj, macht alle seine Eigenschaften nicht konfigurierbar und gibt es zurück. Dass die Eigenschaften nicht konfigurierbar sind, bedeutet, dass sie nicht mehr geändert werden können (außer ihren Werten): Schreibgeschützte Eigenschaften bleiben schreibgeschützt, aufzählbare Eigenschaften bleiben aufzählbar usw.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
Aber wir können ihre Attribute nicht ändern
assert.throws(
() => Object.defineProperty(obj, 'first', { enumerable: false }),
/^TypeError: Cannot redefine property: first$/);Prüft, ob obj versiegelt ist – zum Beispiel
obj sofort zurück, wenn es kein Objekt ist.obj und gibt es zurück. Das heißt, obj ist nicht erweiterbar, alle Eigenschaften sind schreibgeschützt und es gibt keine Möglichkeit, dies zu ändern.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$/);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)
trueObject.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'],
});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$/);