Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «for...in проти for...of у масиві». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`for...in` перебирає ключі, тобто імена перелічуваних властивостей, і для масиву віддає індекси у вигляді рядків `"0"`, `"1"`, `"2"`, а `for...of` перебирає значення елементів ітерованої структури і віддає `"a"`, `"b"`, `"c"`.** Для масивів правильний вибір це `for...of`: він іде строго по елементах у порядку індексів і не чіпає сторонні властивості. `for...in` призначений для об'єктів, і на масиві він також покаже будь-яку властивість, дописану вручну або успадковану з прототипу. ```javascript const arr = ['a', 'b', 'c']; for (const index of Object.keys(arr)) console.log(index); // '0', '1', '2' for (const value of arr) console.log(value); // 'a', 'b', 'c' ``` **Ключове:** `for...in` це ключі та об'єкти, `for...of` це значення та масиви.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Різниця між `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...in` | `for...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`, а в обох цих циклах вони працюють.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.