توابع به‌عنوان مقدار: First-Class Functions و Higher-Order Functions

مقدمه

در طول این دوره، بارها توابعی را دیدیم که به متغیر نسبت داده می‌شدند، یا به‌عنوان Callback به متدهایی مثل map و filter پاس داده می‌شدند. در این درس، مبنای نظری این رفتارها را با دو مفهوم کلیدی روشن می‌کنیم: First-Class Functions و Higher-Order Functions. این دو مفهوم، ستون اصلی سبک برنامه‌نویسی تابعی (Functional Programming) در جاوااسکریپت هستند.

First-Class Functions یعنی چه؟

وقتی می‌گوییم توابع در جاوااسکریپت شهروند درجه‌یک (First-Class) هستند، منظور این است که با تابع دقیقاً مانند هر مقدار دیگری (عدد، رشته، شیء) رفتار می‌شود. به‌طور مشخص، این ویژگی به معنای موارد زیر است:

  • می‌توان یک تابع را در یک متغیر ذخیره کرد.
  • می‌توان یک تابع را به‌عنوان آرگومان به تابع دیگری پاس داد.
  • می‌توان یک تابع را از داخل تابع دیگری return کرد.
  • می‌توان یک تابع را داخل یک آرایه یا Property یک شیء قرار داد.
تابع مانند هر مقدار دیگری رفتار می‌کند
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل کد

در این مثال، تابع greet بدون هیچ محدودیتی داخل یک متغیر، یک عضو آرایه و یک Property شیء قرار گرفته است؛ دقیقاً همان‌طور که می‌شد این کار را با عدد 25 یا رشته‌ی "سلام" انجام داد. این‌که چنین کاری اصلاً ممکن است، دقیقاً همان چیزی است که First-Class بودن توابع به آن اشاره دارد. لازم به یادآوری است که وقتی storedFunction = greet نوشته می‌شود، خودِ تابع فراخوانی نمی‌شود (چون پرانتز ندارد)؛ فقط ارجاع به آن کپی می‌شود.

Higher-Order Function چیست؟

حالا که می‌دانیم توابع را می‌توان مثل هر مقدار دیگری جابه‌جا کرد، به یک مفهوم مبتنی بر همین ویژگی می‌رسیم: Higher-Order Function به تابعی گفته می‌شود که حداقل یکی از این دو شرط را داشته باشد:

  • یک یا چند تابع دیگر را به‌عنوان آرگومان دریافت کند.
  • یک تابع را به‌عنوان مقدار بازگشتی خودش برگرداند.

توجه کنید که Higher-Order Function خودش دقیقاً به همان دلیل که در جاوااسکریپت توابع First-Class هستند، اصلاً امکان‌پذیر است؛ بدون First-Class Functions، مفهوم Higher-Order Function اصلاً معنایی نداشت.

مثال‌هایی که قبلاً دیده‌ایم

در واقع، در طول این دوره، بارها با Higher-Order Function سروکار داشته‌ایم بدون این‌که نام رسمی آن را بدانیم:

// map یک Higher-Order Function است، چون تابع می‌گیرد
let doubled = [1, 2, 3].map(function (num) {
  return num * 2;
});

// createMultiplier هم Higher-Order است، چون تابع برمی‌گرداند
function createMultiplier(factor) {
  return function (number) {
    return number * factor;
  };
}

map یک Higher-Order Function است چون یک تابع (Callback) را به‌عنوان آرگومان می‌پذیرد. createMultiplier که در فصل Closure دیدیم نیز یک Higher-Order Function است، چون یک تابع دیگر را return می‌کند.

نوشتن یک Higher-Order Function ساده

برای درک بهتر، بیایید یک Higher-Order Function ساده از صفر بنویسیم:

نوشتن یک Higher-Order Function از صفر
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل رفتار جاوااسکریپت

تابع repeat نمی‌داند و اهمیتی هم نمی‌دهد که Callback ارسالی دقیقاً چه کاری انجام می‌دهد؛ فقط وظیفه دارد آن را به تعداد مشخصی فراخوانی کند و شماره‌ی تکرار فعلی را به آن پاس دهد. این جداسازی -یعنی جدا کردن «چگونگی تکرار» از «چه کاری در هر تکرار انجام شود»- دقیقاً همان قدرت اصلی Higher-Order Functions است: می‌توان با تغییر فقط Callback، رفتار کاملاً متفاوتی از همان تابع repeat گرفت، بدون این‌که خودِ repeat را تغییر دهیم.

چرا این مفهوم اهمیت دارد؟

Higher-Order Functions به ما اجازه می‌دهند کد را به بخش‌های کوچک‌تر و قابل استفاده‌ی مجدد تقسیم کنیم. به‌جای نوشتن حلقه‌های تکراری برای هر سناریوی خاص (مثلاً یک حلقه برای دوبرابر کردن اعداد، یک حلقه‌ی دیگر برای فیلتر کردن)، می‌توان یک تابع عمومی نوشت که رفتار دقیق آن، از طریق Callback مشخص می‌شود. این دقیقاً همان چیزی است که متدهای map، filter و reduce که در فصل آرایه دیدیم، انجام می‌دهند.

جدول جمع‌بندی

مفهومتوضیح
First-Class Functionsتوابع مانند هر مقدار دیگری قابل ذخیره، انتقال و بازگشت هستند
Higher-Order Functionتابعی که تابع دیگری را می‌پذیرد یا برمی‌گرداند
پیش‌نیازHigher-Order Function فقط به دلیل First-Class بودن توابع ممکن است

ارتباط با سایر مفاهیم

مفهوم First-Class Functions همان چیزی است که در فصل توابع مبانی، هنگام بررسی Function Expression، به‌طور غیرمستقیم دیدیم. در درس‌های بعدی همین فصل، با Callback، Pure Functions و Function Composition آشنا می‌شویم که همگی مستقیماً بر پایه‌ی همین دو مفهوم بنا شده‌اند.

جمع‌بندی

First-Class Functions به این معناست که در جاوااسکریپت، توابع مانند هر مقدار دیگری قابل ذخیره در متغیر، پاس دادن به تابع دیگر، یا بازگرداندن از یک تابع هستند. Higher-Order Function به تابعی گفته می‌شود که از این ویژگی استفاده می‌کند: یا تابعی را به‌عنوان آرگومان می‌پذیرد، یا تابعی را بازمی‌گرداند. متدهایی مثل map و filter که پیش‌تر دیدیم، نمونه‌های عملی و پرکاربرد Higher-Order Functions هستند.