کاربردهای عملی 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 میگویند:
تحلیل رفتار جاوااسکریپت
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 State | createAccount با balance خصوصی |
| حفظ وضعیت بین فراخوانیها | createClickTracker |
| Function Factory | createMultiplier |
| پیکربندی از پیش برای Callback | createLogger |
ارتباط با سایر مفاهیم
Module Pattern که در این درس دیدیم، پیشزمینهی خوبی برای فهم بهتر Private Fields در Class است که در فصل Object Model پیشرفته بررسی میشود؛ آنجا یک راهحل رسمی و داخل خودِ زبان برای همین مسئلهی حالت خصوصی ارائه میشود. همچنین، الگوی Function Factory ارتباط مستقیمی با مبحث Higher-Order Functions در فصل توابع پیشرفته دارد. در درس بعدی همین فصل، به سراغ رابطهی دقیق Closure و مدیریت حافظه میرویم؛ جایی که خواهیم دید همین قابلیتهای قدرتمند، در صورت بیدقتی، میتوانند منجر به نگهداشتن غیرضروری حافظه هم بشوند.
جمعبندی
Closure در عمل کاربردهای متعددی دارد: ساخت حالت خصوصی که از بیرون قابل دستکاری مستقیم نیست (Private State)، حفظ وضعیت بین چند فراخوانی مجزا از یک تابع، ساخت توابع تخصصی بر اساس یک تنظیم اولیه (Function Factory)، و آمادهسازی Callback هایی با اطلاعات از پیش مشخص. تمام این کاربردها، در نهایت به همان اصل پایه متکی هستند: یک تابع میتواند پس از پایان اجرای تابع بیرونیاش، همچنان به متغیرهای آن دسترسی داشته باشد.
