شیءهای حلقهپذیر تعمیمی از آرایهها هستند. این مفهومی است که به ما اجازه میدهد تا هر شیءای را در حلقه for..of قابل استفاده کنیم.
قطعا آرایهها حلقهپذیر هستند. اما شیءهای درونساخت دیگری هم هستند که حلقهپذیرند. برای مثال، رشتهها هم حلقهپذیرند.
اگر یک شیء به طور فنی آرایه نباشد، اما یک مجموعه (لیست یا دسته) از چیزها را نشان دهد، for..of یک سینتکس عالی برای حلقهزدن درون آن است، پس بیایید ببینیم چگونه چنین کاری را انجام دهیم.
ویژگی Symbol.iterator
ما میتوانیم به راحتی مفهوم حلقهپذیرها را با ایجاد حلقهپذیر خودمان درک کنیم.
برای مثال، ما یک شیء داریم که آرایه نیست، اما برای استفاده در for..of مناسب است.
مانند یک شیء range که بازهای از اعداد را نشان میدهد:
let range = {
from: 1,
to: 5
};
// :کار کند for..of میخواهیم که
// for(let num of range) ... num=1,2,3,4,5
برای اینکه شیء range را حلقهپذیر کنیم (و به این ترتیب بگذاریم for..of کار کند) ما نیاز داریم که یک متد به اسم Symbol.iterator را به شیء اضافه کنیم ( یک سمبل خاص درون ساخت که فقط برای این کار است).
- زمانی که
for..ofشروع میشود، متد را یک بار صدا میزند (یا اگر پیدا نشود ارور میدهد). متد باید یک حلقهزننده را برگرداند – شیءای که متدnextرا دارد. - همینطور رو به جلو،
for..ofتنها با شیء برگردانده شده کار میکند. - زمانی که
for..ofمقدار بعدی را نیاز دارد، روی آن شیءnext()را صدا میزند. - نتیجه
next()باید به شکل{done: Boolean, value: any}باشد کهdone=trueبه معنی پایان حلقهزدن است، در غیر این صورتvalueمقدار بعدی خواهد بود.
اینجا پیادهسازی کامل range را به همراه ملاحظات داریم:
let range = {
from: 1,
to: 5
};
// 1. در ابتدا این متد صدا زده میشود for..of با صدازدن
range[Symbol.iterator] = function() {
// :این متد شیء حلقهزننده را برمیگرداند...
// 2. فقط با این حلقهزننده کار میکند، که از آن مقدار بعدی را درخواست میکند for..of ،همینطور رو به جلو
return {
current: this.from,
last: this.to,
// 3. فراخوانی میشود for..of در هر دور حلقه توسط next()
next() {
// 4. برگرداند {done:..., value:...} این متد باید مقدار را به عنوان یک شیء
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
}
};
};
// !حالا کار میکند
for (let num of range) {
alert(num); // 1, then 2, 3, 4, 5
}