> For the complete documentation index, see [llms.txt](https://strctr.gitbook.io/programming/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://strctr.gitbook.io/programming/01-languages/javascript/01-language/b-structured/b2-objects/06-object-iteration.md).

# 06. Object Iteration

* **Перечисляемые свойства** --- это те свойства, для которых внутренний атрибут ключа выставлен в `enumerable = true`. Перечисляемые свойства можно перебрать в цикле `for..in`, за исключением полей-символов.
* **Принадлежность свойства** определяется тем, принадлежит ли свойство самому объекту, или находится в его цепочке прототипов.
  * *Собственные свойства* принадлежат самому объекту, а
  * *Унаследованные свойства* принадлежат его прототипу.

В ES6 порядок перебора ключей стандартизирован:

* Все индексы массивов, отсортированные по возрастанию. Индекс массива - специальный ключ-строка, хранящий число `0..2^32−1`.
* Все строковые ключи, в том порядке, в котором они объявлены.
* Все символы, в том порядке в котором они объявлены.

В зависимости от способа перечисления можно получить разные свойства:

![alt text](https://1356653101-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5bu9Zoywb01ca-XJjd%2F-M5buJXbyydR9RxqeVRI%2F-M5buQW5FF_-5095zXr_%2Fjs-detection-table.png?generation=1587660055005924\&alt=media)

## `for-in`

Цикл `for..in` перебирает перечисляемые свойства объекта в произвольном порядке, включая унаследованные:

```javascript
const obj = { a: 1, b: 2, c: 3 };
for (const prop in obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}
```

Особенности:

* Порядок не определен
* Перечисляются так же и наследуемые свойства по всей цепочке прототипов.
* Если необходимо перечислять только собственные свойства объекта, их необходимо фильтровать
* Если свойство объекта отмечено как `enumerable: false`, то оно не будет встречаться в цикле.
* В теле цикла можно изменять объект, а так же удалять его свойства.

## `hasOwnProperty` guard

Common pattern consisting of `for..in` with `Object.prototype.hasOwnProperty` guards:

```javascript
const obj = {
  x: 1,
  y: 1,
  z: 1
};
const total = 0;
for (const prop in obj) {
  if (obj.hasOwnProperty(prop)) {
    total += obj[prop];
  }
}
```

Note that in this particular case, the `Object#hasOwnProperty` guard is not used properly, because the lookup happens on the `obj` itself, which leads to potential performance issues, i.e. if you pass many different shapes of objects then the `obj.hasOwnProperty` access will become megamorphic, but might also turn into a correctness issue, i.e. if `obj` has a `null` prototype because `obj` was created via `Object.create(null)`, then the expression `obj.hasOwnProperty` will raise an error. So make sure to always follow the advice to write `Object.prototype.hasOwnProperty.call(obj, prop)` instead, which is safe and avoids the potential negative performance impact:

```javascript
const obj = {
  x: 1,
  y: 1,
  z: 1
};
const total = 0;
for (const prop in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, prop)) {
    total += obj[prop];
  }
}
```

## `for-of`

Конструкция `for..of` в начале своего выполнения автоматически вызывает `Symbol.iterator()`, получает итератор и далее вызывает метод `next()` до получения done: `true`. Если функционал по перебору (метод `next`) предоставляется самим объектом, то можно вернуть `this` в качестве итератора:

```javascript
// Throw Error
const obj = { a: 1, b: 2, c: 3 };
for (const prop of obj) {
  console.log("obj." + prop + " = " + obj[prop]);
}

// Ok
const obj = { a: 1, b: 2, c: 3 };
for (const [key, value] of Object.entries(obj)) {
  console.log("obj." + key + " = " + value);
}
```

Создание собственного итератора:

```javascript
const range = {
    from: 1,
    to: 5,

    [Symbol.iterator]() {
        return this;
    },

    next() {
        ...
    }
};

for (let num of range) {
    console.log(num); // 1, затем 2, 3, 4, 5
}
```

## `Object.keys()`, `Object.values()`

`Object.keys() : Array<string>` --- возвращает массив имен собственных перечислимых свойств объекта.

```javascript
const arr = ["a", "b", "c"];
Object.keys(arr); // console: ['0', '1', '2']

// array like object
const obj = { 0: "a", 1: "b", 2: "c" };
Object.keys(obj); // console: ['0', '1', '2']

// array like object with random key ordering
const an_obj = { 100: "a", 2: "b", 7: "c" };
Object.keys(an_obj); // console: ['2', '7', '100']

// getFoo is property which isn't enumerable
const my_obj = Object.create(
  {},
  {
    getFoo: {
      value: function() {
        return this.foo;
      }
    }
  }
);
my_obj.foo = 1;

Object.keys(my_obj); // console: ['foo']
```

Не возвращает унаследованные свойства

```javascript
const simpleColors = {
  colorA: "white",
  colorB: "black"
};
const natureColors = {
  colorC: "green",
  colorD: "yellow"
};

Object.setPrototypeOf(natureColors, simpleColors);
Object.keys(natureColors); // => ['colorC', 'colorD']

natureColors["colorA"]; // => 'white'
natureColors["colorB"]; // => 'black'
```

Похожие функции, работающие по тем же принципам:

* `Object.values`
* `Object.entries`

## `Object.getOwnPropertyNames()`

`Object.getOwnPropertyNames() : Array<string>` - действует подобно функции `Object.keys()`, но возвращает имена всех собственных свойств указанного объекта, а не только перечислимые. Порядок свойств такой же, как при ручном переборе объекта в цикле.

```javascript
const arr = ["a", "b", "c"];
Object.getOwnPropertyNames(arr).sort(); // logs '0,1,2,length'

// Array-like object
const obj = { 0: "a", 1: "b", 2: "c" };
Object.getOwnPropertyNames(obj).sort(); // logs '0,1,2'

// non-enumerable property
const my_obj = Object.create(
  {},
  {
    getFoo: {
      value: function() {
        return this.foo;
      },
      enumerable: false
    }
  }
);
my_obj.foo = 1;

Object.getOwnPropertyNames(my_obj).sort(); // logs 'foo,getFoo'
```

Похожие методы:

* `Object.getOwnPropertySymbols(obj) : Array<symbol>` -- возвращает все собственные ключи-символы объекта.
* `Reflect.ownKeys(obj) : Array<string|symbol>` --- возвращает все ключи всех свойств объекта.
* `Reflect.enumerate(obj) : Iterator` - возвращает все строковые значения всех перечисляемых свойств.
