تکرار کننده‌ها (Iterators) در JavaScript

گردآوری و تالیف : عرفان کاکایی
تاریخ انتشار : 22 مرداد 1397
دسته بندی ها : جاوا اسکریپت

مواقع زیادی پیش می‌آیند که خود را در معرض کار با آرایه‌ها می‌بینید. اما نگران این مسئله نیستید؛ زیرا این کار را قبلا هم انجام داده‌اید و سخت هم نبوده است. همچنین گزینه‌های زیادی دارید. مثلا می‌توانید از حلقه‌های معمولی، map، reduce و filter، و یا iteratorها استفاده کنید.

Iterator یک الگوی طراحی است که ما را قار می‌سازد تا یک لیست یا مجموعه را بپیماییم. در JavaScript، این موارد به عنوان آبجکت پیاده‌سازی می‌شوند. قبل از این که به جزئیات وارد شویم، در اینجا یک مثال ساده را می‌بینید:

const arr = [1, 2, 3, 4, 5];

for (const num of arr) {
  console.log(num);
}

با استفاده از حلقه for…of، می‌توانید بر روی هر آبجکتی که پروتکل iterable را پیاده‌سازی می‌کند، iterate کنید.

پروتکل Iterable

برای پیروی از این پروتکل، آبجکت مورد نظر باید متد خاصی به نام @@iterator را تعریف کند، که هیچ آرگومانی نمی‌گیرد و یک آبجکت را بر می‌گرداند. این آبجکت برگردانده شده، خود باید از پروتکل iterator پیروی کند.

پروتکل Iterator

برای پیروی از این پروتکل، آبجکت مورد نظر باید متدی به نام next را تعریف کند، که خودش آبجکتی با دو ویژگی را بر می‌گرداند:

  1. Value: آیتم فعلی در تکرار (iteration)
  2. Done: یک مقدار boolean، که وضعیت کامل شدن یا نشدن تکرار را نشان می‌دهد. done=true یعنی این که تکرار کامل شده است.

آرایه، رشته، Map و Set از پروتکل iterator پیروی می‌کنند.

پیاده‌سازی پروتکل‌ها

در اینجا، تابعی می‌بینید که یک آبجکت iterable را بر می‌گرداند، که ما را قادر می‌سازد تا بر روی اولین عدد طبیعی n، iterate کنیم:

function numbers(till = 100) {
  let i = 0;
  const iteratorFx = () => {
    const iterator = {
      next() {
        i += 1;
        if (i <= till) {
          return { done: false, value: i };
        }
        return { done: true };
      },
    };
    return iterator;
  };
  return {
    [Symbol.iterator]: iteratorFx,
  };
}

const numbersTill10 = numbers(10);
for (const i for numbersTill10) {
  // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
}

متد @@iterator فقط یک بار و در ابتدای حلقه for…of فراخوانی می‌شود. پس کد زیر نیز مشابه مورد بالا است:

for (const i for numbers(10)) {
  // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
}

از آنجایی که ما می‌دانیم این آبجکت متدی به نام next دارد که جزئیات تکرار را پیاده‌سازی می‌کند، می‌توانیم به سادگی خودمان این متد را فراخوانی کنیم.

const numbersTill5 = number(5);
numbersTill5.next(); // { done: false, value : 1 }
numbersTill5.next(); // { done: false, value : 2 }
numbersTill5.next(); // { done: false, value : 3 }
numbersTill5.next(); // { done: false, value : 4 }
numbersTill5.next(); // { done: false, value : 5 }
numbersTill5.next(); // { done: true }

این می‌تواند با انتقال مقادیر به متد next، برای کنترل خارجی تکرار استفاده شود.

می‌توانیم به صورت بالا، iteratorهای سفارشی خود را پیاده‌سازی کنیم. گرچه، JavaScript راه دیگری برای ساخت iterableها نیز فراهم کرده است.

مولدها

مولدها توابع خاصی هستند که وقتی فراخوانی می‌شوند، آبجکتی به نام Generator را بر می‌گردانند. آبجکت generator از پروتکل‌های تکرار پیروی می‌کند. پس برای پیاده‌سازی مثال بالا با استفاده از مولدها، به این صورت عمل می‌کنیم:

function* generateNumbers(till = 100) {
  let i = 1;
  while (i <= till) {
    yield i;
    i += 1;
  }
}

const numbersTill10 = generateNumbers(10); // iterator

مقدار ارسال شده توسط yield، (که در اینجا i است) مقدار ذخیره شده در آبجکت بازگردانده شده توسط متد next خواهد بود، و وقتی که مولد کار خود را تمام می‌کند، مقدار { done: true } را بر می‌گرداند.

از مثال بالا کاملا مشخص است که مولدها راه مختصری برای ساخت iterableها فراهم می‌کنند. آن‌ها پروتکل‌ها را چکیده‌سازی می‌کنند، و ما فقط نیاز است که نگران منطق تکرار باشیم.

نتیجه گیری

از آنجایی که این پست را با پیموندن آرایه‌ها شروع کردیم، بهتر است مثالی شامل آرایه‌ها نیز داشته باشیم. آرایه‌ها به طور پیشفرض iterable هستند، پس ما یک value mapper می‌سازیم که iterable باشد:

function* mapOver(arr, mapper = (v) => v) {
  for (let i = 0; i < arr.length; i += 1) {
    yield mapper(arr[i]);
  }
}

const twices = mapOver([...numbers(5)], (v) => v + 2);
for (const num of twices) {
  // 2, 4, 6, 8, 10
}

منبع

مقالات پیشنهادی

تکرار کردن یک سطر در لاراول

امروز میخواهم در مورد ویژگی در لاراول صحبت کنم که به اصطلاح "hidden" است ، یعنی این قابلیت در سیستم لاراول وجود دارد اما در documentation لاراول چیزی...

15 کتابخانه جالب javascript و css

ماموریت ما در راکت این است که شما را بر اساس تکنولوژی روز طراحی وب به روز نگه داریم . به همین خاطر هم هست که ما تقریبا ماهانه یا چند هفته در میان پستی...

15 کتابخانه جالب javascript و css دی ۹۵

ماموریت ما در راکت این است که شما را بر اساس تکنولوژی روز طراحی وب به روز نگه داریم . به همین خاطر هم هست که ما تقریبا ماهانه یا چند هفته در میان پستی...

با استفاده از Billboardjs.js، نمودارهای داده‌ای بر پایه JavaScript بسازید

گرافیک و ویژگی‌های بصری، نقش حیاتی‌ای در پیشرفت محتویات وب بازی می‌کنند. با فناوری وب مدرن، اضافه کردن ویژگی‌های بصری سفارشی مانند آیکون‌های SVG در صف...