Skip to main content

for...in vs for...of on an array

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

Featurefor...infor...of
Iterates overkeys (indices, properties)values
Works withobjects, arraysiterables (array, string, Set, Map)
Returnsstrings, property namesthe values themselves
Iteration orderunpredictable for non-numeric keyspreserves the iterator order
Sees added propertiesYesNo
Good for arraysNoYes
Good for objectsYesNo (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.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.