Kodokon kodokon.com

المكررات والمولدات وبروتوكولات التكرار

أتقن البروتوكولات التي تقف خلف for...of والنشر والتفكيك، ثم استخدم المولدات لإنتاج تسلسلات كسولة.

9 دقيقة · 3 أسئلة

افتح هذا الدرس في Kodokon

يحكم بروتوكولان متمايزان التكرار. يكون الكائن قابلا للتكرار (iterable) إذا كشف عن دالة تحت مفتاح Symbol.iterator؛ يجب أن تعيد هذه الدالة مكررا (iterator)، أي كائنا له دالة next() تعيد { value, done }. كل ما يستهلك التسلسلات - for...of، معامل النشر ...، التفكيك، Array.from، Promise.all - يمر عبر هذين البروتوكولين. لذا يمكنك جعل أي كائن متوافقا.

JAVASCRIPT
const range = {
  from: 1,
  to: 3,
  [Symbol.iterator]() {
    let current = this.from;
    const last = this.to;
    return {
      next() {
        return current <= last
          ? { value: current++, done: false }
          : { value: undefined, done: true };
      }
    };
  }
};

console.log([...range]); // [1, 2, 3]
قابل للتكرار مكتوب يدويا، دون صنف أو مولد.

كتابة المكررات يدويا مطولة. المولدات (function*) تنتج مكررا تلقائيا: كل yield يعلق التنفيذ ويحفظ الحالة المحلية للدالة. يتيح لك هذا التقييم الكسول نمذجة تسلسلات لا نهائية، ما دام مستهلك يحد من عدد القيم المطلوبة.

JAVASCRIPT
function* naturals() {
  let n = 0;
  while (true) yield n++;
}

function* take(iterable, count) {
  let index = 0;
  for (const value of iterable) {
    if (index >= count) return;
    yield value;
    index += 1;
  }
}

console.log([...take(naturals(), 4)]); // [0, 1, 2, 3]
تسلسل لا نهائي يستهلك دون أن يحجب أبدا.

تكشف مكررات المولدات أيضا عن return() وthrow(). تفصيلة في المواصفة كثيرا ما تمر دون انتباه: عندما تغادر حلقة for...of بـ break أو return أو باستثناء، يستدعي المحرك تلقائيا iterator.return(). في المولد، يشغل ذلك أي كتل finally معلقة - المكان المثالي لتحرير مورد (ملف، اتصال، قفل).

JAVASCRIPT
function* readLines() {
  try {
    yield "line 1";
    yield "line 2";
  } finally {
    console.log("cleanup");
  }
}

for (const line of readLines()) {
  console.log(line);
  break; // triggers return(), hence the finally
}
// line 1, cleanup
break يستدعي return(): تعمل كتلة finally.

اختبار المعرفة

تأكّد من أنك تذكّرت النقاط الأساسية في هذا الدرس.

  1. ما الذي يجعل الكائن قابلا للتكرار بمعنى البروتوكول؟
    • امتلاك خاصية length وفهارس رقمية
    • الكشف عن دالة Symbol.iterator تعيد كائنا له next()
    • الوراثة من Array.prototype
    • امتلاك دالة next() مباشرة
  2. ماذا يحدث عندما تخرج من حلقة for...of بـ break بينما تستهلك مولدا؟
    • لا شيء خاص، يبقى المولد معلقا عند آخر yield
    • يستدعي المحرك throw() على المكرر
    • يستدعي المحرك return()، الذي يشغل كتل finally في المولد
    • يعيد المولد البدء من البداية في التكرار التالي
  3. لماذا يمكنك العمل مع مولد لا نهائي دون حجب البرنامج؟
    • يحد المحرك تلقائيا من عدد التكرارات
    • تعمل المولدات على خيط منفصل
    • التقييم كسول: تحسب كل قيمة فقط عند استدعاء next()
    • القيم محسوبة مسبقا ومخزنة مؤقتا في المحرك