Suggest an editImprove this articleRefine the answer for “for...in vs for...of on an array”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**`for...in` iterates over keys, that is, the names of enumerable properties, and on an array it yields indices as strings `"0"`, `"1"`, `"2"`, while `for...of` iterates over the values of an iterable and yields `"a"`, `"b"`, `"c"`.** For arrays the right choice is `for...of`: it walks the elements strictly in index order and ignores extra properties. `for...in` is meant for objects, and on an array it will also surface any property added by hand or inherited from the prototype. ```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' ``` **Key point:** `for...in` means keys and objects, `for...of` means values and arrays.Shown above the full answer for quick recall.Answer (EN)Image**The difference between `for...in` and `for...of` is that the first iterates over keys and the second over values: on an array `for...in` yields indices as strings, while `for...of` yields the elements themselves.** It is one of those things you need to understand precisely, because arrays are where the difference bites most often. ## Theory ### TL;DR - `for...in` iterates over **keys and indices**, the names of enumerable properties, and is meant for **objects**. - `for...of` iterates over the **values** of iterables: `Array`, `String`, `Set`, `Map` and others. - On an array `for...in` returns indices as **strings** (`'0'`, `'1'`), not as numbers. - `for...in` will also surface properties added to the array by hand and enumerable properties from the prototype. - `for...of` guarantees element order, `for...in` gives no order guarantee for non-numeric keys. - Use `for...of` for arrays, and `for...in` or `Object.entries()` for objects. ### Quick example ```javascript const arr = ['a', 'b', 'c']; for (const index in arr) { console.log(index); // '0', '1', '2', strings } for (const value of arr) { console.log(value); // 'a', 'b', 'c', values } ``` ### for...in iterates over keys ```javascript const arr = ['a', 'b', 'c']; for (const index in arr) { console.log(index); // '0', '1', '2' console.log(typeof index); // 'string' } ``` What follows from this: - the loop returns **string indices**, not numbers, so `index + 1` gives `'01'` rather than `1`; - it was designed for **objects**, not for arrays; - it can pick up **extra properties** if they were added by hand or put on the prototype. ### for...of iterates over values ```javascript const arr = ['a', 'b', 'c']; for (const value of arr) { console.log(value); // 'a', 'b', 'c' } ``` What follows from this: - the loop works with **iterables**: `Array`, `String`, `Set`, `Map`, `arguments`, `NodeList`; - it returns the **element values**, not the keys; - it is the ideal choice for an array. When you need the index and the value at once, use `entries()`: ```javascript for (const [index, value] of arr.entries()) { console.log(index, value); // 0 'a', 1 'b', 2 'c' } ``` Here `index` is already a number, not a string. ### The difference in practice: extra properties ```javascript const arr = ['x', 'y', 'z']; arr.custom = 'hello'; // add a property by hand for (const key in arr) { console.log(key); // '0', '1', '2', 'custom', an unwanted key } for (const value of arr) { console.log(value); // 'x', 'y', 'z', elements only } ``` `for...in` walks **every enumerable property** of the object, including ones added by hand and ones inherited from the prototype. `for...of` takes **only the array elements**. This is exactly why `for...in` code is so often "rescued" by a guard that is only needed because the wrong loop was chosen: ```javascript for (const key in arr) { if (!Object.hasOwn(arr, key)) continue; // a crutch you do not need with for...of console.log(arr[key]); } ``` One more detail: `for...in` skips holes in a sparse array, while `for...of` walks them and yields `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 ``` ### Strings and other iterables ```javascript for (const char of 'JS') { console.log(char); // 'J', 'S' } ``` `for...of` works beautifully with strings, `Set` and `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` on a string returns the indices `'0'`, `'1'`, and on a `Set` or a `Map` it gives nothing useful, because their data does not live in enumerable properties. ### Comparison table | Feature | `for...in` | `for...of` | | --- | --- | --- | | Iterates over | keys (indices, properties) | values | | Works with | objects, arrays | iterables (array, string, Set, Map) | | Returns | strings, property names | the values themselves | | Iteration order | unpredictable for non-numeric keys | preserves the iterator order | | Sees added properties | Yes | No | | Good for arrays | No | Yes | | Good for objects | Yes | No (not without `Object.entries()`) | A simple rule worth remembering: > `for...in` iterates over **keys** and is for **objects**. > `for...of` iterates over **values** and is for **arrays and iterables**. ### Common mistakes - **Reaching for `for...in` on an array.** This is the most common mistake: the loop hands you string indices and picks up extra properties. - **Treating the index as a number.** In `for...in` the index is a string, so `index + 1` concatenates into `'01'`. You need `Number(index)` or `arr.entries()`. - **Trying `for...of` on a plain object.** A plain object is not iterable, so you get `TypeError: obj is not iterable`. Use `Object.keys()`, `Object.values()` or `Object.entries()`. - **Relying on `for...in` order.** Integer keys come out in ascending order and the rest in insertion order, so a mixed object is not walked the way it looks. - **Forgetting the prototype.** `for...in` climbs the prototype chain and will show enumerable properties from there too. - **Expecting `break` to work in `forEach`.** It is not about these two loops as such, but it is often why they get chosen: `forEach` has no `break` or `continue`, while both of these loops support them.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.