EduFlow
أساسيات JavaScript
⁦0%⁩

الوحدة 2 · المنطق والدوال

حلقات التكرار: for وfor…of وwhile

16 دقراءة 4 دبيئة تجريب الكود

الدرس في لمحة

تكرّر العمل باستخدام for…of وحلقة for العدّادة وwhile، وتتوقّف مبكرًا بـ break وcontinue، وتستخدم نمط المُراكِم (accumulator) لحساب مجموع قائمة مصاريف.

  1. 1قائمة نمرّ عليها
  2. 2for…of: افعل هذا لكل عنصر
  3. 3حلقة for العدّادة
  4. 4while: كرّر حتى يتغيّر شيء ما
  5. 5break وcontinue
  6. 6أن ترى ما تفعله الحلقة

لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.

حتى الآن، كل مثال في Pocket جمع عددًا ثابتًا من المصاريف: coffee + lunch + bus. لكن الإنفاق الحقيقي قائمة تكبر كل يوم، ولا يمكنك كتابة + لعناصر لم تصلك بعد. تحتاج إلى طريقة تقول بها “افعل هذا لكل واحد منها”، وهذه هي حلقة التكرار (loop).

قائمة نمرّ عليها

للقوائم درس كامل في القسم 3، لكنك تحتاج أساسياتها الآن. قائمة القيم بين أقواس مربّعة اسمها مصفوفة (array):

JavaScript
const amounts = [450, 1220, 270, 899];
console.log(amounts.length);  // how many items
console.log(amounts[0]);      // the first item: positions start at 0

for…of: افعل هذا لكل عنصر

تنفّذ for...of كتلتها مرة لكل عنصر، مع اسم تختاره أنت يشير إلى العنصر الحالي:

JavaScript
const amounts = [450, 1220, 270, 899];

let total = 0;
for (const amount of amounts) {
  total += amount;
}
console.log(total);

تتبّعها خطوة بخطوة كما يفعل المحرّك. يبدأ total من 0. الدورة الأولى: amount تساوي 450، فيصبح total يساوي 450. الدورة الثانية: amount تساوي 1220، فيصبح total يساوي 1670. ثم 1940، ثم 2839. لم يتبقَّ أي عنصر، فتنتهي الحلقة ويطبع السطر التالي المجموع.

يُسمّى هذا الشكل نمط المُراكِم (accumulator pattern): صرّح بنتيجة قبل الحلقة، وحدّثها داخلها، واستخدمها بعدها. وهو وراء المجاميع والأعداد والقيم القصوى ومعظم الملخّصات التي ستكتبها. واستخدام const amount سليم مع أنها تتغيّر في كل دورة: فكل دورة تُنشئ amount جديدة، ولا يُعاد إسنادها أبدًا.

وتعمل for...of أيضًا مع النصوص، حرفًا حرفًا: for (const ch of "Pocket").

حلقة for العدّادة

أحيانًا تحتاج إلى الموضع إلى جانب العنصر، مثلًا لتطبع “1. Coffee”. لحلقة for الكلاسيكية ثلاثة أجزاء بين قوسيها:

JavaScript
const labels = ["Coffee", "Lunch", "Bus"];

for (let i = 0; i < labels.length; i++) {
  console.log(`${i + 1}. ${labels[i]}`);
}
تنفّذ حلقة for جزء البداية مرة واحدة، ثم تكرّر: فحص الشرط، ثم تنفيذ الجسم، ثم تنفيذ التحديث، حتى يصبح الشرط خاطئًا فتخرج من الحلقة. let i = 0 i < labels.length? نفّذ الجسم i++ خروج true false مرة واحدة
أجزاء حلقة for الثلاثة وترتيب تنفيذها

جزء البداية، let i = 0، يُنفَّذ مرة واحدة. ثم قبل كل دورة يُفحص الشرط i < labels.length؛ فإن كان صحيحًا نُفّذ الجسم، ثم يضيف التحديث i++ واحدًا إلى i. وحين يصبح الشرط خاطئًا تنتهي الحلقة. مع ثلاث تسميات، تأخذ i القيم 0 و1 و2، وهي الفهارس الصالحة بالضبط.

while: كرّر حتى يتغيّر شيء ما

حلقة while ليس فيها إلا شرط. إنها للحالات التي تعرف فيها متى تتوقّف، لكن لا تعرف كم دورة سيستغرق ذلك. كم يومًا تستطيع أن تشتري قهوة بـ 4.50 دولار بميزانية قدرها 20 دولارًا؟

JavaScript
let balance = 2000;
const coffee = 450;
let days = 0;

while (balance >= coffee) {
  balance -= coffee;
  days++;
}
console.log(`${days} coffees, ${balance} cents left`);

كل دورة تُنقص balance، فيصبح الشرط خاطئًا في النهاية. ولو لم يكن في الجسم ما يقترب من المخرج، كأن تنسى balance -= coffee، لبقي الشرط صحيحًا إلى الأبد. وهذه حلقة لا نهائية (infinite loop): تتجمّد الصفحة، ويعرض عليك المتصفّح في النهاية إيقاف السكربت. وحين يتجمّد تبويب أثناء تدرّبك، ابحث عن حلقة لا يتغيّر شرطها أبدًا.

break وcontinue

كلمتان مفتاحيتان تغيّران مسار الحلقة من داخلها:

  • break تُنهي الحلقة فورًا.
  • continue تتخطّى باقي هذه الدورة وتنتقل إلى التالية.
JavaScript
const amounts = [450, -300, 1220, 270, 899];
const budget = 2000;
let running = 0;

for (const amount of amounts) {
  if (amount < 0) {
    continue; // a refund: not spending, skip it
  }
  running += amount;
  if (running > budget) {
    console.log(`Budget passed at an expense of ${amount}`);
    break; // no need to look further
  }
}
console.log(running);

تتبّعها: يُضاف 450، ثم يُتخطّى -300، ثم يرفع 1220 المجموع إلى 1670، ثم يرفعه 270 إلى 1940، ثم يدفعه 899 إلى 2839، وهذا يتجاوز الميزانية، فتُطبع الرسالة وتتوقّف الحلقة.

أن ترى ما تفعله الحلقة

الحلقات هي المكان الذي يفقد فيه المبتدئون أثر ما يفعله البرنامج أكثر من أي مكان آخر، لأن الأسطر نفسها تُنفَّذ مرات كثيرة بقيم مختلفة. والعلاج أن تجعل الحلقة تُريك. ضع console.log في أول الجسم يطبع كل متغيّر له علاقة:

JavaScript
const amounts = [450, 1220, 270];
let total = 0;
for (let i = 0; i < amounts.length; i++) {
  console.log({ i, amount: amounts[i], totalBefore: total });
  total += amounts[i];
}
console.log("final", total);

كل دورة تطبع سطرًا واحدًا، فتستطيع أن تقارن ما حدث فعلًا بما توقّعته، وتجد أول دورة يختلفان فيها. تلك الدورة هي مكان الخطأ. ووضع القيم داخل { } يطبعها مع أسمائها، فلا تحتاج إلى تخمين أيّ رقم هو أيّ متغيّر. المطوّرون المحترفون يفعلون هذا بالضبط كل يوم؛ وفي القسم 5 ستتعلّم فعله أيضًا باستخدام مصحّح الأخطاء (debugger) في DevTools.

الحلقات والشروط معًا تتيح لك كتابة منطق حقيقي من الآن. ولكي تعيد استخدام هذا المنطق بدلًا من نسخه، تحتاج إلى دوالّك الخاصة، وهذا موضوع الدرس التالي.

أهم النقاط

  • for...of تمرّ على كل عنصر في القائمة بالترتيب، وهي حلقة التكرار الافتراضية لـ “افعل هذا لكل مصروف”.
  • حلقة for العدّادة تعطيك فهرسًا، وتحتاجه حين يهمّ الموضع؛ والفهارس الصالحة تمتد من 0 إلى length - 1.
  • while تكرّر ما دام الشرط صحيحًا، لذلك يجب أن يجعله شيء داخل الحلقة خاطئًا في النهاية.
  • نمط المُراكِم (ابدأ من 0، وأضف داخل الحلقة، واستخدم الناتج بعدها) يحوّل قائمة إلى مجموع واحد.

قراءات إضافية

تحقّق من فهمك

4 أسئلة

السؤال 1 من 4

ماذا يطبع هذا الكود؟

JavaScript
const amounts = [400, 900, 250];
let total = 0;
for (const a of amounts) {
  total = a;
}
console.log(total);
السؤال 2 من 4

const days = ["Mon", "Tue", "Wed"]; أيّ حلقة لا ترمي أي خطأ لكنها تطبع undefined مرة واحدة في النهاية؟

السؤال 3 من 4

متى تكون while أنسب من for...of؟

السؤال 4 من 4

داخل حلقة تمرّ على المصاريف، ماذا تفعل continue؟

تدريب

بيئة تجريب الكود

مُرّ على مصاريف Pocket

تستلم كل دالة amounts، وهي مصفوفة مبالغ بالسنتات، والأرقام السالبة فيها مبالغ مستردّة. أكمل:

  • totalSpent(amounts): مجموع كل الإنفاق، مع تخطّي المبالغ المستردّة.
  • countAtLeast(amounts, minimum): عدد المبالغ الأكبر من minimum أو المساوية له.
  • firstOverBudget(amounts, budget): فهرس المصروف الذي يتجاوز عنده مجموع الإنفاق المتراكم budget لأول مرة (مع تخطّي المبالغ المستردّة)، أو -1 إن لم يتجاوزها أبدًا.
const amounts = [450, -300, 1220, 270, 899];

// Total of all spending; negative amounts are refunds, skip them
function totalSpent(amounts) {
  let total = 0;
  // loop here
  return total;
}

// How many amounts are at least `minimum`
function countAtLeast(amounts, minimum) {
  return 0;
}

// The index of the expense at which the running total of spending
// first goes over `budget`, or -1 if it never does (skip refunds here too)
function firstOverBudget(amounts, budget) {
  return -1;
}

console.log(totalSpent(amounts), countAtLeast(amounts, 500), firstOverBudget(amounts, 2000));
جارٍ تحميل المحرّر…

Ctrl/⌘ + Enter للتشغيل · يعمل في بيئة معزولة داخل متصفّحك. لا شيء يغادر جهازك.

الـ console

(لا مخرجات)

الفحوص

  • totalSpent تجمع الإنفاق وتتخطّى المبالغ المستردّة
  • countAtLeast تعدّ المبالغ التي تساوي الحدّ الأدنى أو تزيد عليه
  • firstOverBudget تُرجع الفهرس الذي تُتجاوز عنده الميزانية لأول مرة
  • firstOverBudget تُرجع -1 حين لا تُتجاوز الميزانية أبدًا