Enumerability and ownership of properties
=========================================

В языке JavaScript свойства объектов могут быть перечисляемыми или неперечисляемыми (встречается вариант перевода: счётные или несчётные). Если внутреннему флагу [[Enumerable]] свойства присвоить значение true, то данное свойство становится перечисляемым. Это происходит по умолчанию для свойств, созданных простым присваиванием или через инициализацию свойств (свойства, определённые через Object.defineProperty получают по умолчанию значение флага [[Enumerable]] равным false). Перечисляемые свойства участвуют в итерации в цикле for...in, если только имя свойства не Символ. Принадлежность свойства определяется тем, принадлежит ли оно непосредственно объекту или получено из цепочки прототипов. Также можно получить весь список свойств объекта. Ниже, в таблице, указаны возможные способы нахождения, получения и итерации свойств объектов. Некоторые из них нельзя использовать без дополнительного кода, примеры которого приведены после таблицы.


 Перечисляемость и принадлежность свойств - встроенные методы определения,
 получения и итерации
 | Назначение | Свойства объекта | Свойства объекта и его прототипов | Свойства из цепочки прототипов |
| --- | --- | --- | --- |
| Определение | 

| Перечисляемые | Неперечисляемые | Перечисляемые и Неперечисляемые |
| --- | --- | --- |
| `propertyIsEnumerable (en-US)` | `hasOwnProperty`
 и !`propertyIsEnumerable (en-US)` | `hasOwnProperty` |

 | Недоступно без дополнительного кода | Недоступно без дополнительного кода |
| Получение | 

| Перечисляемые | Неперечисляемые | Перечисляемые и Неперечисляемые |
| --- | --- | --- |
| `Object.keys (en-US)` | 
 Получить
 `getOwnPropertyNames (en-US)`, выбрать свойства, не удовлетворяющие
 `propertyIsEnumerable (en-US)` | `getOwnPropertyNames (en-US)` |

 | Недоступно без дополнительного кода | Недоступно без дополнительного кода |
| Итерация | 

| Перечисляемые | Неперечисляемые | Перечисляемые и Неперечисляемые |
| --- | --- | --- |
| 
 Итерация по массиву
 `Object.keys (en-US)` | 
 Итерация по
 `getOwnPropertyNames (en-US)`, с выбранными свойствами, не удовлетворяющими
 `propertyIsEnumerable (en-US)` | 
 Итерация по
 `getOwnPropertyNames (en-US)` |

 | 

| Перечисляемые | Неперечисляемые | Перечисляемые и Неперечисляемые |
| --- | --- | --- |
| `for..in (en-US)` | Недоступно без дополнительного кода | Недоступно без дополнительного кода |

 | Недоступно без дополнительного кода |

Доступ к свойствам по их перечисляемости/принадлежности
-------------------------------------------------------

Хотим заметить, что данный алгоритм эффективен не для всех классов.

* Определение свойства: `SimplePropertyRetriever.theGetMethodYouWant(obj).indexOf(prop) > -1`
* Итерация: `SimplePropertyRetriever.theGetMethodYouWant(obj).forEach(function (value, prop) {});` (or use `filter()`, `map()`, etc.)


```js
var SimplePropertyRetriever = {
  getOwnEnumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, false, this._enumerable);
    // Или можно использовать for..in, отфильтрованный по hasOwnProperty или проще: return Object.keys(obj);
  },
  getOwnNonenumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, false, this._notEnumerable);
  },
  getOwnEnumerablesAndNonenumerables: function (obj) {
    return this.\_getPropertyNames(
      obj,
      true,
      false,
      this._enumerableAndNotEnumerable,
    );
    // Или можно использовать: return Object.getOwnPropertyNames(obj);
  },
  getPrototypeEnumerables: function (obj) {
    return this.\_getPropertyNames(obj, false, true, this._enumerable);
  },
  getPrototypeNonenumerables: function (obj) {
    return this.\_getPropertyNames(obj, false, true, this._notEnumerable);
  },
  getPrototypeEnumerablesAndNonenumerables: function (obj) {
    return this.\_getPropertyNames(
      obj,
      false,
      true,
      this._enumerableAndNotEnumerable,
    );
  },
  getOwnAndPrototypeEnumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, true, this._enumerable);
    // Или можно использовать for..in
  },
  getOwnAndPrototypeNonenumerables: function (obj) {
    return this.\_getPropertyNames(obj, true, true, this._notEnumerable);
  },
  getOwnAndPrototypeEnumerablesAndNonenumerables: function (obj) {
    return this.\_getPropertyNames(
      obj,
      true,
      true,
      this._enumerableAndNotEnumerable,
    );
  },
  // Private static property checker callbacks
  \_enumerable: function (obj, prop) {
    return obj.propertyIsEnumerable(prop);
  },
  \_notEnumerable: function (obj, prop) {
    return !obj.propertyIsEnumerable(prop);
  },
  \_enumerableAndNotEnumerable: function (obj, prop) {
    return true;
  },
  // По мотивам http://stackoverflow.com/a/8024294/271577
  \_getPropertyNames: function getAllPropertyNames(
    obj,
 iterateSelfBool,
 iteratePrototypeBool,
 includePropCb,
  ) {
    var props = [];

    do {
      if (iterateSelfBool) {
        Object.getOwnPropertyNames(obj).forEach(function (prop) {
          if (props.indexOf(prop) === -1 && includePropCb(obj, prop)) {
            props.push(prop);
          }
        });
      }
      if (!iteratePrototypeBool) {
        break;
      }
      iterateSelfBool = true;
    } while ((obj = Object.getPrototypeOf(obj)));

    return props;
  },
};
```

Определимость свойств
---------------------

|  | `in` | `for..in` | `hasOwnProperty` | `propertyIsEnumerable` | `in Object.keys` | `in Object.getOwnPropertyNames` |
| --- | --- | --- | --- | --- | --- | --- |
| Перечисляемые | true | true | true | true | true | true |
| Неперечисляемые | true | false | true | false | false | true |
| Унаследованные Перечисляемые | true | true | false | false | false | false |
| Унаследованные Неперечисляемые | true | false | false | false | false | false |

Смотрите также
--------------

* `in` (en-US)
* `for..in` (en-US)
* `hasOwnProperty`
* `propertyIsEnumerable` (en-US)
* `getOwnPropertyNames` (en-US)
* `Object.keys` (en-US)