کاربردهای عملی Closure در کدنویسی واقعی

مقدمه

در درس قبل دیدیم که Closure چگونه شکل می‌گیرد: یک تابع، به همراه دسترسی پایدار به Lexical Environment محل تعریف خودش. اما این ویژگی چه کاربرد عملی‌ای دارد؟ در این درس، مهم‌ترین و رایج‌ترین کاربردهای Closure در کدنویسی واقعی جاوااسکریپت را بررسی می‌کنیم؛ مهم‌ترین آن‌ها ساخت حالت خصوصی (Private State) است.

مشکل: نبود Private بودن واقعی در جاوااسکریپت (پیش از Class)

فرض کنید می‌خواهیم یک شیء بسازیم که یک مقدار داخلی را نگه دارد، اما این مقدار مستقیماً از بیرون قابل دستکاری نباشد؛ فقط از طریق توابع مشخصی بتوان آن را تغییر داد یا خواند. اگر مستقیماً از یک Property معمولی استفاده کنیم:

let account = {
  balance: 1000
};

account.balance = -5000; // هیچ مانعی وجود ندارد!
console.log(account.balance); // -5000

همان‌طور که می‌بینید، هر بخشی از کد می‌تواند مستقیماً balance را به هر مقداری، حتی مقادیر نامعتبر، تغییر دهد. Closure راه‌حلی برای این مشکل ارائه می‌دهد.

ساخت Private State با Closure

ساخت یک حساب بانکی با موجودی خصوصی
خروجی کنسول اینجا نمایش داده می‌شود...

تحلیل کد

در این مثال، createAccount یک شیء برمی‌گرداند که سه متد deposit، withdraw و getBalance دارد. هر سه‌ی این متدها، Closure ای روی متغیر balance تشکیل می‌دهند؛ یعنی به آن دسترسی دارند و می‌توانند آن را بخوانند یا تغییر دهند. اما نکته‌ی کلیدی این‌جاست: balance هرگز مستقیماً به‌عنوان یک Property روی شیء بازگشتی قرار نگرفته است. به همین دلیل، myAccount.balance مقدار undefined برمی‌گرداند؛ چون از بیرون، هیچ راهی برای دسترسی مستقیم به balance وجود ندارد. تنها راه تعامل با آن، از طریق همان سه متدی است که آگاهانه در اختیار گذاشته‌ایم.

این الگو را Module Pattern نیز می‌نامند؛ روشی که پیش از معرفی Class و Private Fields (که در فصل Object Model پیشرفته بررسی خواهیم کرد) رایج‌ترین راه شبیه‌سازی حالت خصوصی در جاوااسکریپت بود.

کاربرد دوم: به‌خاطر سپردن وضعیت بین فراخوانی‌ها

Closure می‌تواند برای نگه‌داری وضعیتی استفاده شود که باید بین چند فراخوانی مجزا از یک تابع، حفظ شود؛ دقیقاً همان چیزی که در مثال شمارنده‌ی درس قبل دیدیم. این الگو در سناریوهایی مثل شمارش تعداد کلیک‌ها یا پیگیری تعداد درخواست‌های ارسال‌شده کاربرد دارد:

function createClickTracker() {
  let clicks = 0;

  return function () {
    clicks = clicks + 1;
    console.log("تعداد کلیک: " + clicks);
  };
}

const trackClick = createClickTracker();

trackClick(); // تعداد کلیک: 1
trackClick(); // تعداد کلیک: 2

کاربرد سوم: تابع‌ساز (Function Factory)

Closure امکان ساخت توابعی را می‌دهد که بر اساس یک مقدار اولیه‌ی مشخص، رفتار متفاوتی داشته باشند؛ به این الگو Function Factory می‌گویند:

ساخت توابع تخصصی با Function Factory
خروجی کنسول اینجا نمایش داده می‌شود...

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

double و triple هر دو از یک تابع واحد (createMultiplier) ساخته شده‌اند، اما هرکدام Closure مستقل خودشان را روی مقدار متفاوتی از factor دارند: double با factor = 2 و triple با factor = 3. این دقیقاً همان اصل «هر فراخوانی، Closure مستقل خودش را می‌سازد» است که در درس قبل دیدیم؛ اما این‌جا از آن به‌طور مفید برای تولید توابع تخصصی استفاده کرده‌ایم.

کاربرد چهارم: پیکربندی از پیش تعیین‌شده برای Callback

Closure اغلب برای «آماده‌سازی» یک Callback با اطلاعات از پیش مشخص، پیش از پاس دادن آن به یک تابع دیگر (مثل یک Event Listener یا متد آرایه) استفاده می‌شود:

function createLogger(prefix) {
  return function (message) {
    console.log("[" + prefix + "] " + message);
  };
}

const errorLogger = createLogger("خطا");
const infoLogger = createLogger("اطلاعات");

errorLogger("فایل پیدا نشد");  // [خطا] فایل پیدا نشد
infoLogger("عملیات موفق بود"); // [اطلاعات] عملیات موفق بود

این الگو در فصل‌های بعدی، به‌ویژه هنگام کار با Event ها در Browser و همچنین در برخی الگوهای Asynchronous، بارها تکرار خواهد شد.

جدول جمع‌بندی کاربردها

کاربردمثال معرفی‌شده
Private StatecreateAccount با balance خصوصی
حفظ وضعیت بین فراخوانی‌هاcreateClickTracker
Function FactorycreateMultiplier
پیکربندی از پیش برای CallbackcreateLogger

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

Module Pattern که در این درس دیدیم، پیش‌زمینه‌ی خوبی برای فهم بهتر Private Fields در Class است که در فصل Object Model پیشرفته بررسی می‌شود؛ آن‌جا یک راه‌حل رسمی و داخل خودِ زبان برای همین مسئله‌ی حالت خصوصی ارائه می‌شود. همچنین، الگوی Function Factory ارتباط مستقیمی با مبحث Higher-Order Functions در فصل توابع پیشرفته دارد. در درس بعدی همین فصل، به سراغ رابطه‌ی دقیق Closure و مدیریت حافظه می‌رویم؛ جایی که خواهیم دید همین قابلیت‌های قدرتمند، در صورت بی‌دقتی، می‌توانند منجر به نگه‌داشتن غیرضروری حافظه هم بشوند.

جمع‌بندی

Closure در عمل کاربردهای متعددی دارد: ساخت حالت خصوصی که از بیرون قابل دستکاری مستقیم نیست (Private State)، حفظ وضعیت بین چند فراخوانی مجزا از یک تابع، ساخت توابع تخصصی بر اساس یک تنظیم اولیه (Function Factory)، و آماده‌سازی Callback هایی با اطلاعات از پیش مشخص. تمام این کاربردها، در نهایت به همان اصل پایه متکی هستند: یک تابع می‌تواند پس از پایان اجرای تابع بیرونی‌اش، همچنان به متغیرهای آن دسترسی داشته باشد.