Перелічуваність і власність властивостей
Кожна властивість об'єктів у JavaScript може бути класифікована за трьома критеріями:
- Перелічувана або неперелічувана;
- Рядок або символ;
- Власна властивість або властивість, успадкована з ланцюжка прототипів.
Перелічувані властивості – ті властивості, чия прихована позначка перелічуваності має значення істинності, що є усталеним для властивостей, створених шляхом простого присвоєння або ініціалізатора властивостей. Властивості, означені за допомогою Object.defineProperty
, усталено не є перелічуваними. Більшість засобів ітерування (як то цикли for...in
і статичний метод Object.keys
) обробляє лише перелічувані ключі.
Власність властивостей визначається тим, чи належить властивість безпосередньо об'єктові, а не його ланцюжкові прототипів.
До всіх властивостей, перелічуваних і ні, рядкових і символьних, власних і успадкованих, можна звернутися за допомогою запису крапки або квадратних дужок. Цей розділ зосереджується на засобах, які JavaScript надає для послідовної обробки групи властивостей об'єкта.
Перевірка властивості об'єкта
Є чотири вбудовані способи перевірити властивість об'єкта. Вони підтримують і рядкові, і символьні ключі. Наступна таблиця підсумовує те, коли кожен з методів повертає true
.
Перелічувана, власна | Перелічувана, успадкована | Неперелічувана, власна | Неперелічувана, успадкована | |
---|---|---|---|---|
propertyIsEnumerable() |
true ✅ |
false ❌ |
false ❌ |
false ❌ |
hasOwnProperty() |
true ✅ |
false ❌ |
true ✅ |
false ❌ |
Object.hasOwn() |
true ✅ |
false ❌ |
true ✅ |
false ❌ |
in |
true ✅ |
true ✅ |
true ✅ |
true ✅ |
Обхід властивостей об'єкта
У JavaScript є чимало методів, що обходять групу властивостей об'єкта. Іноді ці властивості повертаються як масив; іноді вони ітеруються в циклі одна за одною; іноді вони використовуються для створення або видозмінювання іншого об'єкта. наступна таблиця підсумовує те, коли може бути оброблена властивість.
Методи, що обробляють лише рядкові властивості або лише символьні властивості, мають окремі примітки. ✅ означає, що властивість такого типу обробляється; ❌ означає, що ні.
Перелічувана, власна | Перелічувана, успадкована | Неперелічувана, власна | Неперелічувана, успадкована | |
---|---|---|---|---|
Object.keys Object.values Object.entries |
✅ (рядки) |
❌ | ❌ | ❌ |
Object.getOwnPropertyNames |
✅ (рядки) |
❌ | ✅ (рядки) |
❌ |
Object.getOwnPropertySymbols |
✅ (символи) |
❌ | ✅ (symbols) |
❌ |
Object.getOwnPropertyDescriptors |
✅ | ❌ | ✅ | ❌ |
Reflect.ownKeys |
✅ | ❌ | ✅ | ❌ |
for...in |
✅ (рядки) |
✅ (strings) |
❌ | ❌ |
Object.assign (Після першого параметра) |
✅ | ❌ | ❌ | ❌ |
Object spread | ✅ | ❌ | ❌ | ❌ |
Отримання властивостей за їхньою перелічуваністю чи власністю
Зверніть увагу, що це не найефективніший алгоритм для всіх випадків, зате корисний для швидкої демонстрації.
- Перевірка може відбутися за
SimplePropertyRetriever.theGetMethodYouWant(obj).includes(prop)
- Ітерування може відбутися за
SimplePropertyRetriever.theGetMethodYouWant(obj).forEach((value, prop) => {});
(або використайтеfilter()
,map()
тощо)
const SimplePropertyRetriever = {
getOwnEnumProps(obj) {
return this._getPropertyNames(obj, true, false, this._enumerable);
// Або можна було б використати for...in, відфільтрований за Object.hasOwn, або просто таке: return Object.keys(obj);
},
getOwnNonEnumProps(obj) {
return this._getPropertyNames(obj, true, false, this._notEnumerable);
},
getOwnProps(obj) {
return this._getPropertyNames(
obj,
true,
false,
this._enumerableAndNotEnumerable,
);
// Або просто використайте: 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);
// Або ж можна використати невідфільтрований for...in
},
getOwnAndPrototypeNonEnumProps(obj) {
return this._getPropertyNames(obj, true, true, this._notEnumerable);
},
getOwnAndPrototypeEnumAndNonEnumProps(obj) {
return this._getPropertyNames(
obj,
true,
true,
this._enumerableAndNotEnumerable,
);
},
// Функції зворотного виклику для перевірки приватних статичних властивостей
_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;
},
// Натхненно 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;
},
};