حلقه‌ها و تکرار

حلقه‌های for، while و do while، دستورات break و continue، حلقه‌های تو در تو و مفاهیم Iterable و Iterator در جاوا اسکریپت

حلقه چیست؟

حلقه (Loop) ابزاری برای تکرار یک قطعه کد است. به‌جای نوشتن چندین بار یک دستور، از حلقه استفاده می‌کنیم:

// بدون حلقه:
console.log(1);
console.log(2);
console.log(3);
// ... خسته‌کننده!

// با حلقه:
for (let i = 1; i <= 3; i++) {
    console.log(i);
}

حلقه for

پرکاربردترین حلقه — سه بخش دارد: مقداردهی اولیه، شرط و افزایش:

// for (مقداردهی; شرط; گام)
for (let i = 0; i < 5; i++) {
    console.log(i); // 0 1 2 3 4
}

// شمارش معکوس:
for (let i = 5; i > 0; i--) {
    console.log(i); // 5 4 3 2 1
}

// گام‌های سفارشی:
for (let i = 0; i <= 20; i += 5) {
    console.log(i); // 0 5 10 15 20
}

حلقه while

تا زمانی که شرط برقرار باشد تکرار می‌کند. شرط قبل از اجرا بررسی می‌شود:

let count = 1;

while (count <= 3) {
    console.log("شمارش:", count);
    count++;
}
// شمارش: 1
// شمارش: 2
// شمارش: 3

// خطر: اگر شرط هرگز false نشود → حلقه بی‌نهایت!
// let n = 0;
// while (n < 10) {
//     console.log(n); // فراموش کردیم n را زیاد کنیم!
// }

حلقه do while

مثل while است اما شرط بعد از اجرا بررسی می‌شود — یعنی بلاک حداقل یک بار اجرا می‌شود:

let num = 10;

do {
    console.log(num); // 10 (یک بار اجرا شد)
    num++;
} while (num < 3); // شرط false ولی یک بار اجرا شد!

// مثال کاربردی — تا کاربر عدد درست وارد کند:
let guess;
do {
    guess = 7; // در واقعیت: از کاربر بگیرید
} while (guess !== 7);
console.log("درست حدس زدید!");

حلقه for...of

برای پیمایش مقادیر یک شیء تکرارپذیر (آرایه، رشته، Map، Set و...):

// روی آرایه:
const fruits = ["سیب", "موز", "پرتقال"];
for (const fruit of fruits) {
    console.log(fruit);
}

// روی رشته (حرف به حرف):
for (const char of "سلام") {
    console.log(char); // س ل ا م
}

// روی Map:
const colors = new Map([
    ["red", "قرمز"],
    ["blue", "آبی"]
]);
for (const [en, fa] of colors) {
    console.log(en, "=", fa);
}

حلقه for...in

برای پیمایش کلیدها (نام ویژگی‌های) یک شیء:

const user = {
    name: "علی",
    age: 25,
    city: "تهران"
};

for (const key in user) {
    console.log(key, ":", user[key]);
}
// name : علی
// age : 25
// city : تهران

// هشدار: for...in کلیدهای ارث‌بری‌شده را هم می‌گیرد
// برای آرایه از for...of استفاده کنید نه for...in!

break — توقف حلقه

اجرای حلقه را کاملاً متوقف می‌کند:

// پیدا کردن اولین عدد زوج:
const numbers = [1, 3, 5, 8, 10, 7];

for (const num of numbers) {
    if (num % 2 === 0) {
        console.log("اولین زوج:", num); // 8
        break; // دیگر ادامه نده
    }
    console.log("فرد:", num);
}

continue — پرش از تکرار

تکرار فعلی را رها می‌کند و به تکرار بعدی می‌رود:

// چاپ فقط اعداد فرد:
for (let i = 1; i <= 10; i++) {
    if (i % 2 === 0) {
        continue; // زوج‌ها را رد کن
    }
    console.log(i); // 1 3 5 7 9
}

حلقه‌های تو در تو

حلقه داخل حلقه — برای کار با داده‌های دوبعدی (مثل جدول یا ماتریس):

// جدول ضرب 3×3:
for (let i = 1; i <= 3; i++) {
    let row = "";
    for (let j = 1; j <= 3; j++) {
        row += (i * j) + "\t";
    }
    console.log(row);
}

// پیمایش ماتریس:
const matrix = [
    [1, 2, 3],
    [4, 5, 6]
];

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

// نکته: break فقط داخلی‌ترین حلقه را می‌شکند
// برای شکستن هر دو، از label استفاده کنید:
outer: for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
        if (i * j > 2) break outer;
        console.log(i, j);
    }
}

تکرار روی آرایه‌ها

راه‌های مختلف پیمایش آرایه:

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

// 1) for کلاسیک — دسترسی به index:
for (let i = 0; i < items.length; i++) {
    console.log(i, items[i]);
}

// 2) for...of — فقط مقدار:
for (const item of items) {
    console.log(item);
}

// 3) entries() — هم index هم مقدار:
for (const [index, item] of items.entries()) {
    console.log(index, item);
}

Iterable — تکرارپذیر

شیئی که قابل پیمایش با for...of است Iterable نامیده می‌شود. این اشیا متد Symbol.iterator دارند:

// آرایه یک Iterable است:
const arr = [1, 2, 3];
console.log(typeof arr[Symbol.iterator]); // "function"

// ساخت یک Iterable سفارشی:
const myIterable = {
    [Symbol.iterator]() {
        let step = 0;
        return {
            next() {
                step++;
                if (step <= 3) {
                    return { value: step * 10, done: false };
                }
                return { done: true };
            }
        };
    }
};

for (const val of myIterable) {
    console.log(val); // 10 20 30
}

Iterator — پیمایشگر

شیئی که متد next() دارد و آبجکتی با value و done برمی‌گرداند:

const arr = ["الف", "ب", "پ"];
const iterator = arr[Symbol.iterator]();

console.log(iterator.next()); // { value: "الف", done: false }
console.log(iterator.next()); // { value: "ب", done: false }
console.log(iterator.next()); // { value: "پ", done: false }
console.log(iterator.next()); // { value: undefined, done: true }

Iterator Protocol

قراردادی که شیء باید رعایت کند تا قابل پیمایش باشد:

// یک Iterator دستی:
function createRangeIterator(start, end) {
    let current = start;
    return {
        next() {
            if (current <= end) {
                return { value: current++, done: false };
            }
            return { value: undefined, done: true };
        }
    };
}

const it = createRangeIterator(1, 3);
console.log(it.next()); // { value: 1, done: false }
console.log(it.next()); // { value: 2, done: false }
console.log(it.next()); // { value: 3, done: false }
console.log(it.next()); // { done: true }

Async Iterator

پیمایشگر ناهمگام — برای داده‌هایی که با تأخیر می‌آیند (مثل درخواست‌های شبکه). متد next() یک Promise برمی‌گرداند:

// ساخت Async Iterator:
async function* fetchPages() {
    let page = 1;
    while (page <= 2) {
        // در واقعیت: const res = await fetch(`/api/page/${page}`);
        const data = await new Promise(resolve => {
            setTimeout(() => resolve(`صفحه ${page}`), 100);
        });
        yield data;
        page++;
    }
}

// پیمایش با for await...of:
(async () => {
    for await (const page of fetchPages()) {
        console.log(page); // صفحه 1، صفحه 2
    }
})();

Async Iterable

شیئی که Symbol.asyncIterator دارد و با for await...of پیمایش می‌شود:

const asyncIterable = {
    [Symbol.asyncIterator]() {
        let i = 0;
        return {
            async next() {
                if (i < 3) {
                    await new Promise(r => setTimeout(r, 50));
                    return { value: i++, done: false };
                }
                return { done: true };
            }
        };
    }
};

(async () => {
    for await (const num of asyncIterable) {
        console.log(num); // 0 1 2
    }
})();

کدام حلقه را انتخاب کنیم؟

حلقهکاربرد
forتعداد تکرار مشخص، نیاز به index
whileتکرار تا برقراری شرط (تعداد نامشخص)
do...whileحداقل یک بار اجرا لازم است
for...ofپیمایش مقادیر Iterable (آرایه، رشته، Map...)
for...inپیمایش کلیدهای شیء
for await...ofپیمایش Async Iterable (داده‌های ناهمگام)

جمع‌بندی

این مبحث بخشی از دوره آموزش کامل جاوا اسکریپت است.

ابزارهای مرتبط