حل

حلقات `for` في JavaScript

30 تمرينًا

نبذة عن حلقات `for`

الصياغة العامة

تُعدّ حلقة for من أكثر العبارات شيوعًا لتنفيذ بعض المنطق بشكل متكرر. في JavaScript، تتكوّن من الكلمة المفتاحية for، و_رأس_ محاط بالأقواس الهلالية، وكتلة كود محاطة بالأقواس المعقوفة وتحتوي على جسم الحلقة.

for (initialization; condition; step) {
  // code that is executed repeatedly as long as the condition is
  // true
}

الرأس

عادةً ما تُعِدّ التهيئة متغير عداد، ويتحقق الشرط مما إذا كان ينبغي متابعة الحلقة أو إيقافها، وتزيد الخطوة العداد في نهاية كل تكرار. تُفصل أجزاء الرأس عن بعضها بفواصل منقوطة.

const list = ['a', 'b', 'c'];
for (let i = 0; i < list.length; i++) {
  // code that should be executed for each item in the array
}

أجزاء الرأس الثلاثة كلها اختيارية. وهذا نادرًا ما يُستخدم عمليًا. يمكنك قراءة المزيد عن هذا في توثيق MDN.

حلقات for المتداخلة

يمكن تداخل حلقات for، مثلًا للتنقل عبر المصفوفات المتداخلة (متعددة الأبعاد). احرص على إعداد متغير عداد مختلف لكل حلقة.

const coords = [
  [1, 2],
  [4, 7],
  [10, -3],
];

for (let i = 0; i < coords.length; i++) {
  for (let j = 0; j < coords[i].length; j++) {
    // do something with coords[i][j]
  }
}

break وcontinue والتسميات

داخل جسم الحلقة، يمكنك استخدام الكلمة المفتاحية break لإيقاف تنفيذ الحلقة تمامًا. في المقابل، الكلمة المفتاحية continue توقف تنفيذ التكرار الحالي فقط وتنتقل إلى التكرار التالي.

عند العمل مع الحلقات المتداخلة، تنطبق break وcontinue افتراضيًا على الحلقة الأعمق دائمًا. يمكنك استخدام التسميات لتغيير هذا السلوك. التسمية هي اسم معرّف يتبعه نقطتان. تُوضع أمام الحلقة (أو فوقها). يمكن بعد ذلك دمج هذه التسمية مع break أو continue لتحديد الحلقة التي ينبغي أن تنطبق عليها العبارة.

outer: for (let i = 0; i < 10; i++) {
  for (let j = 0; j < 10; j++) {
    // ...
    if (j > 5) {
      continue outer;
    }
    // ...
  }
}

عداد الحلقة والكود غير المتزامن

عليك أن تكون حريصًا على تعريف متغير العداد بشكل صحيح عند التعامل مع الكود غير المتزامن. لنلقِ نظرة على مثال نجمع فيه متغير العداد بشكل غير متزامن باستخدام setTimeout بعد انتظار ثانية واحدة. عند تعريف العداد باستخدام var أو باستخدام let لكن خارج الرأس، لا يعطي الكود النتيجة المتوقعة.

let sum = 0;
for (var i = 1; i < 4; i++) {
  setTimeout(function () { sum += i; }, 1000);
}
// eventually sum will be 12 instead of 6 (1+2+3)
let sum = 0;
let i;
for (i = 1; i < 4; i++) {
  setTimeout(function () { sum += i; }, 1000);
}
// same here, eventually sum will be 12 instead of 6 (1+2+3)

في هاتين الحالتين، لا يقتصر نطاق i على تكرار واحد بعينه من الحلقة. بحلول الوقت الذي يُحسب فيه المجموع، يكون i قد بلغ قيمته النهائية 4، ما يجعل المجموع 4 + 4 + 4 = 12.

يمكن تجنّب هذه المشكلة بتعريف العداد باستخدام let داخل الرأس. ولهذا تأثير خاص: يحصل كل تكرار على متغيّره الخاص let i الذي يقتصر نطاقه على تلك المرة الواحدة من تنفيذ جسم الحلقة. لذلك عندما يُحسب المجموع لاحقًا، تشير كل دالة callback إلى متغيرها الخاص i الذي ما زال يحمل القيمة الصحيحة.

let sum = 0;
for (let i = 1; i < 4; i++) {
  setTimeout(function () { sum += i; }, 1000);
}
// eventually sum will be 6 (1+2+3) as expected
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة

تعلّم حلقات `for`