JavaScript Property Descriptors
Erfahren Sie, wie JavaScript Property Flags (writable, enumerable, configurable) und Deskriptoren funktionieren, einschließlich defineProperty, freeze, seal und Strict-Mode-Verhalten.
JavaScript Property Flags und Deskriptoren bieten präzise Kontrolle über object-Eigenschaften und ermöglichen eine robuste und sichere Anwendungsentwicklung. Dieser Artikel beleuchtet diese Features im Detail und liefert praktische Einblicke sowie Code-Beispiele, die Ihnen helfen, das Verhalten von Eigenschaften effektiv zu steuern.
JavaScript Property Attributes verstehen
JavaScript objects sind Sammlungen von Eigenschaften, und jede Eigenschaft besitzt zugehörige Attribute, die ihr Verhalten definieren. Diese Attribute, häufig als Property Flags bezeichnet, umfassen:
- Writable: Legt fest, ob der Wert der Eigenschaft geändert werden kann.
- Enumerable: Steuert, ob die Eigenschaft bei der Aufzählung sichtbar ist, beispielsweise in einer
for...in-Schleife. - Configurable: Gibt an, ob die Eigenschaft gelöscht oder geändert werden kann.
Diese Flags sind entscheidend, um den Zugriff auf object-Eigenschaften zu kontrollieren, die Datenintegrität sicherzustellen und Kapselung in JavaScript-Anwendungen umzusetzen.
Property Descriptors im Detail
Property Descriptors liefern detaillierte Informationen über die Eigenschaft eines object und kapseln deren Wert und Flags. Sie werden mit Object.getOwnPropertyDescriptor(obj, propName) abgerufen und mit Object.defineProperty(obj, propName, descriptor) gesetzt. Ein Property Descriptor-object kann folgende Felder enthalten:
value: Der der Eigenschaft zugeordnete Wert.writable: Gibt an, ob der Eigenschaftswert geändert werden kann.enumerable: Zeigt an, ob die Eigenschaft aufzählbar ist.configurable: Bestimmt, ob der Property Descriptor geändert und die Eigenschaft aus dem object gelöscht werden kann.
Hinweis: Wenn Sie eine Eigenschaft auf normalem Weg anlegen (user.name = "John"), werden alle drei Flags auf true gesetzt. Wird eine neue Eigenschaft jedoch über Object.defineProperty definiert, ist jedes nicht angegebene Flag standardmäßig false.
Object.getOwnPropertyDescriptor betrachtet ausschließlich die eigenen Eigenschaften eines object. Wird nach einer Eigenschaft gefragt, die das object von seinem Prototyp erbt (oder nach einer Eigenschaft, die gar nicht existiert), gibt die Methode undefined zurück.
Weitere Informationen darüber, wie objects Eigenschaften erben, finden Sie unter Prototypal Inheritance.
Data Descriptors vs. Accessor Descriptors
Bisher haben wir Data Descriptors beschrieben, die einen value zusammen mit dem writable-Flag speichern. JavaScript unterstützt außerdem Accessor Descriptors, die value/writable durch Getter- und Setter-Funktionen ersetzen:
get: Eine Funktion, die beim Lesen der Eigenschaft aufgerufen wird (ohne Argumente).set: Eine Funktion, die beim Zuweisen eines Wertes zur Eigenschaft aufgerufen wird (erhält den neuen Wert).
Ein Descriptor ist entweder ein Data Descriptor oder ein Accessor Descriptor — niemals beides gleichzeitig. Die Kombination von value/writable mit get/set führt zu einem Fehler. Beide Typen teilen die Flags enumerable und configurable.
Accessor Properties werden verwendet, um einen Wert beim Lesen zu berechnen oder beim Schreiben zu validieren. Im folgenden Beispiel wird ein fullName-Accessor auf Basis zweier Data Properties bereitgestellt:
Eine ausführlichere Behandlung der get/set-Syntax (einschließlich der Kurzschreibweise in object-Literalen) finden Sie unter Property Getters and Setters. Da Getter und Setter mit this gebunden an das object ausgeführt werden, ist es hilfreich, Object Methods and "this" zu verstehen.
Mehrere Eigenschaften gleichzeitig definieren und auslesen
Für die Arbeit mit mehreren Eigenschaften in einem Schritt stellt JavaScript die Gegenstücke der oben genannten Methoden bereit:
Object.defineProperties(obj, descriptors)definiert mehrere Eigenschaften aus einer Map von Descriptors.Object.getOwnPropertyDescriptors(obj)gibt Descriptors für alle eigenen Eigenschaften (einschließlich nicht-aufzählbarer und Symbol-Schlüssel) als einzelnes object zurück.
Object.getOwnPropertyDescriptors ist besonders nützlich, um ein object mitsamt seinen Flags zu klonen — ein einfaches Spread oder Object.assign kopiert zwar die Werte, setzt jedoch alle Flags auf true zurück und überspringt Accessors.
Property Flags manipulieren
Das Verstehen und Manipulieren von Property Flags ist entscheidend für eine effektive JavaScript-Entwicklung. Schauen wir uns an, wie man diese Flags steuert, um das Verhalten von Eigenschaften präzise anzupassen.
Eine Eigenschaft als nicht-schreibbar markieren
Das Verhindern von Änderungen an einer Eigenschaft stellt die Datenkonsistenz sicher. Dies erreicht man, indem das writable-Flag auf false gesetzt wird.
Das Verhalten einer fehlgeschlagenen Zuweisung hängt davon ab, in welchem Modus der Code ausgeführt wird. Im Non-Strict-Modus schlägt das Schreiben in eine nicht-schreibbare Eigenschaft lautlos fehl: Die Zuweisung wird einfach ignoriert, es wird kein Fehler ausgelöst, und die Ausführung wird fortgesetzt — was Fehler verschleiern kann. Im Strict Mode ("use strict", und standardmäßig innerhalb von ES-Modulen und Klassen-Bodies) löst dieselbe Zuweisung einen TypeError aus. Diese Regel gilt für jede Operation, die ein Flag verletzt: Das Löschen einer nicht-konfigurierbaren Eigenschaft oder das Hinzufügen einer Eigenschaft zu einem nicht-erweiterbaren object schlägt im Non-Strict-Modus ebenfalls lautlos fehl und wirft im Strict Mode einen Fehler.
Eine Eigenschaft vor der Aufzählung verbergen
Manchmal ist es notwendig, Eigenschaften vor Aufzählungsprozessen wie for...in-Schleifen zu verbergen. Dies erreicht man, indem das enumerable-Flag auf false gesetzt wird.
Löschen und Ändern von Eigenschaften verhindern
Um sicherzustellen, dass eine Eigenschaft ein fester Bestandteil eines object bleibt, setzen Sie das configurable-Flag auf false.
Eine Eigenschaft als nicht-konfigurierbar zu markieren ist eine unumkehrbare Operation — es gibt kein Flag, um sie wieder konfigurierbar zu machen, und man kann enumerable nicht mehr umschalten oder die Eigenschaft zwischen einem Data und einem Accessor Descriptor wechseln.
Es gibt jedoch zwei wichtige Ausnahmen, solange eine Eigenschaft nicht-konfigurierbar ist:
- Das
writable-Flag darf vontrueauffalsegeändert werden (aber nicht zurück vonfalseauftrue). - Wenn die Eigenschaft noch
writable: trueist, kann ihrvaluegeändert werden — entweder durch direkte Zuweisung oder überObject.defineProperty.
Mit anderen Worten: configurable: false sperrt die Form der Eigenschaft, nicht zwingend ihren Wert. Um den Wert einer Eigenschaft wirklich einzufrieren, setzen Sie sowohl configurable: false als auch writable: false.
Übergeordnete APIs auf Basis dieser Flags
Flags müssen selten für jede Eigenschaft einzeln gesetzt werden. JavaScript bietet drei eingebaute Methoden, die diese Flags für ein gesamtes object umschalten:
Object.preventExtensions(obj)— verhindert, dass neue Eigenschaften hinzugefügt werden. Bestehende Eigenschaften können weiterhin geändert oder gelöscht werden.Object.seal(obj)— verhindert das Hinzufügen und Löschen von Eigenschaften, indem jede bestehende Eigenschaft alsconfigurable: falsemarkiert wird. Werte können sich noch ändern.Object.freeze(obj)— am restriktivsten: versiegelt das object und macht jede Eigenschaftwritable: false, sodass nichts hinzugefügt, entfernt oder geändert werden kann.
Jede Methode hat eine entsprechende Prüffunktion: Object.isExtensible, Object.isSealed und Object.isFrozen. Beachten Sie, dass diese Methoden nur eine Ebene tief wirken — Object.freeze friert keine verschachtelten objects ein (es handelt sich um ein "flaches" Einfrieren).
Fazit
Property Flags und Descriptors geben Ihnen präzise Kontrolle darüber, wie sich object-Eigenschaften verhalten:
- Ein Data Descriptor kombiniert einen
valuemitwritable; ein Accessor Descriptor verwendet stattdessenget/set-Funktionen. Beide teilenenumerableundconfigurable. - Flags werden mit
Object.getOwnPropertyDescriptor(eine Eigenschaft) oderObject.getOwnPropertyDescriptors(alle eigenen Eigenschaften) gelesen; geschrieben werden sie mitObject.definePropertyoderObject.defineProperties. Geerbte und fehlende Eigenschaften gebenundefinedzurück. configurable: falseist unumkehrbar und sperrt die Form der Eigenschaft — allerdings kann eine noch-writableEigenschaft ihren Wert ändern und ihrwritable-Flag auffalsesetzen.- Ein Verstoß gegen ein Flag schlägt im Non-Strict-Modus lautlos fehl, wirft jedoch im Strict Mode einen
TypeError. - Greifen Sie auf
Object.freeze,Object.sealundObject.preventExtensionszurück, wenn Sie ein gesamtes object statt einzelner Flags sperren möchten.
Nächste Schritte: Tauchen Sie ein in Property Getters and Setters für die Accessor-Syntax, Object Methods and "this" für das Verhalten von this darin, und Prototypal Inheritance, um zu sehen, wie die Eigenschaftssuche die Prototyp-Kette durchläuft.