Skip to main content

for...in проти for...of у масиві

Різниця між for...in і for...of у тому, що перший перебирає ключі, а другий значення: на масиві for...in дає індекси у вигляді рядків, а for...of дає самі елементи. Це одна з тих речей, які треба розуміти чітко, бо саме на масивах різниця найчастіше і стріляє.

Теорія

TL;DR

  • for...in перебирає ключі та індекси, тобто імена перелічуваних властивостей, і призначений для об'єктів.
  • for...of перебирає значення елементів ітерованих структур: Array, String, Set, Map та інших.
  • На масиві for...in повертає індекси як рядки ('0', '1'), а не як числа.
  • for...in покаже і властивості, дописані до масиву вручну, і перелічувані властивості з прототипу.
  • for...of гарантує порядок елементів, for...in гарантій щодо порядку нечислових ключів не дає.
  • Для масивів беріть for...of, для об'єктів for...in або Object.entries().

Швидкий приклад

javascript
const arr = ['a', 'b', 'c']; for (const index in arr) { console.log(index); // '0', '1', '2', рядки } for (const value of arr) { console.log(value); // 'a', 'b', 'c', значення }

for...in перебирає ключі

javascript
const arr = ['a', 'b', 'c']; for (const index in arr) { console.log(index); // '0', '1', '2' console.log(typeof index); // 'string' }

Що з цього випливає:

  • цикл повертає рядкові індекси, а не числа, тому index + 1 дасть '01', а не 1;
  • він створений для об'єктів, а не для масивів;
  • він може захопити додаткові властивості, якщо їх дописати вручну або додати в прототип.

for...of перебирає значення

javascript
const arr = ['a', 'b', 'c']; for (const value of arr) { console.log(value); // 'a', 'b', 'c' }

Що з цього випливає:

  • цикл працює з ітерованими структурами: Array, String, Set, Map, arguments, NodeList;
  • він повертає значення елементів, а не ключі;
  • це ідеальний вибір для масиву.

Якщо потрібні і індекс, і значення одночасно, беріть entries():

javascript
for (const [index, value] of arr.entries()) { console.log(index, value); // 0 'a', 1 'b', 2 'c' }

Тут index вже число, а не рядок.

Різниця на практиці: зайві властивості

javascript
const arr = ['x', 'y', 'z']; arr.custom = 'hello'; // додамо властивість вручну for (const key in arr) { console.log(key); // '0', '1', '2', 'custom', зайвий ключ } for (const value of arr) { console.log(value); // 'x', 'y', 'z', тільки елементи }

for...in обходить усі перелічувані властивості об'єкта, включно з дописаними вручну і успадкованими з прототипу. for...of бере тільки елементи масиву.

Саме тому код на for...in часто «рятують» перевіркою, яка насправді потрібна лише через неправильно обраний цикл:

javascript
for (const key in arr) { if (!Object.hasOwn(arr, key)) continue; // милиця, якої не треба з for...of console.log(arr[key]); }

Ще одна деталь: for...in пропускає «дірки» у розрідженому масиві, а for...of проходить по них і віддає undefined.

javascript
const sparse = [1, , 3]; for (const key in sparse) console.log(key); // '0', '2' for (const value of sparse) console.log(value); // 1, undefined, 3

Рядки та інші ітеровані структури

javascript
for (const char of 'JS') { console.log(char); // 'J', 'S' }

for...of чудово працює з рядками, Set і Map:

javascript
const set = new Set([1, 2, 2, 3]); for (const value of set) console.log(value); // 1, 2, 3 const map = new Map([['a', 1], ['b', 2]]); for (const [key, value] of map) console.log(key, value); // 'a' 1, 'b' 2

for...in на рядку поверне індекси '0', '1', а на Set чи Map не дасть нічого корисного, бо їхні дані лежать не в перелічуваних властивостях.

Порівняльна таблиця

Особливістьfor...infor...of
Перебираєключі (індекси, властивості)значення
Працює зоб'єктами, масивамиітерованими структурами (масив, рядок, Set, Map)
Повертаєрядки, імена властивостейсамі значення
Порядок обходудля нечислових ключів непередбачуванийзберігає порядок ітератора
Бачить дописані властивостіТакНі
Підходить для масивівНіТак
Підходить для об'єктівТакНі (без Object.entries())

Просте правило, яке варто запам'ятати:

for...in перебирає ключі і потрібен для об'єктів. for...of перебирає значення і потрібен для масивів та ітерованих структур.

Типові помилки

  • Брати for...in для масиву. Це найчастіша помилка: цикл віддасть рядкові індекси і зачепить зайві властивості.
  • Рахувати індекс числом. У for...in індекс це рядок, тому index + 1 дає конкатенацію '01'. Потрібен Number(index) або arr.entries().
  • Пробувати for...of на звичайному об'єкті. Простий об'єкт не ітерований, тому буде TypeError: obj is not iterable. Потрібні Object.keys(), Object.values() або Object.entries().
  • Розраховувати на порядок у for...in. Цілочислові ключі йдуть за зростанням, а решта в порядку додавання, тому змішаний об'єкт обходиться не так, як здається.
  • Забувати про прототип. for...in підіймається ланцюжком прототипів і покаже перелічувані властивості звідти теж.
  • Використовувати break у forEach. Це не про for...in чи for...of, але саме через це їх часто й обирають: у forEach немає break і continue, а в обох цих циклах вони працюють.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.