PropertyIsEnumerable: примеры (JAVASCRIPT)

Метод propertyIsEnumerable для проверки перечисляемых свойств в JS
Раздел: Объекты, Дескрипторы
propertyIsEnumerable(v: PropertyKey): boolean

Описание функции propertyIsEnumerable

Метод propertyIsEnumerable() принадлежит прототипу Object и предназначен для определения, является ли указанное свойство перечисляемым. Перечисляемое свойство — это свойство, которое будет отображаться при итерации объекта с помощью цикла for...in (при условии, что оно не унаследовано от прототипа) или при использовании методов вроде Object.keys().

Метод проверяет, существует ли у объекта собственное (не унаследованное) свойство с указанным именем, и установлен ли для этого свойства внутренний атрибут [[Enumerable]] в значение true.

Синтаксис метода: obj.propertyIsEnumerable(prop)

  • Параметр prop (обязательный): Имя свойства в виде строки или Symbol. Метод преобразует переданное значение в строку, если это возможно.
  • Возвращаемое значение: Логическое значение (true или false).
    • true: Возвращается, если объект имеет собственное (не унаследованное) свойство с указанным именем, и его атрибут [[Enumerable]] равен true.
    • false: Возвращается в остальных случаях: если свойство отсутствует, является унаследованным или если оно собственное, но его атрибут [[Enumerable]] установлен в false.

Краткие примеры использования

Проверка обычного перечисляемого свойства объекта.

const obj = { a: 1, b: 2 };
console.log(obj.propertyIsEnumerable('a'));
true

Проверка свойства с атрибутом enumerable: false, заданного через Object.defineProperty.

const obj2 = {};
Object.defineProperty(obj2, 'hidden', {
    value: 'secret',
    enumerable: false
});
console.log(obj2.propertyIsEnumerable('hidden'));
false

Проверка унаследованного свойства.

function MyFunc() {
    this.ownProp = 'value';
}
MyFunc.prototype.inheritedProp = 'protoValue';
const instance = new MyFunc();
console.log(instance.propertyIsEnumerable('ownProp'));
console.log(instance.propertyIsEnumerable('inheritedProp'));
true
false

Проверка свойства, являющегося символом (Symbol).

const sym = Symbol('id');
const obj3 = { [sym]: 100, regular: 200 };
console.log(obj3.propertyIsEnumerable(sym));
true

Похожие функции в JavaScript

  • Object.keys(obj): Возвращает массив строк, содержащий имена всех собственных перечисляемых свойств объекта. Используется, когда нужен список таких свойств, а не проверка одного.
  • obj.hasOwnProperty(prop): Проверяет, является ли свойство собственным для объекта, но не учитывает атрибут [[Enumerable]]. Возвращает true для любого собственного свойства, независимо от его настройки перечисляемости.
  • Object.getOwnPropertyDescriptor(obj, prop): Возвращает дескриптор свойства, содержащий информацию о его атрибутах, включая enumerable. Это более мощный, но и более многословный метод для получения полной информации о свойстве.
  • for...in: Цикл, который перебирает все перечисляемые свойства объекта, включая унаследованные. Метод propertyIsEnumerable служит для более точного анализа перед или внутри такого цикла.

Метод propertyIsEnumerable предпочтительнее, когда требуется строгая проверка именно на собственное перечисляемое свойство. hasOwnProperty используется для более общей проверки принадлежности свойств объекту.

Типичные ошибки при использовании

1. Проверка унаследованных свойств. Часто ожидают, что метод вернет true для любого свойства, которое видно в объекте.

const arr = [1, 2, 3];
console.log(arr.propertyIsEnumerable('length')); // Собственное, но не перечисляемое
console.log(arr.propertyIsEnumerable('push'));   // Унаследованное от Array.prototype
false
false

2. Использование переменной вместо строки для имени свойства. Хотя метод выполняет преобразование, это может привести к неочевидным результатам.

const obj = { 0: 'zero' };
const propName = 0;
console.log(obj.propertyIsEnumerable(propName)); // Число преобразуется в строку
console.log(obj.propertyIsEnumerable('0'));
true
true

3. Проверка свойства, которое было удалено или никогда не существовало. Метод корректно вернет false, но это может не соответствовать ожиданиям разработчика.

const obj = { x: 10 };
delete obj.x;
console.log(obj.propertyIsEnumerable('x'));
console.log(obj.propertyIsEnumerable('y'));
false
false

Изменения в последних версиях

Метод propertyIsEnumerable присутствует в языке с ранних версий ECMAScript 1 (1997 г.) и остается стабильным. Его поведение не менялось в ключевых версиях стандарта ES5, ES6 (ES2015) и более поздних.

Основное уточнение, связанное с развитием языка, касается работы с новыми типами свойств:

  • В ES5 метод стал учитывать атрибуты свойств, заданные через Object.defineProperty.
  • В ES2015 метод был адаптирован для работы со свойствами-символами (Symbol). Он корректно принимает Symbol в качестве аргумента и проверяет их перечисляемость.

Метод является стандартным и полностью поддерживается во всех современных браузерах и средах выполнения.

Расширенные примеры использования

Работа с объектом, созданным через Object.create и имеющим свойства в прототипе.

Пример javascript
const proto = { enumerableProtoProp: 'from proto' };
const obj = Object.create(proto, {
    ownEnumerable: { value: 1, enumerable: true },
    ownNonEnumerable: { value: 2, enumerable: false }
});
obj.dynamicProp = 3;

console.log('enumerableProtoProp:', obj.propertyIsEnumerable('enumerableProtoProp'));
console.log('ownEnumerable:', obj.propertyIsEnumerable('ownEnumerable'));
console.log('ownNonEnumerable:', obj.propertyIsEnumerable('ownNonEnumerable'));
console.log('dynamicProp:', obj.propertyIsEnumerable('dynamicProp'));

// Для сравнения: for...in выведет перечисляемые свойства, включая унаследованные
let forInResult = [];
for (let key in obj) forInResult.push(key);
console.log('for...in keys:', forInResult);
enumerableProtoProp: false
ownEnumerable: true
ownNonEnumerable: false
dynamicProp: true
for...in keys: [ 'ownEnumerable', 'dynamicProp', 'enumerableProtoProp' ]

Проверка индексов массива и свойств прототипа массива.

Пример javascript
const fruits = ['apple', 'banana'];
fruits.customProp = 'test';

console.log('0:', fruits.propertyIsEnumerable('0')); // Индексы массивов - перечисляемые
console.log('length:', fruits.propertyIsEnumerable('length')); // Встроенное свойство - нет
console.log('customProp:', fruits.propertyIsEnumerable('customProp')); // Добавленное - да
console.log('forEach:', fruits.propertyIsEnumerable('forEach')); // Унаследованное от Array.prototype - нет

console.log('Object.keys:', Object.keys(fruits)); // Только перечисляемые собственные
0: true
length: false
customProp: true
forEach: false
Object.keys: [ '0', '1', 'customProp' ]

Использование с глобальным объектом и встроенными свойствами.

Пример javascript
// Свойства глобального объекта window (в браузере) или global (в Node.js)
// Большинство встроенных свойств не являются перечисляемыми.
console.log('window.propertyIsEnumerable("window"):', window.propertyIsEnumerable('window')); // В браузере
console.log('window.propertyIsEnumerable("document"):', window.propertyIsEnumerable('document'));

// Создание свойства с тем же именем, что и унаследованное
window.customAlert = 'my alert';
console.log('window.propertyIsEnumerable("customAlert"):', window.propertyIsEnumerable('customAlert'));
window.propertyIsEnumerable("window"): false
window.propertyIsEnumerable("document"): false
window.propertyIsEnumerable("customAlert"): true

Свойства, заданные через аксессоры (геттеры/сеттеры). Атрибут enumerable работает для них так же.

Пример javascript
const obj = {
    _value: 10,
    get readable() { return this._value; },
    set readable(val) { this._value = val; }
};
Object.defineProperty(obj, 'hiddenAccessor', {
    enumerable: false,
    get() { return 'secret'; }
});

console.log('readable:', obj.propertyIsEnumerable('readable'));
console.log('hiddenAccessor:', obj.propertyIsEnumerable('hiddenAccessor'));
console.log('_value:', obj.propertyIsEnumerable('_value'));
readable: true
hiddenAccessor: false
_value: true

Аналоги функции в других языках программирования

Концепция перечисляемых свойств часто специфична для JavaScript, но существуют схожие механизмы интроспекции в других языках.

Python: Аналогов в чистом виде нет. Для проверки наличия атрибута используется hasattr(), а для получения всех атрибутов — dir() или vars(). Концепция перечисляемости не применима.

class MyClass:
    def __init__(self):
        self.public_attr = 1
        self._private_attr = 2

obj = MyClass()
print(hasattr(obj, 'public_attr'))  # Проверка наличия
print(dir(obj))  # Список атрибутов
True
['_MyClass__private_attr', '__class__', ..., 'public_attr']

PHP: Существует метод property_exists() для проверки существования свойства у объекта и функция get_object_vars(), которая возвращает только доступные (в контексте) свойства. Понятие "перечисляемое" отсутствует.

class MyClass {
    public $publicProp = 1;
    private $privateProp = 2;
}
$obj = new MyClass();
var_dump(property_exists($obj, 'publicProp'));
var_dump(get_object_vars($obj));
bool(true)
array(1) {
  ["publicProp"]=>
  int(1)
}

C: Язык не имеет встроенной рефлексии или понятия перечисляемых свойств для структур. Интроспекция в чистом C невозможна.

JS propertyIsEnumerable function comments

En
PropertyIsEnumerable Returns a Boolean indicating whether the specified property is enumerable