Aufzählbarkeit und Besitz von Eigenschaften
Jede Eigenschaft in JavaScript-Objekten kann anhand von drei Faktoren klassifiziert werden:
- Aufzählbar oder nicht aufzählbar;
- String oder symbol;
- Eigene Eigenschaft oder geerbte Eigenschaft aus der Prototyp-Kette.
Aufzählbare Eigenschaften sind diejenigen Eigenschaften, deren internes Aufzählungs-Flag auf true gesetzt ist. Dies ist der Standard für Eigenschaften, die durch einfache Zuweisung oder durch einen Eigenschafts-Initializer erstellt wurden. Eigenschaften, die über Object.defineProperty
definiert werden, sind standardmäßig nicht aufzählbar. Die meisten Iterationsmethoden (wie z.B. for...in
-Schleifen und Object.keys
) besuchen nur aufzählbare Schlüssel.
Der Besitz von Eigenschaften wird dadurch bestimmt, ob die Eigenschaft direkt zum Objekt gehört und nicht zu dessen Prototyp-Kette.
Alle Eigenschaften, egal ob aufzählbar oder nicht, String oder Symbol, eigen oder geerbt, können mit Punktnotation oder Klammernotation aufgerufen werden. In diesem Abschnitt konzentrieren wir uns auf die von JavaScript bereitgestellten Mittel, um eine Gruppe von Objekteigenschaften einzeln zu besuchen.
Abfragen von Objekteigenschaften
Es gibt vier integrierte Möglichkeiten, eine Eigenschaft eines Objekts abzufragen. Sie unterstützen sowohl String- als auch Symbolschlüssel. Die folgende Tabelle fasst zusammen, wann jede Methode true
zurückgibt.
Aufzählbar, eigene | Aufzählbar, geerbt | Nicht aufzählbar, eigene | Nicht aufzählbar, geerbt | |
---|---|---|---|---|
propertyIsEnumerable() |
true ✅ |
false ❌ |
false ❌ |
false ❌ |
hasOwnProperty() |
true ✅ |
false ❌ |
true ✅ |
false ❌ |
Object.hasOwn() |
true ✅ |
false ❌ |
true ✅ |
false ❌ |
in |
true ✅ |
true ✅ |
true ✅ |
true ✅ |
Durchlaufen von Objekteigenschaften
Es gibt viele Methoden in JavaScript, die eine Gruppe von Eigenschaften eines Objekts durchlaufen. Manchmal werden diese Eigenschaften als Array zurückgegeben; manchmal werden sie in einer Schleife einzeln durchlaufen; manchmal werden sie zum Erstellen oder Ändern eines anderen Objekts verwendet. Die folgende Tabelle fasst zusammen, wann eine Eigenschaft besucht werden kann.
Methoden, die nur String-Eigenschaften oder nur Symbol-Eigenschaften besuchen, werden einen zusätzlichen Hinweis haben. ✅ bedeutet, dass eine Eigenschaft dieses Typs besucht wird; ❌ bedeutet, dass sie nicht besucht wird.
Aufzählbar, eigene | Aufzählbar, geerbt | Nicht aufzählbar, eigene | Nicht aufzählbar, geerbt | |
---|---|---|---|---|
Object.keys Object.values Object.entries |
✅ (Strings) |
❌ | ❌ | ❌ |
Object.getOwnPropertyNames |
✅ (Strings) |
❌ | ✅ (Strings) |
❌ |
Object.getOwnPropertySymbols |
✅ (Symbole) |
❌ | ✅ (Symbole) |
❌ |
Object.getOwnPropertyDescriptors |
✅ | ❌ | ✅ | ❌ |
Reflect.ownKeys |
✅ | ❌ | ✅ | ❌ |
for...in |
✅ (Strings) |
✅ (Strings) |
❌ | ❌ |
Object.assign (Nach dem ersten Parameter) |
✅ | ❌ | ❌ | ❌ |
Object spread | ✅ | ❌ | ❌ | ❌ |
Erhalten von Eigenschaften nach Aufzählbarkeit/Besitz
Beachten Sie, dass dies nicht der effizienteste Algorithmus für alle Fälle ist, aber nützlich für eine schnelle Demonstration.
- Die Erkennung kann erfolgen über
SimplePropertyRetriever.theGetMethodYouWant(obj).includes(prop)
- Die Iteration kann erfolgen über
SimplePropertyRetriever.theGetMethodYouWant(obj).forEach((value, prop) => {});
(oder verwenden Siefilter()
,map()
, etc.)
const SimplePropertyRetriever = {
getOwnEnumProps(obj) {
return this._getPropertyNames(obj, true, false, this._enumerable);
// Or could use for...in filtered with Object.hasOwn or just this: return Object.keys(obj);
},
getOwnNonEnumProps(obj) {
return this._getPropertyNames(obj, true, false, this._notEnumerable);
},
getOwnProps(obj) {
return this._getPropertyNames(
obj,
true,
false,
this._enumerableAndNotEnumerable,
);
// Or just use: return Object.getOwnPropertyNames(obj);
},
getPrototypeEnumProps(obj) {
return this._getPropertyNames(obj, false, true, this._enumerable);
},
getPrototypeNonEnumProps(obj) {
return this._getPropertyNames(obj, false, true, this._notEnumerable);
},
getPrototypeProps(obj) {
return this._getPropertyNames(
obj,
false,
true,
this._enumerableAndNotEnumerable,
);
},
getOwnAndPrototypeEnumProps(obj) {
return this._getPropertyNames(obj, true, true, this._enumerable);
// Or could use unfiltered for...in
},
getOwnAndPrototypeNonEnumProps(obj) {
return this._getPropertyNames(obj, true, true, this._notEnumerable);
},
getOwnAndPrototypeEnumAndNonEnumProps(obj) {
return this._getPropertyNames(
obj,
true,
true,
this._enumerableAndNotEnumerable,
);
},
// Private static property checker callbacks
_enumerable(obj, prop) {
return Object.prototype.propertyIsEnumerable.call(obj, prop);
},
_notEnumerable(obj, prop) {
return !Object.prototype.propertyIsEnumerable.call(obj, prop);
},
_enumerableAndNotEnumerable(obj, prop) {
return true;
},
// Inspired by http://stackoverflow.com/a/8024294/271577
_getPropertyNames(obj, iterateSelf, iteratePrototype, shouldInclude) {
const props = [];
do {
if (iterateSelf) {
Object.getOwnPropertyNames(obj).forEach((prop) => {
if (props.indexOf(prop) === -1 && shouldInclude(obj, prop)) {
props.push(prop);
}
});
}
if (!iteratePrototype) {
break;
}
iterateSelf = true;
obj = Object.getPrototypeOf(obj);
} while (obj);
return props;
},
};