ComparisonComparisons

for...in vs for...of

for...of hands you the successive values of an iterable object, while for...in hands you the successive property names of an object as strings.

On this page

Side-by-side comparison

#
  • What it hands you on every turn

    for...of
    The next value
    for...in
    The next property name (a string)
  • Works on arrays

    for...of
    Yes, it’s the natural choice
    for...in
    Technically yes, but it returns indexes as strings
  • Works on plain objects

    for...of
    No, an object isn’t iterable
    for...in
    Yes, that’s its main use
  • Properties inherited from the prototype

    for...of
    Doesn’t matter, it only iterates values
    for...in
    Includes them, you need to filter with Object.hasOwn()
  • break, continue, await

    for...of
    All three work
    for...in
    break and continue work, await doesn’t
  • Typical use

    for...of
    Arrays, Map, Set, strings, NodeList
    for...in
    Rare, Object.keys() with for...of is simpler

Example

#
JavaScript
const tasks = ['Lesson 1', 'Lesson 2', 'Lesson 3']

for (const index in tasks) {
  console.log(typeof index, index)
}

for (const task of tasks) {
  console.log(task)
}
Console
string 0
string 1
string 2
Lesson 1
Lesson 2
Lesson 3

Definition and usage

#

Both loops hand you something different on every turn. for...of works on iterable objects, such as arrays, strings, Map, and Set, and returns the next value. for...in works on plain objects and returns the next property name (key) as a string, and you read the value itself separately, through object[key].

On an array for...in technically works too, but it returns the indexes as strings ("0", "1") and, besides them, may also pick up extra enumerable properties, including ones inherited from the prototype. for...of doesn’t have that problem, because it iterates over the values themselves, and it also supports break, continue, and await inside it, which for...in doesn’t fully support.

The rule is simple: use for...of for arrays, strings, and other iterable objects. Leave for...in for rare situations with objects whose shape you don’t know up front, and even there Object.keys() or Object.entries() together with for...of is often more convenient.

Syntax

#
Syntax
for (const value of iterable) {
  // statements
}
for (const key in object) {
  // statements
}

More examples

#
for...in on an array: the trap with indexes
JavaScript
const points = [10, 20, 30]

for (const index in points) {
  console.log(index + 1)
}

for (const [index] of points.entries()) {
  console.log(index + 1)
}
Console
01
11
21
1
2
3

In for...in the index is a string, so + 1 concatenates text. for...of with entries() gives a numeric index, so the addition works normally.

An object: for...in vs Object.entries()
JavaScript
const student = { name: 'Jack', level: 4 }

for (const key in student) {
  console.log(key, student[key])
}

for (const [key, value] of Object.entries(student)) {
  console.log(key, value)
}
Console
name Jack
level 4
name Jack
level 4

For a plain object with no inherited properties, both loops give the same result. Object.entries(), however, returns only the object’s own keys, so it’s safer.

Good practices

#
  • For arrays, always choose for...of (or forEach(), map()). for...in on an array is a common cause of hard-to-find bugs.
  • Need an object’s keys in a for...of loop? Use Object.keys() or Object.entries(), then you don’t need to check Object.hasOwn().
  • With await in a loop over an array of asynchronous operations, only for...of works.

Related entries

#

Spotted a mistake or missing an example? Write to us.