أتقن البروتوكولات التي تقف خلف for...of والنشر والتفكيك، ثم استخدم المولدات لإنتاج تسلسلات كسولة.
افتح هذا الدرس في Kodokonيحكم بروتوكولان متمايزان التكرار. يكون الكائن قابلا للتكرار (iterable) إذا كشف عن دالة تحت مفتاح Symbol.iterator؛ يجب أن تعيد هذه الدالة مكررا (iterator)، أي كائنا له دالة next() تعيد { value, done }. كل ما يستهلك التسلسلات - for...of، معامل النشر ...، التفكيك، Array.from، Promise.all - يمر عبر هذين البروتوكولين. لذا يمكنك جعل أي كائن متوافقا.
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 يعلق التنفيذ ويحفظ الحالة المحلية للدالة. يتيح لك هذا التقييم الكسول نمذجة تسلسلات لا نهائية، ما دام مستهلك يحد من عدد القيم المطلوبة.
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 معلقة - المكان المثالي لتحرير مورد (ملف، اتصال، قفل).
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, cleanuplength وفهارس رقميةSymbol.iterator تعيد كائنا له next()Array.prototypenext() مباشرةfor...of بـ break بينما تستهلك مولدا؟throw() على المكررreturn()، الذي يشغل كتل finally في المولدnext()