cf.completefrontendCode editorOpen lab
THE JAVASCRIPT FIELD GUIDE

for…of & for…in

Loop over collection values with for...of, object keys with for...in, and learn why arrays, strings, mutation, and entries() need different choices.

By the end, you can
  • 01
    Choose the right loopUse for...of for values and for...in for object keys.
  • 02
    Predict tricky outputExplain string keys, emoji iteration, and key order.
  • 03
    Iterate safelyAvoid array for...in bugs and copy keys before mutating.

Two loops, two questions

JavaScript has two loop forms that look almost the same and answer very different questions. for...of asks “what value comes next?” for...in asks “what property key comes next?” If you remember that one split, most of the lesson becomes predictable.

Real-life analogyBasket or drawer labels?

Use for...of when you want the things: colors, letters, numbers, todo items. Use for...in when you want the labels: property names on an object.

In real life: Taking apples out of a basket
In JavaScript: for...of: each value itself
In real life: Reading labels on a chest of drawers
In JavaScript: for...in: each property name
In real life: A label that says “2”
In JavaScript: Array index keys arrive as strings like "2"

Where the analogy stops: The basket analogy is about what the loop gives you, not how JavaScript stores arrays internally. Arrays also have property keys, which is why for...in can technically walk them.

This lesson finishes the Control flow module. You already met if, switch, and classic for loops with indexes. Now you will choose the loop that matches the data you are visiting, then move on to Declaring & calling functions.

for...of over arrays: values

STEP THROUGH

An array is an ordered list. The Array basics lesson later teaches arrays fully; for now, read ["red", "green", "blue"] as “three values in order.” A for...of loop takes each value out one at a time.

The shape of for...ofPop out in the code editor (opens in a new tab)JavaScript
const colors = ["red", "green", "blue"];for (const color of colors) {  console.log(color);}

The variable in the loop header can be const because JavaScript creates a fresh color binding for each trip through the loop. You are not reassigning the same const; you are getting a new one each time.

Values or keys? Switch the loop
Step 0 of 7Ready
Your turn: follow the blue line

Switch between for...of and for...in. Predict whether the loop variable is a color value or a string key, then step through.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
for (const color of colors) {  console.log(color);}
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
Choose the loop on line 2

Changing the choice starts over. Predict the console output before you step.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Switch the setting to for...in. The same array now prints keys like 0 is a string. That is not wrong JavaScript; it is the wrong question for the job. You asked for property names, not color values.

for...of over strings: characters

INTERACTIVE

Strings are iterable too. That means for...of can ask a string for its next character. This is especially useful with Unicode. The Strings lesson showed that "😀".length is 2; that is a storage detail, not how many visible characters a person sees.

An emoji as a character and as indexes
Step 0 of 7Ready
Your turn: follow the blue line

Step through an emoji with for...of, then with an index loop. The same string produces different visits.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
for (const character of face) {  console.log(character);}for (let i = 0; i < face.length; i++) {  console.log(JSON.stringify(face[i]));}
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Why the emoji appears once

for...of follows the string iterator, which visits code points. A classic index loop visits storage positions. Some symbols, including many emoji, use two positions, so an index loop can expose two halves. A later Unicode lesson goes deeper.

for...in over object keys

STEP THROUGH

Objects are collections of properties: a key such as name points to a value such as "Ada". The Object basics lesson covers this in depth. A for...in loop walks enumerable property keys.

Object key order with for...in
Step 0 of 9Ready
Your turn: follow the blue line

Predict the key order before stepping. The object was written as b, 2, a, 1, but integer-like keys come first.

Running in
  1. script
Next: line 1
Click the blue line to take the next stepPop out in the code editor (opens in a new tab)JavaScript
for (const key in order) {  console.log(key + ":" + order[key]);}
CallStoreChangeResultRun = next line. Ran = already executed.
Recent returnsNothing yet. Start with the blue line.
A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.

Key order is defined for ordinary objects: integer-like keys first in ascending order, then other string keys in insertion order. Every key you receive is still a string. If an object inherits enumerable properties, for...in can see those too, so guard when you only want the object’s own keys.

Own key or inherited key?
Guard inherited keysPop out in the code editor (opens in a new tab)JavaScript
const drawer = Object.create({ inherited: "from prototype" });drawer.name = "Ada";for (const key in drawer) {  if (Object.hasOwn(drawer, key)) {    console.log("own:" + key);  } else {    console.log("inherited:" + key);  }}
Console output
  • own:name
  • inherited:inherited
Try it yourself
Static replay of the real output

The object has one own key, name, and one inherited enumerable key, inherited. Object.hasOwn tells them apart.

This demo uses Object.create only to make inherited enumerable properties visible without changing any real prototype.
The safe guard

Object.hasOwn(object, key) is true only for keys directly on that object. It filters out inherited enumerable keys. You will revisit ownership and enumerability in the Enumerability & ownership lesson.

Why not for...in on arrays?

Arrays are objects too, so for...in can walk their keys. That is exactly why it is a poor default: array keys are strings, and extra enumerable properties can appear.

Real-life analogySeat numbers are not the people

If your job is to greet each person, reading “seat 0, seat 1, seat 2” is a detour. It can even pick up labels that are not seats.

In real life: Greeting people in the seats
In JavaScript: for...of over array values
In real life: Reading the seat numbers
In JavaScript: for...in over array keys
In real life: A sticky note on the aisle
In JavaScript: An extra property such as arr.extra

Where the analogy stops: Sometimes you really do need seat numbers. In JavaScript that is usually a classic index for loop, because the index is a number you control.

Extra properties show the trapPop out in the code editor (opens in a new tab)JavaScript
const colors = ["red", "green"];colors.extra = "paint";for (const key in colors) console.log(key);for (const color of colors) console.log(color);

The for...in loop prints 0, 1, and extra. The for...of loop prints red and green. If you need numeric indexes, use the classic for (let i = 0; i < array.length; i++) pattern from Lesson 6.3. If you need values, use for...of.

Choosing a loop
LoopWhat you getWorks onCan break?Best for
forAn index numberArrays and strings with positionsYesWhen you need index math or exact start/stop control
for...ofA valueIterables: arrays, strings, maps, sets, and more laterYesReading each item in order
for...inA property key stringObjects; also sees enumerable inherited keysYesInspecting object keys carefully
forEachA value passed to a functionArraysNo normal breakShort array actions after the Array basics lesson

Changing something while iterating it

INTERACTIVE

A loop does not freeze the thing it is visiting. If you delete an object property before for...in reaches it, that property will not be visited. If you add properties during the loop, whether they are visited is not something to rely on. With arrays, for...of can continue to newly pushed items, as the bounded example shows.

Mutation while a loop is running
Deleting and adding object keysPop out in the code editor (opens in a new tab)JavaScript
const cart = { keep: 1, remove: 2, later: 3 };for (const key in cart) {  if (key === "keep") {    delete cart.remove;    cart.added = 4;  }  console.log(key);}
Pushing during array for...ofPop out in the code editor (opens in a new tab)JavaScript
const numbers = [1, 2];for (const number of numbers) {  console.log(number);  if (number === 1) numbers.push(3);}
Real output

Object loop:

keep,later

Array loop:

1,2,3

Try it yourself
Static replay of the real output

Deleting remove before it is visited prevents it from printing. Pushing 3 during an array for...of makes this bounded loop continue to the new item.

Added object properties during for...in are not something to rely on; iterate a copy when the object may change.
Safe pattern: loop over a copy

If you plan to add or delete while looping, first make a list of what to visit: for (const key of Object.keys(object)) or for (const item of [...array]). Object.keys, Object.values, and Object.entries get a full lesson later.

entries() with destructuring

INTERACTIVE

Sometimes you need both pieces: the index and the value, or the key and the value. entries() gives pairs. Destructuring is the bracket pattern that unpacks a pair into names.

Pairs with entries()
Array and object entriesPop out in the code editor (opens in a new tab)JavaScript
const colors = ["red", "green"];for (const [index, color] of colors.entries()) {  console.log(index + ":" + color);}const user = { name: "Ada", role: "admin" };for (const [key, value] of Object.entries(user)) {  console.log(key + "=" + value);}
Console output
  • 0:red
  • 1:green
  • name=Ada
  • role=admin
Try it yourself
Static replay of the real output

entries() creates pairs. The [index, color] and [key, value] patterns unpack each pair into two names.

Destructuring gets a full lesson later; here you only need the shape: a pair on the right, two names on the left.

Read for (const [index, color] of colors.entries()) as “for each pair, put the first part in index and the second part in color.” The Destructuring lesson will make this pattern feel normal.

Where you’ll use this

These loops appear in everyday code whenever you read lists, strings, or small configuration objects.

  • Render each item in a menu: for...of over an array of labels.
  • Count characters in a word game: for...of over a string.
  • Validate settings: for...in over an object’s keys, guarded with Object.hasOwn.
  • Show key/value rows: Object.entries with destructuring.
for...of or for...in?
  • Print each color in ["red", "green"]
  • Count letters in a short string
  • Print the keys in { name: "Ada", role: "admin" }
  • Inspect a dictionary that might inherit extra keys
  • Add one to every array index
  • Loop an array that someone gave an extra property
Try it yourself
0 of 6 correct

Sort each task by the loop choice that best matches it. A third bucket catches array cases where for...in is the trap.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Common misconceptions

“for...in means in the array.”

No. It means “in the object’s enumerable keys.” Arrays have keys, but array work usually wants values.

“Array indexes from for...in are numbers.”

They are strings. That is why "0" + 1 becomes "01".

“for...of works on every object.”

Plain object literals are not iterable by default. Use Object.keys, Object.values, or Object.entries when you need iterable lists from them.

“Changing a collection during a loop is always safe.”

It is easy to skip, repeat, or unexpectedly include values. Copy first when you can.

“Key order is random.”

Ordinary object key order is defined, but you should still choose data structures for your intent instead of leaning on key order as a design.

Practice: choose and trace loops

5 EXERCISES
Exercise 1 · Warm-upPredict array keys

Predict what the program prints. Do not write the values a, b, and c; this loop asks for keys.

Starter codePop out in the code editor (opens in a new tab)JavaScript
const letters = ["a", "b", "c"];
for (const key in letters) {
  console.log(key);
}

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeCount vowels with for...of

    Run or trace this vowel counter. What number prints?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const word = "education";
    let count = 0;
    for (const character of word) {
      if ("aeiou".includes(character)) {
        count = count + 1;
      }
    }
    console.log(count);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticePrint key/value pairs

      What is the first line printed by this Object.entries loop?

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const user = { name: "Ada", role: "admin" };
      for (const [key, value] of Object.entries(user)) {
        console.log(key + ": " + value);
      }

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · ChallengeFix the string-key math bug

        The bug prints 01, 11, and 21. Fix it so the displayed positions are 1, 2, and 3.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const items = [10, 20, 30];
        for (const key in items) {
          console.log(key + 1); // prints "01", "11", "21"
        }

        Answer, then press Check. Spacing and letter case don’t matter.

          Exercise 5 · ChallengeSafely delete keys

          Use the copy-first pattern to remove false flags. Which keys remain?

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          const flags = { keep: true, remove: false, later: true };
          for (const key of Object.keys(flags)) {
            if (flags[key] === false) {
              delete flags[key];
            }
          }
          console.log(Object.keys(flags).join(","));

          Answer, then press Check. Spacing and letter case don’t matter.

            Quiz: check your understanding

            7 QUESTIONS

            Predict before you choose. Every option explains why it is right or wrong.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What does for...of give you when looping over an array?

              Choose an answer to see the explanation.

            2. Question 2 of 7What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const colors = ["red", "green"];
              for (const key in colors) {
                console.log(typeof key);
              }

              Choose an answer to see the explanation.

            3. Question 3 of 7Which loop is best for printing every character in "héllo"?

              Choose an answer to see the explanation.

            4. Question 4 of 7What keys does this object loop print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const object = { b: 1, 2: "x", a: 2, 1: "y" };
              for (const key in object) {
                console.log(key);
              }

              Choose an answer to see the explanation.

            5. Question 5 of 7Why is for...in a poor default for arrays?

              Choose an answer to see the explanation.

            6. Question 6 of 7What does this print in current JavaScript engines?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const numbers = [1, 2];
              for (const number of numbers) {
                console.log(number);
                if (number === 1) numbers.push(3);
              }

              Choose an answer to see the explanation.

            7. Question 7 of 7What does .entries() plus destructuring give you?

              Choose an answer to see the explanation.

            Key takeaways

            • for...of loops over values from an iterable, such as an array or string.
            • for...in loops over enumerable property keys, which are strings and may include inherited keys.
            • Avoid for...in as your default array loop; use for...of for values or classic for for indexes.
            • Deleting an object property before for...in reaches it means it is not visited; added properties are not reliable.
            • entries() plus destructuring is the friendly way to get index/value or key/value pairs.

            Remember the one-liner.
            for...of gives values; for...in gives keys.

            Up next: Declaring & calling functions.

            CompleteFrontend Clear concepts. Working examples.