مقدمه
به فصل «کار با ماژولها» خوش آمدید. با بزرگتر شدن پروژههای جاوااسکریپت، سازماندهی کد به یک چالش
بزرگ تبدیل میشود. در مدل سنتی، وقتی چندین فایل اسکریپت را در یک صفحه HTML قرار میدهیم، تمام
متغیرها و توابعی که در سطح اصلی آن فایلها تعریف میشوند، به حوزه سراسری (global scope)،
یعنی شیء window، اضافه میشوند.
این موضوع مشکلی به نام «آلودگی حوزه سراسری» (Global Scope Pollution) را ایجاد میکند. اگر دو
فایل اسکریپت مختلف، یک متغیر یا تابع با نام یکسان (مثلاً `init` یا `count`) تعریف کنند، دومی روی
اولی بازنویسی خواهد شد و منجر به باگهای غیرمنتظره و سختی در دیباگ میشود. برای حل این مشکل،
توسعهدهندگان الگوهایی را برای ایجاد کدهای ماژولار ابداع کردند.
راه حل: الگوی ماژول (Module Pattern)
«الگوی ماژول» یکی از قدیمیترین و مهمترین الگوهای طراحی در جاوااسکریپت است. هدف این الگو، ایجاد
قطعات کد مستقل و قابل استفاده مجدد (ماژول) است که دارای متغیرها و توابع خصوصی (private) و یک
رابط عمومی (public API) برای تعامل با دنیای بیرون باشند. این الگو مفهوم «کپسولهسازی»
(Encapsulation) را در جاوااسکریپت شبیهسازی میکند.
استفاده از IIFE و Closure
الگوی ماژول بر دو مفهوم کلیدی جاوااسکریپت استوار است:
- IIFE (Immediately Invoked Function Expression): یک عبارت تابعی که بلافاصله پس از
تعریف، اجرا میشود. این کار یک حوزه (scope) جدید و ایزوله ایجاد میکند و از نشت متغیرها به
حوزه سراسری جلوگیری میکند.
- Closure: این مکانیزم به توابع داخلی اجازه میدهد تا به متغیرهای تعریف شده در تابع
بیرونی خود دسترسی داشته باشند، حتی پس از اینکه اجرای تابع بیرونی به پایان رسیده باشد.
با ترکیب این دو، میتوانیم متغیرها و توابعی را در داخل IIFE تعریف کنیم که از بیرون غیرقابل دسترس
هستند (خصوصی) و سپس یک شیء را برگردانیم که شامل ارجاع به آن دسته از توابع داخلی است که میخواهیم
به صورت عمومی در دسترس باشند.
پیادهسازی یک ماژول ساده
بیایید با یک مثال کلاسیک، یعنی یک ماژول شمارنده، این الگو را در عمل ببینیم.
JAVASCRIPT
const counterModule = (function() {
let privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
};
})();
counterModule.increment();
counterModule.increment();
console.log(counterModule.value());
console.log(counterModule.privateCounter);
در این کد، تابع بیرونی یک IIFE است که بلافاصله اجرا میشود. متغیر privateCounter و تابع
changeBy در داخل این IIFE تعریف شده و کاملاً خصوصی هستند. این تابع یک شیء برمیگرداند.
متدهای موجود در این شیء (increment، decrement و value) به دلیل مکانیزم closure، به متغیرها
و توابع خصوصی داخل IIFE دسترسی دارند. این شیء بازگشتی، رابط عمومی ماژول ماست و تنها راه تعامل با
آن از خارج است.
الگوی ماژول آشکارساز (Revealing Module Pattern)
این الگو یک نسخه بهبودیافته و تمیزتر از الگوی ماژول است. تفاوت اصلی این است که تمام متدها و
پراپرتیها در ابتدا به صورت خصوصی تعریف میشوند. سپس در انتهای ماژول، شیء بازگشتی به صورت یک
نگاشت (mapping) از نامهای عمومی به اعضای خصوصی که میخواهیم «آشکار» (reveal) شوند، عمل میکند.
JAVASCRIPT
const revealingCounter = (function () {
let privateCounter = 0;
function privateIncrement() {
privateCounter++;
}
function privateValue() {
return privateCounter;
}
return {
increment: privateIncrement,
value: privateValue
};
})();
revealingCounter.increment();
console.log(revealingCounter.value());
مزیت این الگو در خوانایی بیشتر است. با یک نگاه به شیء بازگشتی در انتهای ماژول، میتوان به سرعت
فهمید که کدام بخشها عمومی و کدام خصوصی هستند. این الگو انتخاب بسیاری از توسعهدهندگان برای
پیادهسازی ماژولها در جاوااسکریپت سنتی بوده است.
در این درس با الگوی کلاسیک ماژول به عنوان روشی برای سازماندهی کد، ایجاد کپسولهسازی و جلوگیری از
آلودگی حوزه سراسری آشنا شدیم. این الگو پایه و اساس بسیاری از کتابخانهها و فریمورکهای
جاوااسکریپت بوده است. با این حال، امروزه جاوااسکریپت سیستم ماژول بومی خود را (ES Modules) دارد.
برای بارگذاری و مدیریت این ماژولهای بومی و وابستگیهایشان در مرورگر، از ابزارها و الگوهایی
استفاده میشود که در درس بعدی به بررسی آنها خواهیم پرداخت.