completefrontend
Open lab My progress
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 when you want values and for...in when you want the keys of an object.
  • 02
    Predict tricky outputExplain why array keys are strings, why an emoji can split in two, and what order object keys come in.
  • 03
    Loop safelyAvoid for...in bugs on arrays, and loop over a copy when you change data during a loop.

Two loops, two questions

JavaScript has two loops that look almost the same: for...of and for...in. They answer different questions.

  • for...of asks: "What is the next value?"
  • for...in asks: "What is the next key?"

A key is the name of a property, such as name in { name: "Ada" }. Remember this one split, and most of the lesson is easy to predict.

Real-life analogyApples or drawer labels?

You have a basket of apples and a chest of drawers. To eat the apples, you take each apple out. To find a drawer, you read the labels on the front. One job wants the things. The other job wants the names.

In real life: Take each apple out of a basket
In JavaScript: for...of: you get each value
In real life: Read each label on a chest of drawers
In JavaScript: for...in: you get each key
In real life: A drawer label that says “2”
In JavaScript: An array key is a string, like "2"
The array colors holds three value cards: red, green, and blue. Under each card is its key: the strings 0, 1, and 2. An arrow from for...of points at the cards, the values. An arrow from for...in points at the keys.colorsfor...ofredgreenbluevaluesfor...in"0""1""2"keys
One array, two questions. for...of hands you the cards: red, green, blue. for...in hands you the keys under them: "0", "1", "2", as strings.

This lesson is the last one in the Control flow module. You already know if, switch, and the counting for loop. Here you learn to pick the loop that fits your data. After this lesson, you move on to Declaring & calling functions.

An iterable is a value that can hand out its items one at a time. Arrays and strings are iterables, so for...of works on them. A plain object is not an iterable. It holds named properties, so for...in can ask it for those names.

for...of on arrays: values

STEP THROUGH

An array is a list of values in order. The Array basics lesson covers arrays fully. For now, read ["red", "green", "blue"] as "three values in order". A for...of loop takes out one value at a time.

A for...of loop prints each colorPop out in the code editor (opens in a new tab)JavaScript
const colors = ["red", "green", "blue"];for (const color of colors) {  console.log(color);}

Line 1 makes a list of three colors. Line 2 says: take one value from colors and call it color. Line 3 prints that value. The console shows red, then green, then blue, each on its own line.

The loop variable color can be a const. That is because JavaScript makes a new color variable for each pass through the loop. You never change one const. Each pass gets its own.

Add up three pricesPop out in the code editor (opens in a new tab)JavaScript
const prices = [3, 5, 2];let total = 0;for (const price of prices) {  total = total + price;}console.log(total);

Here price is 3, then 5, then 2. Line 4 adds each price to total. So the program prints 10. This is a common job for for...of: read each value, without caring where it sits in the array.

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 holds a color value or a string key, then step through.

Running in
  1. script
Next: line 1
for (const color of colors) {  console.log(color);}
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose the loop on line 2

Changing the choice starts the replay again. Predict what the console prints 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.

Now switch the setting to for...in. The same array now prints keys, such as 0 is a string. This is still valid JavaScript. But it answers a different question. You asked for keys, not for color values.

for...of on strings: characters

INTERACTIVE

Strings are iterables too. So for...of can ask a string for its next character. This helps with emoji. The Strings lesson showed that "😀".length is 2. That number counts how JavaScript stores the emoji. It is not how many characters a person sees.

Print each letter of a wordPop out in the code editor (opens in a new tab)JavaScript
for (const character of "tea") {  console.log(character);}

The loop asks the string "tea" for one character at a time. It prints t, then e, then a, each on its own line. This works the same for a longer name or a sentence. Spaces count as characters too.

The step-through below uses an emoji. Line 1 stores one emoji in face. Lines 2 and 3 run once and print 😀. Then a counting loop on lines 5 and 6 reads position 0 and position 1. It runs twice. JSON.stringify turns each stored half into text you can read, such as "\ud83d".

For short, plain text, both loops look the same. When you mean characters, for...of is the safer choice.

One emoji: read as a character, then by position
Step 0 of 7Ready
Your turn: follow the blue line

Step through one emoji with for...of, then with a counting loop. The same string gives different results.

Running in
  1. script
Next: line 1
for (const character of face) {  console.log(character);}for (let i = 0; i < face.length; i++) {  console.log(JSON.stringify(face[i]));}
CallStoreChangeResultBlue line: runs next. 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.
Left: for...of reads the emoji as one card, one character. Right: an index loop reads the same emoji as two cards, its two stored halves.for...of😀1 characterindex loop"\ud83d""\ude00"2 halves
The same string, "😀". for...of keeps the emoji whole and gives it to you once. An index loop reads the two slots the emoji is stored in, so it gives you two halves.
Why the emoji appears once

JavaScript stores a string in slots. Most letters take one slot. Many emoji take two slots. A counting loop reads slots, so it can split an emoji into two halves. for...of reads whole characters, so it keeps the two halves together. A later Unicode lesson goes deeper.

for...in on objects: keys

STEP THROUGH

An object holds properties. Each property has a key and a value. For example, the key name can hold the value "Ada". The Object basics lesson covers objects fully. A for...in loop visits the keys of an object, one at a time.

Print each key and its valuePop out in the code editor (opens in a new tab)JavaScript
const profile = { name: "Asha", city: "Delhi" };for (const key in profile) {  console.log(key + ": " + profile[key]);}

On the first pass, key is name. On the second pass, it is city. Line 3 uses square brackets, profile[key], to read the value for the current key. So the program prints name: Asha and then city: Delhi.

The step-through below uses an object with four keys, written in the order b, 2, a, 1. Line 2 puts one key at a time in key. Line 3 uses square brackets to read that key's value. The output is 1:y, 2:x, b:1, and then a:2. You need square brackets here because the key changes on every pass.

The order for...in visits object keys
Step 0 of 9Ready
Your turn: follow the blue line

Predict the key order before you step. The keys were written as b, 2, a, 1, but keys that look like whole numbers come first.

Running in
  1. script
Next: line 1
for (const key in order) {  console.log(key + ":" + order[key]);}
CallStoreChangeResultBlue line: runs next. 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 that order? For a normal object, JavaScript visits keys in a fixed order:

  1. First, keys that look like whole numbers, such as 1 and 2, from smallest to largest.
  2. Then all other keys, in the order they were added.

Every key you get is a string, even "1" and "2".

An object can also be made from a parent object. It can then use the parent's keys as if they were its own. These keys are called inherited keys. The keys that sit directly on the object are its own keys. for...in visits both kinds. So check each key when you only want the object's own keys.

Built-in keys, such as toString, are hidden from loops. You won't see them in a for...in loop. Keys you add yourself are not hidden.

The demo below has one own key and one inherited key. Read the output first. Then turn on the checkbox. It makes the loop skip keys that do not sit directly on drawer. Reset turns the checkbox off again, so you can compare the two outputs.

Own key or inherited key?
Skip 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

The object has one own key, name, and one inherited key, inherited, from its parent. Turn on the checkbox to skip the inherited key.

The demo uses Object.create to give drawer a parent object. That is how it gets an inherited key.
The safe check

Object.hasOwn(object, key) gives true only when the key sits directly on that object. For an inherited key, it gives false. The Enumerability & ownership lesson covers this in more detail.

Print only the object's own keyPop out in the code editor (opens in a new tab)JavaScript
const settings = Object.create({ debug: true });settings.theme = "light";for (const key in settings) {  if (Object.hasOwn(settings, key)) console.log(key);}
The cart settings holds its own key, theme. An arrow shows that settings inherits from a parent box, which holds the key debug. for...in visits both keys. theme is own; debug is inherited.for...in settingsdebug: trueparentinheritedinherits fromtheme: "light"settingsown
theme sits directly on settings, so it is an own key. debug sits on the parent object. for...in visits both. Object.hasOwn(settings, key) is true only for theme.

Line 1 uses Object.create to make settings with a parent object. The parent holds debug. Line 2 adds theme directly to settings. The loop on line 3 visits both keys. The check on line 4 skips debug, so the program prints only theme. Use this check whenever an object might have a parent you did not make yourself.

Why not use for...in on arrays?

An array is a kind of object, so for...in can loop over its keys. But that is a poor choice for arrays, for two reasons:

  • The keys are strings, such as "0", not numbers.
  • Extra properties that someone added to the array show up too.
Real-life analogySeat numbers are not the people

You are in a theater, and your job is to greet each person. Reading out "seat 0, seat 1, seat 2" does not greet anyone. You might even read out a sticky note on the aisle, which is not a seat at all.

In real life: Greet each person in their seat
In JavaScript: for...of gives the array values
In real life: Read each seat number
In JavaScript: for...in gives the array keys
In real life: A sticky note on the aisle
In JavaScript: An extra property, such as colors.extra
An extra property shows up in for...inPop 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);

Line 2 adds an extra property called extra to the array. The for...in loop on line 3 prints 0, 1, and extra. The for...of loop on line 4 prints red and green.

Neither loop prints paint. Line 3 prints the key extra, not its value. Line 4 reads only the array's items, so it skips the extra property. So for...in does run on an array. It is just the wrong tool for a list of items.

  • Need the values? Use for...of.
  • Need the position numbers? Use the counting loop from the Loops lesson: for (let i = 0; i < array.length; i++).

The table compares four ways to loop. Read each row as: "this loop gives me this, works on this, and fits this job."

Four loops compared: what each one gives you and when to use it
LoopWhat the loop variable holdsWhat it works onCan you stop early with break?Use it when
forA number: the position, such as 0.Arrays and strings, which have numbered positions.Yes.You need the position number, or exact control over where to start and stop.
for...ofA value, such as "red".Arrays, strings, and other values that hand out items one by one.Yes.You want to read each item in order.
for...inA key, as a string, such as "name".Objects. It also sees keys the object gets from its parent.Yes.You want the key names of an object.
forEachA value, given to a function you write.Arrays.No. break does not work inside it.You want to run one function for each array item. You'll meet it after the Array basics lesson.

Changing an object or array during a loop

INTERACTIVE

A loop does not freeze the object or array it is looping over. If your code changes it during the loop, the loop can see the change. Here are the rules:

  • Object, for...in: if you delete a property before the loop reaches it, the loop skips it.
  • Object, for...in: if you add a property, the loop may or may not visit it. Do not rely on either.
  • Array, for...of: if you push a new item onto the end, the loop goes on to visit it.

Read the object example in order. The loop visits keep first. Then line 4 deletes remove, and line 7 prints keep. Next the loop visits later. It never prints remove, because that property no longer exists. Line 5 also adds a property, added. This lesson makes no promise about whether the loop visits it.

Changing data while a loop runs
Delete and add object keys during for...inPop 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);}
Push to an array during 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
The real output of both programs

Line 4 deletes remove before the loop reaches it, so remove never prints. In the array loop, pushing 3 makes the loop go on and visit it.

A property added during for...in may or may not be visited. Loop over a copy when the object might change.
Safe pattern: loop over a copy

Do you plan to add or delete during the loop? Then first make a separate list of what to visit, and loop over that list. For an object, use for (const key of Object.keys(object)). For an array, use for (const item of [...array]). Object.keys, Object.values, and Object.entries get a full lesson later.

Loop over a copy of the cartPop out in the code editor (opens in a new tab)JavaScript
const cart = ["tea", "milk"];for (const item of [...cart]) {  console.log(item);  if (item === "tea") cart.push("bread");}console.log(cart.join(","));
Left: the copy made by [...cart] holds tea and milk, and the loop visits these two. Right: the real cart holds tea, milk, and bread, because line 4 adds bread. An arrow shows the copy was made from the cart on line 2.[...cart]teamilkvisitedcopied on line 2teamilkbreadcartline 4 adds bread
Line 2 makes the copy before the loop starts, so the copy holds only tea and milk. Line 4 adds bread to the real cart. The loop never sees it.

On line 2, [...cart] makes a new array with tea and milk. The loop visits this copy. Line 4 adds bread to the real cart, but not to the copy. So the loop prints only tea and milk. The last line prints tea,milk,bread. That proves the real cart did change.

Get two things at once with entries()

INTERACTIVE

Sometimes you need two things on each pass: the position and the value, or the key and the value.

  • colors.entries() gives pairs like [0, "red"]: a position and a value.
  • Object.entries(user) gives pairs like ["name", "Ada"]: a key and a value.

Destructuring unpacks a pair into two named variables. You write the two names inside square brackets: [index, color].

In the array example, the first pair is [0, "red"]. Destructuring puts 0 in index and "red" in color. In the object example, the first pair is ["name", "Ada"]. So the output starts with 0:red, and later it shows name=Ada.

Pairs from entries()
Pairs from an array and from an objectPop 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
The real output of the program

entries() makes pairs. [index, color] and [key, value] unpack each pair into two named variables.

Destructuring gets a full lesson later. For now, remember the shape: a pair on the right, two names on the left.

Read for (const [index, color] of colors.entries()) like this: "for each pair, put the first part in index and the second part in color." The Destructuring lesson covers this in full.

Where you'll use this

You will use these loops whenever you read a list, a string, or a small object of settings.

First ask what you need, then pick the loop:

  • "What is the next menu label?" asks for a value. Use for...of.
  • "Which setting name should I check?" asks for a key. Use for...in with the Object.hasOwn check.
  • Need both the key and the value? Make pairs with Object.entries, and loop over the pairs with for...of.
Print only the menu items you can orderPop out in the code editor (opens in a new tab)JavaScript
const menu = [  { label: "Tea", available: true },  { label: "Cake", available: false },];for (const item of menu) {  if (item.available) console.log(item.label);}

On each pass, item is one menu object. The if on line 6 lets only available items through. So this prints Tea and skips Cake. A web page often does this: it gets a list from a server, then decides which items to show.

  • Show each item in a menu: for...of over an array of labels.
  • Count letters in a word game: for...of over a string.
  • Check settings: for...in over an object's keys, with the Object.hasOwn check.
  • Show rows of keys and values: Object.entries with destructuring.
for...of or for...in?
  • Print each color in ["red", "green"]
  • Count the letters in a short string
  • Print the keys in { name: "Ada", role: "admin" }
  • Check the keys of an object that might get extra keys from a parent
  • Add one to every array position
  • Loop over an array that someone gave an extra property
Try it yourself
0 of 6 correct

Sort each task into the loop that fits it. The third group is for array tasks where for...in causes a bug.

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

Common misconceptions

"for...in means 'each item in the array'."

No. It means "each key in the object". Arrays have keys, but array code usually wants the values.

"The array keys from for...in are numbers."

No, they are strings. That is why "0" + 1 gives "01": + joins the text instead of adding.

"for...of works on every object."

No. A plain object like { name: "Ada" } is not an iterable, so for...of throws an error. Use Object.keys, Object.values, or Object.entries to get an array from it first.

"Changing an object or array during a loop is always safe."

No. The loop can skip items, or visit items you did not expect. When you can, loop over a copy.

"Object keys come out in a random order."

No. The order is fixed: keys that look like whole numbers come first, then other keys in the order they were added. Still, don't build your program around key order. If order matters, use an array.

Practice: choose and trace loops

5 EXERCISES
Exercise 1 · Warm-upPredict the array keys

Predict what the program prints. The answer is not 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 does it print?

    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 keys and values

      What is the first line this Object.entries loop prints?

      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

        This program prints 01, 11, and 21. Fix it so it prints the positions 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 · ChallengeDelete keys safely

          This program loops over a copy of the keys and deletes every flag that is false. Which keys are left?

          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 the answer before you choose. Every choice explains why it is right or wrong.

            For each code question, find the word between the loop variable and the data. of means the variable gets a value. in means it gets a key. Then trace one pass of the loop before you look at the choices.

            Lesson quiz · 7 questionsScore: first tries count
            1. Question 1 of 7What does for...of give you when it loops 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 choice for looping over an array?

              Choose an answer to see the explanation.

            6. Question 6 of 7This loop pushes a new number while it runs. What does it print?

              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() with destructuring give you?

              Choose an answer to see the explanation.

            Key takeaways

            • for...of gives you each value from an iterable, such as an array or a string.
            • for...in gives you each key of an object. Keys are strings, and they can include keys from a parent object.
            • Don't use for...in to loop over an array. Use for...of for values, or a counting for loop for positions.
            • If you delete a property before for...in reaches it, the loop skips it. Added properties may or may not be visited.
            • entries() with destructuring gives you two things at once: position and value, or key and value.

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

            Up next: Declaring & calling functions.

            CompleteFrontend Clear concepts. Working examples.