JavaScript/Objekte/Object/propertyIsEnumerable

Aus SELFHTML-Wiki
Wechseln zu: Navigation, Suche

Mit der von Object.prototype vererbten Methode propertyIsEnumerable kann geprüft werden, ob eine eigene Eigenschaft eines Objektes aufzählbar ist oder nicht. Geerbte Eigenschaften werden bei der Prüfung nicht berücksichtigt.


Syntax

Object.prototype.propertyIsEnumerable(name)


Attribute
Writable true
Enumerable false
Configurable true


Prototyp


Eigenschaften


Beschreibung

Ob eine Objekteigenschaft aufzählbar ist oder nicht, entscheidet der Wert des Attributes [[Enumerable]] der Eigenschaft, der entweder true oder false ist. Nicht aufzählbare Eigenschaften werden bei bestimmten Operationen, wie etwa einer Schleife mit for und in nicht berücksichtigt. Die Frage nach der Aufzählbarkeit von Eigenschaften besitzt also einige Relevanz, umso mehr, wenn diese Eigenschaften über die Prototypenkette vererbt werden. Mit der Methode propertyIsEnumerable, welche standardmäßig an alle Objekte vererbt wird, kann die Aufzählbarkeit von Eigenschaften geprüft werden.


Beispiel
const object = {
  property : 'value'
};

console.log(object.propertyIsEnumerable('property')); // true


Im obigen Beispiel wird zunächst in Literalschreibweise (Object Initializer) ein Objekt mit einer Eigenschaft erstellt und der Konstante mit dem Bezeichner object als Wert zugewiesen. Auf diesem Objekt wird dann im nächsten Schritt die Methode propertyIsEnumerable aufgerufen, wobei der Name der Eigenschaft, also die Zeichenkette property als Argument übergeben wird. Da es sich hier nun erstens um eine eigene Eigenschaft des Objektes handelt und da zweitens für Eigenschaften die durch Zuweisung (Assignment) erstellt werden, der Standardwert des internen Attributes [[Enumerable]] true ist, wird entsprechend dieser Wert zurückgegeben und in die Konsole geschrieben.


Beispiel
const object = { };

Object.defineProperty(object, 'property', {
  value : 'value'
});

console.log(object.propertyIsEnumerable('property')); // false


Anders würde es aussehen, wenn wir die Objekteigenschaft nicht durch Zuweisung, sondern durch explizite Definition erstellt hätten, wie in dem Beispiel oben. Die Definition von Eigenschaften kann wie hier mit der Methode defineProperty des Konstruktors Object erfolgen. Wird nun eine Eigenschaft definiert, dann ist der Wert der Eigenschaft [[Enumerable]] anders als bei einer Erzeugung durch Zuweisung standardmäßig false. Eine auf diese Weise erstellte Objekteigenschaft ist also nicht aufzählbar, sofern nichts anderes bei der Definition angegeben wurde. Entsprechend ergibt die Prüfung mit der Methode propertyIsEnumerable im letzten Beispiel den Wert false.

Symbole als Schlüssel

Die Methode propertyIsEnumerable kann nicht nur für solche Objekteigenschaften aufgerufen werden, deren Schlüssel vom Datentyp String ist, sondern es ist ebenfalls möglich, der Methode ein Symbol als Argument zu übergeben, wenn dieses der Schlüssel einer zu prüfenden Eigenschaft ist.


Beispiel
const key = Symbol( );

const object = {
  [key] : 'value'
};

console.log(object.propertyIsEnumerable(key)); // true


In diesem Beispiel wird zunächst durch den Aufruf der Funktion Symbol ein Symbol erzeugt und in einer Konstante hinterlegt. Unter Verwendung der Syntax für berechnete Eigenschaftsnamen (Computed Property Names) wird dann eine Eigenschaft auf einem Objekt angelegt, deren Schlüssel das zuvor erzeugte Symbol ist. Abschließend wird die Methode propertyIsEnumerable auf dem Objekt aufgerufen und dabei das Symbol als Argument übergeben. Da es sich hier nun um eine eigene, aufzählbare Eigenschaft handelt, wird im Ergebnis der Wert true in die Konsole geschrieben.

Eigenschaften von Prototypen

Grundsätzlich gilt, dass die Methode propertyIsEnumerable nur solche Eigenschaften auf ihre Aufzählbarkeit hin überprüft, die auf dem Objekt selbst definiert sind, auf dem die Methode aufgerufen wurde. Das bedeutet im Umkehrschluss, dass Eigenschaften die über einen Prototypen an das Objekt vererbt wurden, grundsätzlich nicht geprüft werden.


Beispiel
const proto = {
  name : 'value'
};

console.log(proto.propertyIsEnumerable('name')); // true

const object = Object.create(proto);


In diesem Beispiel wird zunächst ein Objekt erzeugt und in der Konstante mit dem Bezeichner proto hinterlegt. Dabei wird auf dem Objekt eine Eigenschaft angelegt, die jedoch nur zugewiesen und nicht ausdrücklich definiert wurde, weshalb sie standardgemäß aufzählbar ist. Entsprechend wird beim Aufruf der Methode propertyIsEnumerable auf dem Objekt proto für die Eigenschaft name der Wert true zurückgegeben. Als nächstes wird nun ein weiteres Objekt erzeugt, diesmal jedoch nicht als Literal sondern durch den Aufruf der Methode Object.create, wobei eine Referenz auf das Objekt proto als Argument übergeben wird. Dies hat zur Folge, dass die Eigenschaft [[Prototype]] des auf diese Weise erzeugten Objektes nun eine Referenz auf proto enthält, dieses Objekt also der Prototyp des erstellten Objektes ist.


Beispiel
console.log(object.name); // value

console.log(object.propertyIsEnumerable('name')); // false


Da das Objekt, welches in der Konstante mit dem Bezeichner object gespeichert wurde von dem Objekt proto erbt, kann die aufzählbare Eigenschaft mit dem Namen name folglich auch über dieses Objekt referenziert werden. Wird nun aber nicht auf proto sondern auf object die Methode propertyIsEnumerable aufgerufen und dabei der String name übergeben, dann wird von der Methode der Wert false zurückgegeben, obwohl das Attribut [[Enumerable]] der Eigenschaft wie gesehen den Wert true hat. Es wird also in dem Fall, dass der Methode der Schlüssel einer geerbten Eigenschaft übergeben wird kein Fehler geworfen, sondern lediglich der Wert false zurückgegeben.

Undefinierte Eigenschaften

Wird der Methode propertyIsEnumerable bei ihrem Aufruf ein Wert als Argument übergeben, der keinem Schlüssel einer definierten Eigenschaft entspricht, dann ist der Rückgabewert der Methode false. Eine Ausnahme wird nicht geworfen. Das Verhalten unterscheidet sich also nicht vom Verhalten bei der Übergabe eines Schlüssels, für den nur auf einem Prototypen eine Eigenschaft existiert, wie im letzten Abschnitt beschrieben.


Beispiel
const object = { };

console.log(object.propertyIsEnumerable('key')); // false


Hier wird ein leeres Objekt erzeugt und gespeichert. Im nächsten Schritt wird nun auf diesem Objekt die Methode propertyIsEnumerable aufgerufen und dabei ein String als Argument übergeben, für den keine eigene Eigenschaft oder Methode auf dem Objekt definiert ist. Der Aufruf der internen Methode [[GetOwnProperty]] mit dem an die Methode propertyIsEnumerable übergebenen String ergibt hier entsprechend den Wert undefined, und für den Fall, dass die interne Methode undefined zurückgibt, ist als Rückgabewert von propertyIsEnumerable der Wert false vorgesehen. Dieser Wert wird hier entsprechend in der Konsole ausgegeben.

Aufzählbarkeit

Wie bereits gesehen, besteht ein Unterschied darin, ob eine Eigenschaft durch einfache Zuweisung oder durch ausdrückliche Definition erstellt wird. Wird eine Eigenschaft nämlich durch einen gewöhnlichen Zuweisungsausdruck erstellt, dann ist der Wert des Attributes [[Enumerable]] der Eigenschaft standardmäßig true. Dies gilt in der Regel sowohl für Eigenschaften als auch für Methoden, unabhängig davon, welche Syntax im konkreten Fall verwendet wird. Bei einer expliziten Definition, etwa mittels der Methode defineProperty, ist die Eigenschaft oder Methode hingegen nicht aufzählbar, insofern bei der Definition für das Attribut enumerable nichts anderes spezifiziert wurde.


Beispiel
const object = {
  method (value) {
    console.log(value);
  }
};

object.method('message'); // message

console.log(object.propertyIsEnumerable('method')); // true


In diesem Beispiel wird ein Objekt erstellt und dabei unter Verwendung der besonderen Syntax für die Definition von Methoden in Objektliteralen eine Methode angelegt. Wie die Überprüfung mit der Methode propertyIsEnumerable zeigt, ist die auf diese Weise definierte Methode aufzählbar. Würde es sich bei dem hier erstellten Objekt um einen Prototypen handeln, von dem andere Objekte erben, dann könnte die Tatsache, dass die Methode aufzählbar ist, zu Problemen führen, da sie unter Umständen bei der Iteration über die Eigenschaften eines erbenden Objektes berücksichtigt würde.


Beispiel
const name = 'propertyIsEnumerable';

console.log(Object.prototype.propertyIsEnumerable(name)); // false


Aus diesem Grund sind eingebaute Methoden auf prototypischen Objekten wie Object.prototype prinzipiell nicht aufzählbar, so wie auch die Methode propertyIsEnumerable selbst, wie das Beispiel oben zeigt. Insbesondere dann, wenn Eigenschaften und Methoden vererbt werden sollen, sollte man also immer berücksichtigen und abwägen, ob eine solche vererbe Eigenschaft oder Methode aufzählbar sein soll oder nicht, um im Zweifel das Attribut enumerable ausdrücklich auf false zu setzten.


Beispiel
class CustomType {
  method ( ) {
    return true;
  }
}

console.log(
  CustomType.prototype.propertyIsEnumerable('method') // false
);


Wird eine zu vererbende Methode übrigens innerhalb einer Klasse notiert, was seit der sechsten Edition der Sprache möglich ist, dann ist der Wert des Attributs [[Enumerable]] dieser Methode standardmäßig false. In diesem Fall kann man sich die explizite Definition mittels defineProperty oder der Schwestermethode defineProperties also sparen, wenn es nur darum geht zu verhindern, dass die vererbte Methode aufzählbar ist.


Beispiel
const object = {
  first : 'alpha',
  last : 'omega'
};

Object.defineProperty(object, 'last', {
  enumerable : false
});

for (let name in object) {
  if (object.hasOwnProperty(name)) {
    console.log(object[name]); // alpha
  }
}


Um die Auswirkungen der Aufzählbarkeit von Objekteigenschaften einmal zu veranschaulichen, wird in dem Beispiel oben zunächst ein Objekt mit zwei Eigenschaften erzeugt und in einer Konstante gespeichert. Wie bereits gesehen, sind auf diese Weise definierte Eigenschaften standardmäßig aufzählbar. Im nächsten Schritt wird dann unter Verwendung der Methode defineProperty der Wert des Attributs enumerable der Eigenschaft mit dem Namen last auf false gesetzt. Schließlich wird dann durch eine Schleife mit for und in über die Eigenschaften des zuvor erstellten Objektes iteriert.


Hinweis:
Es hat übrigens eine besondere Bewandnis, dass im Schleifenkörper die eigentliche Anweisung vom Rückgabewert der Methode hasOwnProperty abhängig gemacht wird, denn es ist so, dass bei dieser Schleifenvariante prinzipiell nicht nur eigene Eigenschaften des Objektes berücksichtigt werden, über das iteriert wird, sondern auch Eigenschaften die über die Prototypenkette geerbt wurden. Durch die Prüfung mit der Methode hasOwnProperty wird also sichergestellt, dass tatsächlich nur eigene Eigenschaften bei der Iteration berücksichtigt werden.


Im Ergebnis wird aufgrund der Anweisung im Körper der Schleife nur der Wert der Eigenschaft mit dem Namen first in die Konsole geschrieben, da nicht aufzählbare Eigenschaften wie die Eigenschaft mit dem Namen last, deren internes Attribut [[Enumerable]] nachträglich auf den booleschen Wert false gesetzt wurde, bei der Iteration mit dieser Schleife nicht berücksichtigt werden.


Beispiel
const object = { };

Object.defineProperties(object, {
  first : {
    value : 'alpha',
    enumerable : true
  },
  last : {
    value : 'omega'
  }
});

const keys = Object.keys(object);

console.log(keys); // ['first']


Ebenfalls nicht berücksichtigt werden Eigenschaften die nicht aufzählbar sind beim Aufruf der Methode Object.keys, welche die Schlüssel der eigenen Eigenschaften eines Objekts in einem Array zurückgibt, deren Schlüssel vom Datentyp String sind. Da bei der Definition wie in dem Beispiel oben der Standardwert für das Attribut [[Enumerable]] false ist, wird hier entsprechend nur der Schlüssel der zuerst definierten Eigenschaft mit dem Namen first in das Array eingefügt, welches von der Methode keys zurückgegeben wird.


Beispiel
const keys = Object.getOwnPropertyNames(object);

console.log(keys); // ['first', 'last']


Sollen ausdrücklich alle Eigenschaftsnamen ermittelt werden, unabhängig davon, ob eine Eigenschaft aufzählbar ist oder nicht, dann wäre die Methode getOwnPropertyNames eine bessere Alternative, oder sofern es sich bei den Schlüsseln der Eigenschaften um Symbole handelt, die Methode getOwnPropertySymbols. Sind hingegen sowohl Eigenschaften mit Strings als auch Eigenschaften mit Symbolen als Schlüssel zu berücksichtigen, dann bietet sich die Verwendung der Methode ownKeys des Standardobjektes Reflect an, welche ein Array zurückgibt, in welches erst alle eigenen Eigenschaften eingefügt werden deren Schlüssel Strings sind, und danach alle eigenen Eigenschaften mit Symbolen als Schlüssel.

Eigene Eigenschaften

Die Methode propertyIsEnumerable besitzt zwei eigene Eigenschaften mit den Namen length und name. Beide Eigenschaften sind schreibgeschützt aber konfigurierbar, das heißt, sie können zwar nicht durch Zuweisung, jedoch durch Definition verändert werden. Da von der Manipulation eingebauter Objekte jedoch grundsätzlich abzuraten ist, sollten auch diese Eigenschaften nicht verändert werden. Hierbei besteht nämlich immer das Risiko, dass ein gegebenenfalls zu einem späteren Zeitpunkt eingebundenes Programm sich auf die Originalwerte dieser Eigenschaften verlässt und es nach einer Veränderung dieser Eigenschaften entsprechend nicht mehr wie gewünscht funktioniert.


Beispiel
const method = Object.prototype.propertyIsEnumerable;

console.info(method.length); // 1

console.info(method.name); // propertyIsEnumerable


Der Wert der Eigenschaft length der Methode propertyIsEnumerable ist 1, da die Methode über genau einen benannten Parameter verfügt, welcher mit dem Schlüssel für die zu prüfende Eigenschaft zu initialisieren ist. Die Eigenschaft name wiederum enthält als Wert den Namen der Methode, also den String propertyIsEnumerable.

Weblinks