Wie wir wissen, können Objekte Eigenschaften speichern.
Bisher war eine Eigenschaft für uns ein einfaches „Schlüssel-Wert“-Paar. Aber eine Objekteigenschaft ist tatsächlich eine flexiblere und leistungsfähigere Sache.
In diesem Kapitel untersuchen wir zusätzliche Konfigurationsoptionen und im nächsten sehen wir, wie wir sie unsichtbar in Getter/Setter-Funktionen umwandeln.
Objekteigenschaften verfügen neben einem value
über drei spezielle Attribute (sog. „Flags“):
writable
– wenn true
, kann der Wert geändert werden, andernfalls ist er schreibgeschützt.
enumerable
– wenn true
, dann in Schleifen aufgelistet, andernfalls nicht aufgelistet.
configurable
– wenn true
, kann die Eigenschaft gelöscht und diese Attribute geändert werden, andernfalls nicht.
Wir haben sie noch nicht gesehen, da sie in der Regel nicht auftauchen. Wenn wir eine Immobilie „auf die übliche Weise“ erstellen, sind alle davon true
. Wir können sie aber auch jederzeit ändern.
Sehen wir uns zunächst an, wie man diese Flags erhält.
Die Methode Object.getOwnPropertyDescriptor ermöglicht die Abfrage der vollständigen Informationen zu einer Eigenschaft.
Die Syntax lautet:
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
obj
Das Objekt, von dem Informationen abgerufen werden sollen.
propertyName
Der Name der Eigenschaft.
Der zurückgegebene Wert ist ein sogenanntes „Property Descriptor“-Objekt: Es enthält den Wert und alle Flags.
Zum Beispiel:
let user = { Name: „John“ }; let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); alarm( JSON.stringify(descriptor, null, 2 ) ); /* Eigenschaftsbeschreibung: { „value“: „John“, „beschreibbar“: wahr, „aufzählbar“: wahr, „konfigurierbar“: wahr } */
Um die Flags zu ändern, können wir Object.defineProperty verwenden.
Die Syntax lautet:
Object.defineProperty(obj, propertyName, descriptor)
obj
, propertyName
Das Objekt und seine Eigenschaft zum Anwenden des Deskriptors.
descriptor
Anzuwendendes Eigenschaftsdeskriptorobjekt.
Wenn die Eigenschaft vorhanden ist, aktualisiert defineProperty
ihre Flags. Andernfalls wird die Eigenschaft mit dem angegebenen Wert und den angegebenen Flags erstellt. Wenn in diesem Fall kein Flag angegeben wird, wird davon ausgegangen, dass es false
ist.
Hier wird beispielsweise ein name
mit allen Falsy-Flags erstellt:
let user = {}; Object.defineProperty(user, "name", { Wert: „John“ }); let descriptor = Object.getOwnPropertyDescriptor(user, 'name'); alarm( JSON.stringify(descriptor, null, 2 ) ); /* { „value“: „John“, „beschreibbar“: falsch, „aufzählbar“: falsch, „konfigurierbar“: falsch } */
Vergleichen Sie es mit „normal erstelltem“ user.name
oben: Jetzt sind alle Flags falsch. Wenn das nicht das ist, was wir wollen, sollten wir sie im descriptor
besser auf true
setzen.
Sehen wir uns nun die Auswirkungen der Flags anhand eines Beispiels an.
Machen wir user.name
nicht beschreibbar (kann nicht neu zugewiesen werden), indem wir writable
Flag ändern:
let user = { Name: „John“ }; Object.defineProperty(user, "name", { beschreibbar: falsch }); user.name = "Pete"; // Fehler: Zuweisung zur schreibgeschützten Eigenschaft „Name“ nicht möglich
Jetzt kann niemand den Namen unseres Benutzers ändern, es sei denn, er wendet seine eigene defineProperty
an, um unsere zu überschreiben.
Fehler treten nur im strikten Modus auf
Im nicht strikten Modus treten beim Schreiben in nicht beschreibbare Eigenschaften usw. keine Fehler auf. Aber die Operation wird immer noch nicht gelingen. Flag-verletzende Aktionen werden in nicht strikten Aktionen einfach stillschweigend ignoriert.
Hier ist das gleiche Beispiel, aber die Eigenschaft wurde von Grund auf neu erstellt:
let user = { }; Object.defineProperty(user, "name", { Wert: „John“, // für neue Eigenschaften müssen wir explizit auflisten, was wahr ist aufzählbar: wahr, konfigurierbar: wahr }); Alert(Benutzername); // John user.name = "Pete"; // Fehler
Fügen wir nun einen benutzerdefinierten toString
zu user
hinzu.
Normalerweise ist ein integrierter toString
für Objekte nicht aufzählbar und wird in for..in
nicht angezeigt. Wenn wir jedoch einen eigenen toString
hinzufügen, wird dieser standardmäßig in for..in
angezeigt, etwa so:
let user = { Name: „John“, toString() { return this.name; } }; // Standardmäßig werden unsere beiden Eigenschaften aufgelistet: for (Benutzer eingeben lassen) alarm(key); // Name, toString
Wenn es uns nicht gefällt, können wir enumerable:false
setzen. Dann erscheint es nicht in einer for..in
Schleife, genau wie die eingebaute:
let user = { Name: „John“, toString() { return this.name; } }; Object.defineProperty(user, "toString", { aufzählbar: falsch }); // Jetzt verschwindet unser toString: for (Benutzer eingeben lassen) alarm(key); // Name
Nicht aufzählbare Eigenschaften sind ebenfalls von Object.keys
ausgeschlossen:
alarm(Object.keys(user)); // Name
Das nicht konfigurierbare Flag ( configurable:false
) ist manchmal für integrierte Objekte und Eigenschaften voreingestellt.
Eine nicht konfigurierbare Eigenschaft kann nicht gelöscht werden, ihre Attribute können nicht geändert werden.
Beispielsweise ist Math.PI
nicht beschreibbar, nicht aufzählbar und nicht konfigurierbar:
let descriptor = Object.getOwnPropertyDescriptor(Math, 'PI'); alarm( JSON.stringify(descriptor, null, 2 ) ); /* { „Wert“: 3.141592653589793, „beschreibbar“: falsch, „aufzählbar“: falsch, „konfigurierbar“: falsch } */
Daher ist ein Programmierer nicht in der Lage, den Wert von Math.PI
zu ändern oder zu überschreiben.
Math.PI = 3; // Fehler, weil es beschreibbar ist: false // Math.PI löschen wird auch nicht funktionieren
Wir können Math.PI
auch nicht wieder writable
machen:
// Fehler, weil konfigurierbar: false Object.defineProperty(Math, "PI", { writable: true });
Mit Math.PI
können wir absolut nichts anfangen.
Eine Eigenschaft nicht konfigurierbar zu machen, ist eine Einbahnstraße. Wir können es nicht mit defineProperty
zurückändern.
Bitte beachten Sie: configurable: false
verhindert Änderungen an Eigenschaftsflags und deren Löschung, erlaubt aber gleichzeitig die Änderung ihres Werts.
Hier ist user.name
nicht konfigurierbar, aber wir können ihn trotzdem ändern (da er beschreibbar ist):
let user = { Name: „John“ }; Object.defineProperty(user, "name", { konfigurierbar: falsch }); user.name = "Pete"; // funktioniert gut Benutzername löschen; // Fehler
Und hier machen wir user.name
zu einer „für immer versiegelten“ Konstante, genau wie das integrierte Math.PI
:
let user = { Name: „John“ }; Object.defineProperty(user, "name", { beschreibbar: falsch, konfigurierbar: falsch }); // kann user.name oder seine Flags nicht ändern // das alles wird nicht funktionieren: user.name = "Pete"; Benutzername löschen; Object.defineProperty(user, "name", { value: "Pete" });
Die einzig mögliche Attributänderung: beschreibbar wahr → falsch
Beim Ändern von Flags gibt es eine kleine Ausnahme.
Wir können writable: true
für eine nicht konfigurierbare Eigenschaft in false
ändern und so ihre Wertänderung verhindern (um eine weitere Schutzebene hinzuzufügen). Allerdings nicht umgekehrt.
Es gibt eine Methode Object.defineProperties(obj, descriptors), mit der viele Eigenschaften gleichzeitig definiert werden können.
Die Syntax lautet:
Object.defineProperties(obj, { prop1: deskriptor1, prop2: deskriptor2 // ... });
Zum Beispiel:
Object.defineProperties(user, { Name: {Wert: „John“, beschreibbar: false}, Nachname: {Wert: „Smith“, beschreibbar: falsch}, // ... });
Wir können also viele Eigenschaften gleichzeitig festlegen.
Um alle Eigenschaftsdeskriptoren auf einmal abzurufen, können wir die Methode Object.getOwnPropertyDescriptors(obj) verwenden.
Zusammen mit Object.defineProperties
kann es als „Flags-bewusste“ Methode zum Klonen eines Objekts verwendet werden:
let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
Wenn wir ein Objekt klonen, verwenden wir normalerweise eine Zuweisung zum Kopieren von Eigenschaften, wie folgt:
for (Benutzer eingeben lassen) { Klon[Schlüssel] = Benutzer[Schlüssel] }
…Aber das kopiert keine Flags. Wenn wir also einen „besseren“ Klon wollen, ist Object.defineProperties
vorzuziehen.
Ein weiterer Unterschied besteht darin, dass for..in
symbolische und nicht aufzählbare Eigenschaften ignoriert, Object.getOwnPropertyDescriptors
jedoch alle Eigenschaftsdeskriptoren zurückgibt, einschließlich symbolischer und nicht aufzählbarer Eigenschaften.
Eigenschaftsbeschreibungen funktionieren auf der Ebene einzelner Eigenschaften.
Es gibt auch Methoden, die den Zugriff auf das gesamte Objekt beschränken:
Object.preventExtensions(obj)
Verbietet das Hinzufügen neuer Eigenschaften zum Objekt.
Object.seal(obj)
Verbietet das Hinzufügen/Entfernen von Eigenschaften. Legt configurable: false
für alle vorhandenen Eigenschaften fest.
Object.freeze(obj)
Verbietet das Hinzufügen/Entfernen/Ändern von Eigenschaften. Legt configurable: false, writable: false
für alle vorhandenen Eigenschaften fest.
Und auch für sie gibt es Tests:
Object.isExtensible(obj)
Gibt false
zurück, wenn das Hinzufügen von Eigenschaften verboten ist, andernfalls true
.
Object.isSealed(obj)
Gibt true
zurück, wenn das Hinzufügen/Entfernen von Eigenschaften verboten ist und alle vorhandenen Eigenschaften configurable: false
.
Object.isFrozen(obj)
Gibt true
zurück, wenn das Hinzufügen/Entfernen/Ändern von Eigenschaften verboten ist und alle aktuellen Eigenschaften configurable: false, writable: false
.
Diese Methoden werden in der Praxis selten angewendet.