Docs

README

4.5 For...of and For...in Loops

Overview

JavaScript provides two specialized loop constructs for iterating over data structures:

  • •for...of - Iterates over iterable values (arrays, strings, Maps, Sets, etc.)
  • •for...in - Iterates over enumerable property keys (primarily for objects)

Table of Contents

  1. •For...of Loop
  2. •For...in Loop
  3. •Comparison: for...of vs for...in
  4. •Iterating Arrays
  5. •Iterating Objects
  6. •Iterating Strings
  7. •Iterating Maps and Sets
  8. •Common Patterns
  9. •Best Practices

For...of Loop

The for...of statement iterates over iterable objects (objects with Symbol.iterator), accessing the values directly.

Syntax

for (const value of iterable) {
  // use value
}

Supported Iterables

TypeWhat it iterates
ArrayElements
StringCharacters
Map[key, value] pairs
SetValues
argumentsFunction arguments
NodeListDOM nodes
TypedArrayBuffer values

Basic Examples

// Array
const colors = ['red', 'green', 'blue'];
for (const color of colors) {
  console.log(color);
}
// "red", "green", "blue"

// String
const word = 'hello';
for (const char of word) {
  console.log(char);
}
// "h", "e", "l", "l", "o"

Why Use for...of?

const fruits = ['apple', 'banana', 'cherry'];

// āŒ Traditional for loop (verbose)
for (let i = 0; i < fruits.length; i++) {
  console.log(fruits[i]);
}

// āœ… for...of (clean and readable)
for (const fruit of fruits) {
  console.log(fruit);
}

For...in Loop

The for...in statement iterates over enumerable properties of an object, accessing the keys (property names).

Syntax

for (const key in object) {
  // use key and object[key]
}

Basic Example

const person = {
  name: 'John',
  age: 30,
  city: 'NYC',
};

for (const key in person) {
  console.log(`${key}: ${person[key]}`);
}
// "name: John"
// "age: 30"
// "city: NYC"

Important: Inherited Properties

const parent = { inherited: true };
const child = Object.create(parent);
child.own = 'property';

for (const key in child) {
  console.log(key); // "own", "inherited"
}

// Filter to own properties only
for (const key in child) {
  if (child.hasOwnProperty(key)) {
    console.log(key); // Only "own"
  }
}

Comparison: for...of vs for...in

ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│                    for...of vs for...in                  │
ā”œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¤
│                                                          │
│  for...of                        for...in                │
│  ────────                        ────────                │
│  • Iterates VALUES               • Iterates KEYS         │
│  • For iterables                 • For objects           │
│  • Arrays, Strings, Maps         • Object properties     │
│  • Does NOT work on objects      • Includes inherited    │
│                                                          │
│  const arr = ['a', 'b'];         const obj = {x: 1};    │
│  for (const v of arr)            for (const k in obj)   │
│    console.log(v);                 console.log(k);      │
│  // 'a', 'b'                     // 'x'                 │
│                                                          │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
Featurefor...offor...in
ReturnsValuesKeys (strings)
Works onIterablesObjects
Arraysāœ… Valuesāš ļø Indices as strings
ObjectsāŒ Not directlyāœ… Property names
OrderGuaranteedNot guaranteed
InheritedNoYes (unless filtered)

Quick Demonstration

const arr = ['a', 'b', 'c'];

console.log('for...of (values):');
for (const value of arr) {
  console.log(value); // "a", "b", "c"
}

console.log('for...in (keys):');
for (const key in arr) {
  console.log(key); // "0", "1", "2" (strings!)
}

Iterating Arrays

for...of (Recommended for Arrays)

const numbers = [10, 20, 30];

// Just values
for (const num of numbers) {
  console.log(num); // 10, 20, 30
}

// With entries() to get index + value
for (const [index, value] of numbers.entries()) {
  console.log(`${index}: ${value}`);
}
// 0: 10
// 1: 20
// 2: 30

Why NOT to use for...in with Arrays

const arr = ['a', 'b', 'c'];
arr.customProperty = 'extra';

// for...in includes non-index properties!
for (const key in arr) {
  console.log(key); // "0", "1", "2", "customProperty"
}

// for...of only iterates values
for (const value of arr) {
  console.log(value); // "a", "b", "c"
}

Iterating Objects

for...in (For Objects)

const user = {
  name: 'Alice',
  age: 25,
  email: 'alice@example.com',
};

for (const key in user) {
  console.log(`${key}: ${user[key]}`);
}

Object Methods as Alternatives

const person = { name: 'Bob', age: 30 };

// Object.keys() with for...of
for (const key of Object.keys(person)) {
  console.log(key); // "name", "age"
}

// Object.values() with for...of
for (const value of Object.values(person)) {
  console.log(value); // "Bob", 30
}

// Object.entries() with for...of
for (const [key, value] of Object.entries(person)) {
  console.log(`${key}: ${value}`);
}

Iterating Strings

const text = 'Hello';

// for...of iterates characters
for (const char of text) {
  console.log(char);
}
// "H", "e", "l", "l", "o"

// With index using entries() on spread array
for (const [i, char] of [...text].entries()) {
  console.log(`${i}: ${char}`);
}
// 0: H
// 1: e
// 2: l
// 3: l
// 4: o

// for...in on string (not recommended)
for (const index in text) {
  console.log(index, text[index]);
}
// "0" "H", "1" "e", "2" "l", "3" "l", "4" "o"

Unicode Support

const emoji = 'šŸ‘‹šŸŒ';

// for...of handles multi-byte characters correctly
for (const char of emoji) {
  console.log(char); // "šŸ‘‹", "šŸŒ"
}

// Traditional for loop breaks them
for (let i = 0; i < emoji.length; i++) {
  console.log(emoji[i]); // Broken surrogate pairs
}

Iterating Maps and Sets

Map

const userRoles = new Map([
  ['admin', 'Full access'],
  ['editor', 'Edit content'],
  ['viewer', 'View only'],
]);

// Iterate entries (default)
for (const [role, description] of userRoles) {
  console.log(`${role}: ${description}`);
}

// Iterate keys
for (const role of userRoles.keys()) {
  console.log(role);
}

// Iterate values
for (const description of userRoles.values()) {
  console.log(description);
}

Set

const uniqueNumbers = new Set([1, 2, 3, 2, 1]);

for (const num of uniqueNumbers) {
  console.log(num); // 1, 2, 3 (duplicates removed)
}

Common Patterns

1. Filtering During Iteration

const numbers = [1, 2, 3, 4, 5, 6];

for (const num of numbers) {
  if (num % 2 === 0) {
    console.log(`${num} is even`);
  }
}

2. Breaking Early

const items = ['apple', 'banana', 'cherry', 'date'];

for (const item of items) {
  if (item === 'cherry') {
    console.log('Found cherry!');
    break;
  }
}

3. Skipping Items

const values = [1, null, 3, undefined, 5];

for (const value of values) {
  if (value == null) continue;
  console.log(value); // 1, 3, 5
}

4. Summing Values

const prices = [10.99, 5.99, 15.99, 3.99];
let total = 0;

for (const price of prices) {
  total += price;
}
console.log(`Total: $${total.toFixed(2)}`);

5. Transforming Object to Array

const scores = { math: 90, science: 85, history: 88 };
const results = [];

for (const [subject, score] of Object.entries(scores)) {
  results.push({ subject, score, grade: score >= 90 ? 'A' : 'B' });
}
console.log(results);

6. Nested Iteration

const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
];

for (const row of matrix) {
  for (const cell of row) {
    console.log(cell);
  }
}

7. Async Iteration

async function processItems(items) {
  for (const item of items) {
    await processItem(item); // Sequential processing
  }
}

Best Practices

1. Use for...of for Arrays

const items = ['a', 'b', 'c'];

// āœ… Preferred
for (const item of items) {
  console.log(item);
}

// āŒ Avoid for arrays
for (const index in items) {
  console.log(items[index]);
}

2. Use for...in with hasOwnProperty

const obj = { a: 1, b: 2 };

// āœ… Safe iteration
for (const key in obj) {
  if (Object.prototype.hasOwnProperty.call(obj, key)) {
    console.log(key, obj[key]);
  }
}

// āœ… Or use Object.keys()
for (const key of Object.keys(obj)) {
  console.log(key, obj[key]);
}

3. Use const for Loop Variables

// āœ… Use const when not reassigning
for (const item of items) {
  console.log(item);
}

// Use let only if you need to reassign
for (let item of items) {
  item = item.toUpperCase(); // Reassignment
  console.log(item);
}

4. Prefer Object Methods Over for...in

const data = { x: 1, y: 2, z: 3 };

// āœ… Clearer intent
Object.keys(data).forEach((key) => console.log(key));
Object.values(data).forEach((val) => console.log(val));
Object.entries(data).forEach(([k, v]) => console.log(k, v));

5. Don't Use for...of on Plain Objects

const obj = { a: 1, b: 2 };

// āŒ Error: obj is not iterable
for (const value of obj) {
  console.log(value);
}

// āœ… Use Object methods
for (const value of Object.values(obj)) {
  console.log(value);
}

Summary

Featurefor...offor...in
PurposeIterate valuesIterate keys
Best forArrays, Strings, Maps, SetsObjects
ReturnsValues directlyProperty names (strings)
OrderGuaranteedNot guaranteed
InheritedN/A (values only)Includes unless filtered

Quick Reference

// for...of - values from iterables
for (const value of array) {
}
for (const char of string) {
}
for (const [key, val] of map) {
}

// for...in - keys from objects
for (const key in object) {
}

// Object methods with for...of
for (const key of Object.keys(obj)) {
}
for (const val of Object.values(obj)) {
}
for (const [k, v] of Object.entries(obj)) {
}

Next Steps

  • •Practice with the examples in examples.js
  • •Complete the exercises in exercises.js
  • •Learn about error handling with try-catch
  • •Explore array iteration methods (map, filter, reduce)
README - JavaScript Tutorial | DeepML