text: الكائنات المكرَّرة (Iterables) في جافاسكربت - جافاسكريبت - أكاديمية حسوب
  الكائنات المكرَّرة (Iterables) في جافاسكربت
  دليل تعلم جافاسكربت الكائنات المكرَّرة (Iterables) في جافاسكربت
  الكائنات المُكرَّرة (Iterables) هي مفهوم أعمّ من المصفوفات. تتيح لنا هذه الكائنات تحويل أيّ كائن إلى "كائن يمكن تكراره" فيمكننا استعماله في حلقة for..of.
  بالطبع فالمصفوفات يمكن تكرارها، ولكن هناك كائنات أخرى (مضمّنة في أصل اللغة) يمكن تكرارها أيضًا، مثل السلاسل النصية.
  لو لم يكن الكائن مصفوفة تقنيًا، ولكن يمكننا تمثيله على أنّه تجميعة من العناصر (النوع list، والنوع set)، فصياغة for..of ممتازة لنمرّ على عناصره. لذا دعنا نرى كيف يمكن توظيف هذه "المُكرَّرات".
  يمكن لنا أن نُدرك هذا المفهوم -مفهوم المُكرَّرات أعني- بأن نصنع واحدًا بنفسنا. لنقل أنّ لدينا كائن وهو ليس بمصفوفة بأيّ شكل، ولكن يمكن توظيفه لحلقة for..of. مثلًا كائن المدى هذا range يُمثّل مجموعة متتالية من الأعداد.
  // ‫‏نُريد أن تعمل for..of هكذا:
  لنُضيف خاصية التكرار إلى range (فتعمل بهذا for..of)، علينا إضافة تابِع إلى الكائن بالاسم Symbol.iterator (وهو رمز خاصّ في اللغة يتيح لنا هذه الميزة).
  حين تبدأ for..of، تنادي ذلك التابِع مرة واحدة (أو تعرض الأخطاء المعروفة لو لم تجدها). على هذا التابِع إعادة مُكرِّر/iterator، أي كائنًا له التابِع next.
  بعدها، تعمل for..of مع ذلك الكائن المُعاد فقط لا غير.
  حين تحتاج for..of القيمة التالية، تستدعي next()‎ لذاك الكائن.
  يجب أن يكون ناتج next()‎ بالشكل هذا {done: Boolean, value: any}، حيث لو كانت done=true فيعني أن التكرار اكتمل، وإلّا فقيمة value هي التالية.
  إليك النص الكامل لتنفيذ كائن range (مع الملاحظات):
  // ‫‏1. حين ننادي for..of فهي تنادي هذه
  // ‫‏...وتُعيد الكائن المُكرِّر:
  // ‫‏2. بعد ذلك تعمل for..of مع هذا المُكرِّر، طالبةً منه القيم التالية
  // ‫‏3. يُستدعى next()‎ في كلّ كرّة في حلقة for..of
  // ‫‏4. يجب أن يُعيد القيمة كائنًا كهذا {done:.., value :...‎}
  // ‫‏والآن تعمل!
  alert(num); // ‫‏1، ثمّ 2 فَ‍ 3 فَ‍ 4 فَ‍ 5
  لاحظ الميزة الأساس للمُكرَّرات: فصل الاهتمامات.
  عنصر range ذاته ليس له التابِع next()‎.
  بدل ذلك، يُنشأ كائن آخر (أي "المُكرَّر") عند استدعاء range[Symbol.iterator]()‎، وتابِعه next()‎ يُولّد قيم التكرار.
  الخلاصة هي أنّ كائن المُكرَّر منفصل عن الكائن الذي يُكرِّره هذا المُكرَّر.
  نظريًا، يمكننا دمجهما معًا واستعمال كائن range نفسه مُتعدَّدًا لتبسيط الكود أكثر. هكذا تمامًا:
  alert(num); // ‏1، ثمّ 2 فَ‍ 3 فَ‍ 4 فَ‍ 5
  الآن صار يُعيد range[Symbol.iterator]()‎ كائن range نفسه: وهذا الكائن فيه تابِع next()‎ اللازم كما ويتذكّر حالة التعداد الحالية في this.current. الشيفرة أبسط، أجل. وأحيانًا لا بأس به هكذا.
  المشكلة هنا هي استحالة وجود حلقتي for..of تعملان على الكائن في آن واحد، إذ سيتشاركان حالة التعداد؛ فليس هناك إلا مُتعدَّد واحد: الكائن نفسه. لكن أصلًا وجود حلقتي for..of نادر، حتى في العمليات غير المتزامَنة.
  مُكرَّرات لا تنتهي يمكن أيضًا ألا تنتهي المُكرَّرات أبدًا. فمثلًا لا ينتهي المدى range لو صار range.to = Infinity. يمكن أيضًا أن نصنع كائن مُتعدَّد يولّد أعدادًا شبه عشوائية (pseudorandom) لانهائية، ستفيد في حالات حرجة.
  ما من حدود مفروضة على ناتج next. يمكنه إعادة القيم مهما أراد وبالكم الذي أراد، لا مشكلة. طبعًا، المرور على هذا المُكرَّر بحلقة for..of لن ينتهي أبد الدهر، ولكن يمكننا إيقافها باستعمال break.
  السلاسل النصية مُكرَّرة
  تُعدّ المصفوفات والسلاسل النصية أكثر المُكرَّرات المقدَّمة من اللغة استعمالًا. بالنسبة إلى السلاسل النصية، فحلقة for..of تمرّ على محارفها:
  // ‫تتنفّذ أربع مرات: مرة لكلّ محرف
  alert( char ); // ‫t فَ‍ e فَ‍ s فَ‍ t
  كما وتعمل -كما يجب!- مع الأزواج النائبة أو البديلة (Surrogate Pairs)!
  let str = '??';
  alert( char ); // ‫? وثمّ ?
  نداء المُكرَّر جهارة
  لنعرف المُكرَّرات معرفةً أعمق، لنرى كيف يمكن استعمالها جهارةً.
  سنمرّ على سلسلة نصية بنفس الطريقة التي يمرّ بها for..of، ولكن هذه المرة ستكون النداءات مباشرة. تُنشِئ هذه الشيفرة مُكرَّرًا لسلسلة نصية وتأخذ القيم منه "يدويًا":
  // تنفّذ ما تنفّذه
  alert(result.value); // تطبع المحارف واحدًا تلو الآخر
  في الحياة الواقعية، نادرًا ما ستحتاج هذا. لكن المفيد أنّنا نتحكّم أكثر على عملية التكرار موازنةً بِـ for..of. فمثلًا يمكننا تقسيم عملية التكرار: نكرّر قليلًا، نتوقّف ونفعل شيئًا آخر، ثمّ نواصل التكرار.
  المُكرَّرات والشبيهات بالمصفوفات
  هذان المصطلحان الرسميان يبدوان متشابهين إلى حدّ ما، ولكنّهما مختلفين تمام الاختلاف. حاوِل إدراكهما إدراكًا صحيحًا لتتجنّب هذا الاختلاط لاحقًا.
  المُكرَّرات كائنات تُنفّذ التابِع Symbol.iterator، كما شرحنا أعلاه.
  الشبيهات بالمصفوفات كائنات لها فهارس وصفة طول length، وبهذا "تشبه المصفوفات"… المصطلح يشرح نفسه.
  حين نستعمل جافاسكربت للمهام الحقيقية في المتصفحات وغيرها من بيئات، نقابل مختلف الكائنات أكانت مُكرَّرات أو شبيهات بالمصفوفات، أو كليهما معًا.
  السلاسل النصية مثلًا مُكرَّرة (يمكن استعمال for..of عليها)، وشبيهة بالمصفوفات أيضًا (لها فهارس عددية وصفة length). ولكن ليس من الضروري أن يكون المُكرَّر شبيه بالمصفوفة، والعكس صحيح (لا يكون الشبيه بالمصفوفة مُكرَّر). فالمدى range في المثال أعلاه مُكرَّر، ولكنه ليس شبيه بالمصفوفة إذ ليس فيه صفات فهارس وlength.
  إليك كائنًا شبيهًا بالمصفوفات وليس مُكرَّرًا:
  let arrayLike = { // ‫فيه فهارس وطول => شبيه بالمصفوفات
  // ‫خطأ (ما من Symbol.iterator)
  عادةً، لا تكون لا المُكرَّرات ولا الشبيهات بالمصفوفات مصفوفات حقًا، فليس لها push أو pop وغيرها. لكن هذا غير منطقي. ماذا لو كان لدينا كائن من هذا النوع وأردنا التعامل معه بأنه مصفوفة؟ لنقل أنّا سنعمل على range باستعمال توابِع المصفوفات، كيف السبيل؟
  التابِع العام Array.from يأخذ مُكرَّرًا أو شبيهًا بالمصفوفات ويحوّله إلى مصفوفة "فعلية". بعدها ننادي توابِع المصفوفات التي نعرفها عليها.
  هكذا مثلًا:
  alert(arr.pop()); // ‫تكتب World (أيّ أنّ التابِع عمل)
  يأخذ التابِع Array.from في سطر (*) الكائن، ويفحصه أكان مُكرَّرًا أو شبيهًا بالمصفوفات، ويصنع مصفوفة جديدة ينسخ قيم ذلك الكائن فيها.
  ذات الأمر للمُكرَّرات:
  // ‫نأخذ range من المثال أعلاه
  alert(arr); // ‫تكتب 1,2,3,4,5 (تحويل toString للمصفوفة يعمل)
  والصياغة الكاملة للتابِع Array.from تتيح لنا تقديم دالة "خريطة" اختيارية:
  يمكن أن يكون الوسيط الاختياري الثاني mapFn دالةً تُطبّق على كلّ عنصر قبل إضافته للمصفوفة، ويتيح thisArg ضبط ماهيّة this للتابِع.
  // ‫نُربّع كلّ عدد
  هنا نستعمل Array.from لتحويل سلسلة نصية إلى مصفوفة من المحارف:
  // يقسم ‫str إلى مصفوفة من المحارف
  alert(chars[0]); // ?
  alert(chars[1]); // ?
  على العكس من str.split، فهي هنا تعتمد على طبيعة تكراريّة السلسلة النصية، ولهذا تعمل كما ينبغي (كما تعمل for..of) مع الأزواج النائبة.
  هنا أيضًا تقوم بذات الفعل، نظريًا:
  let chars = []; // ‫داخليًا، تُنفّذ Array.from ذات الحلقة
  …ولكن تلك أقصر.
  يمكننا أيضًا صناعة تابِع slice مبني عليها يحترم الأزواج النائبة.
  let str = '???';
  alert( slice(str, 1, 3) ); // ??
  // ‫التابِع الأصيل/native في اللغة لا يدعم الأزواج النائبة
  alert( str.slice(1, 3) ); // ‫يُولّد نصّ "قمامة" (قطعتين من أزواج نائبة مختلفة)
  تُدعى الكائنات التي يمكن استعمالها في for..of بالمُكرَّرات (Iterables).
  على المُكرَّرات (تقنيًا) تنفيذ التابِع بالاسم Symbol.iterator.
  يُدعى ناتج obj[System.iterator]‎ بالمُكرَّر. يتعامل المُكرَّر بعملية التكرار.
  يجب أن يحتوي المُكرَّر التابِع بالاسم next()‎ حيث يُعيد كائن {done: Boolean, value: any}… تُشير done:true هنا بأنّ التكرار اكتمل، وإلّا فَـ value هي القيمة التالية.
  تُنادي الحلقة for..of التابِع Symbol.iterator تلقائيًا عند تنفيذها، ولكن يمكننا أيضًا فعل ذلك يدويًا.
  تُنفّذ المُكرَّرات المضمّنة في اللغة Symbol.iterator (مثل السلاسل النصية والمصفوفات).
  مُكرَّر السلاسل النصية يفهم الأزواج البديلة.
  تُدعى الكائنات التي فيها صفات فهارس وصفة طول length بالشبيهات بالمصفوفات. يمكن أيضًا أن تكون لها صفات وتوابِع أخرى، إلّا أنّ ليس فيها توابِع المصفوفات المضمّنة في بنية اللغة.
  لو نظرنا ورأينا مواصفات اللغة، فسنرى بأنّ أغلب التوابِع المضمّنة فيها تتعامل مع المصفوفات على أنّها مُكرَّرات أو شبيهات بالمصفوفات بدل أن تكون مصفوفات "حقيقية"؛ هكذا تصير أكثر تجرّديّة (abstract).
  تصنع Array.from(obj[, mapFn, thisArg])‎ مصفوفةً Array حقيقية من المُكرَّر أو الشبيه بالمصفوفات obj، بهذا يمكن استعمال توابِع المصفوفات عليها. يتيح لنا الوسيطين mapFn وthisArg تقديم دالة لكلّ عنصر من عناصرها.