Symbol.iterator در عمل: از کلاس‌ها تا Spread و Destructuring

در درس قبل با مفهوم کلی Iterable و Iterator آشنا شدید و دیدید که Symbol.iterator کلید اصلی قابل‌پیمایش‌شدن یک شیء است. در این درس، تمرکز روی خود Symbol.iterator است: این مقدار دقیقاً چیست، چرا از یک Symbol برای این منظور استفاده شده، و چگونه می‌توان آن را در کلاس‌ها پیاده‌سازی کرد تا نمونه‌های آن با ابزارهایی مثل عملگر spread و destructuring نیز به‌طور خودکار سازگار شوند.

چرا از Symbol به‌جای یک نام رشته‌ای استفاده شده؟

Symbol.iterator یکی از چند Well-Known Symbol است که موتور جاوااسکریپت به‌صورت داخلی تعریف کرده است. استفاده از Symbol به‌جای یک نام رشته‌ای مثل "iterator" دلیل مشخصی دارد: مقدار Symbol در سراسر برنامه یکتاست و هرگز با نام ویژگی‌های دیگر یک شیء، حتی به‌صورت تصادفی، تداخل پیدا نمی‌کند:

console.log(typeof Symbol.iterator); // "symbol"

const obj = {
  iterator: "این فقط یک ویژگی معمولی است",
  [Symbol.iterator]: function () {
    // این متد واقعی پروتکل Iterable است
  },
};

اگر جاوااسکریپت از یک نام رشته‌ای ساده مثل "iterator" استفاده می‌کرد، احتمال تداخل با ویژگی‌های دلخواه توسعه‌دهندگان بسیار بالا می‌رفت. Symbol این ریسک را کاملاً حذف می‌کند.

پیاده‌سازی Symbol.iterator در یک کلاس

یکی از کاربردهای رایج Symbol.iterator، تعریف آن به‌عنوان یک متد در کلاس‌هاست تا نمونه‌های آن کلاس مستقیماً با for...of قابل پیمایش شوند:

کلاس قابل پیمایش با Symbol.iterator
خروجی کنسول اینجا نمایش داده می‌شود...

نکته‌ی مهم این است که پیاده‌سازی Symbol.iterator در یک کلاس، فقط for...of را فعال نمی‌کند، بلکه نمونه‌های آن کلاس را با تمام ابزارهایی که به‌طور داخلی به دنبال Iterable می‌گردند، سازگار می‌کند.

ابزارهایی که به‌طور پنهان از Symbol.iterator استفاده می‌کنند

بسیاری از قابلیت‌های آشنای جاوااسکریپت، پشت صحنه دقیقاً از همین پروتکل استفاده می‌کنند، نه یک مکانیزم جداگانه:

const myPlaylist = new Playlist(["آهنگ اول", "آهنگ دوم"]);

// عملگر spread
const songsArray = [...myPlaylist];
console.log(songsArray); // ["آهنگ اول", "آهنگ دوم"]

// Destructuring
const [first, second] = myPlaylist;
console.log(first); // "آهنگ اول"

// تبدیل به آرایه‌ی واقعی
const converted = Array.from(myPlaylist);

جدول ابزارهای وابسته به Symbol.iterator

ابزار نیازمند Symbol.iterator
for...of بله
عملگر spread (...) بله
Destructuring آرایه‌ای بله
Array.from بله (یا یک شیء array-like)
حلقه‌ی for...in خیر؛ این حلقه به Symbol.iterator وابسته نیست

نکات کلیدی

  • Symbol.iterator یک Well-Known Symbol است که به دلیل یکتا بودن، از تداخل نام با ویژگی‌های دیگر جلوگیری می‌کند.
  • پیاده‌سازی Symbol.iterator در یک کلاس، نمونه‌های آن را با for...of و بسیاری از ابزارهای دیگر سازگار می‌کند.
  • عملگر spread، destructuring آرایه‌ای، و Array.from همگی پشت صحنه از همین پروتکل استفاده می‌کنند.
  • حلقه‌ی for...in با Symbol.iterator ارتباطی ندارد و بر پایه‌ی مکانیزم متفاوتی کار می‌کند.

جمع‌بندی

Symbol.iterator نه‌تنها پایه‌ی نظری پروتکل Iterable است، بلکه در عمل، دروازه‌ی ورود یک شیء دلخواه به دنیای ابزارهای بومی جاوااسکریپت مثل spread، destructuring و Array.from محسوب می‌شود. پیاده‌سازی آن در کلاس‌های سفارشی، امکان می‌دهد ساختارهای داده‌ی خودتان دقیقاً مثل آرایه یا Map، به‌طور طبیعی در کد جاوااسکریپت رفتار کنند. در درس بعدی، Generator بررسی می‌شود که نوشتن همین نوع Iterator را به‌طور چشمگیری ساده‌تر می‌کند.