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
- ā¢For...of Loop
- ā¢For...in Loop
- ā¢Comparison: for...of vs for...in
- ā¢Iterating Arrays
- ā¢Iterating Objects
- ā¢Iterating Strings
- ā¢Iterating Maps and Sets
- ā¢Common Patterns
- ā¢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
| Type | What it iterates |
|---|---|
| Array | Elements |
| String | Characters |
| Map | [key, value] pairs |
| Set | Values |
| arguments | Function arguments |
| NodeList | DOM nodes |
| TypedArray | Buffer 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' ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
| Feature | for...of | for...in |
|---|---|---|
| Returns | Values | Keys (strings) |
| Works on | Iterables | Objects |
| Arrays | ā Values | ā ļø Indices as strings |
| Objects | ā Not directly | ā Property names |
| Order | Guaranteed | Not guaranteed |
| Inherited | No | Yes (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
| Feature | for...of | for...in |
|---|---|---|
| Purpose | Iterate values | Iterate keys |
| Best for | Arrays, Strings, Maps, Sets | Objects |
| Returns | Values directly | Property names (strings) |
| Order | Guaranteed | Not guaranteed |
| Inherited | N/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)