WeakMap و WeakSet: مدیریت حافظهی هوشمند برای کلیدهای شیئی
در درس قبل با Map و Set آشنا شدید که کلیدها یا مقادیر خود را تا زمانی که بهطور دستی حذف نشوند، در حافظه نگه میدارند. اما این رفتار در برخی سناریوها میتواند مشکلساز شود؛ بهخصوص وقتی کلید یک Map، یک شیء DOM یا شیئی است که ممکن است در جای دیگری از برنامه دیگر مورد استفاده نباشد. WeakMap و WeakSet برای دقیقاً همین سناریو طراحی شدهاند.
مشکل Garbage Collection با Map معمولی
جاوااسکریپت بهطور خودکار حافظهی مربوط به شیءهایی را که دیگر از هیچ نقطهای از برنامه قابل دسترسی نیستند، آزاد میکند؛ فرآیندی به نام Garbage Collection. اما اگر یک شیء بهعنوان کلید در یک Map معمولی ذخیره شده باشد، حتی اگر تمام ارجاعات دیگر به آن شیء از بین بروند، وجود آن بهعنوان کلید در Map مانع از پاکشدن آن از حافظه میشود:
const cache = new Map();
let userSession = { id: 1024 };
cache.set(userSession, "دادهی نشست کاربر");
userSession = null; // تلاش برای رها کردن ارجاع
// اما شیء اصلی همچنان در حافظه باقی میماند،
// چون Map هنوز آن را بهعنوان کلید نگه داشته است
console.log(cache.size); // 1
در برنامههای بزرگ که تعداد زیادی شیء موقت ساخته میشود، این رفتار میتواند بهمرور باعث نشتی حافظه (memory leak) شود، حتی اگر برنامهنویس آن شیء را دیگر لازم نداشته باشد.
WeakMap: کلیدهایی که مانع Garbage Collection نمیشوند
WeakMap دقیقاً مانند Map عمل میکند، با یک تفاوت اساسی: کلیدهای آن بهصورت ضعیف (weak) نگهداری میشوند. یعنی اگر هیچ ارجاع دیگری به آن شیء در برنامه وجود نداشته باشد، Garbage Collector مجاز است آن را از حافظه پاک کند، حتی اگر همچنان بهعنوان کلید در WeakMap ثبت شده باشد:
const cache = new WeakMap();
let userSession = { id: 1024 };
cache.set(userSession, "دادهی نشست کاربر");
console.log(cache.get(userSession)); // "دادهی نشست کاربر"
userSession = null;
// اکنون شیء اصلی، در صورت نبود هیچ ارجاع دیگری،
// میتواند توسط Garbage Collector آزاد شود
محدودیتهای مهم WeakMap
این رفتار خاص، محدودیتهای مشخصی نیز به همراه دارد که WeakMap را از Map کاملاً متمایز میکند:
- کلیدهای WeakMap فقط میتوانند شیء باشند؛ استفاده از رشته، عدد یا هر مقدار primitive دیگر بهعنوان کلید، خطا تولید میکند.
- WeakMap ویژگی
sizeندارد و Iterable هم نیست؛ به همین دلیل هیچ راهی برای پیمایش یا شمارش کلیدهای آن وجود ندارد. - متدهای موجود محدود به
get،set،hasوdeleteهستند.
این محدودیتها اتفاقی نیستند؛ از آنجا که Garbage Collector میتواند در هر لحظه یک کلید را حذف کند، اگر پیمایش یا شمارش مجاز بود، نتیجهی آن در هر لحظه غیرقابلپیشبینی میشد.
کاربرد رایج: نگهداری دادهی خصوصی مرتبط با یک شیء
یکی از کاربردهای عملی WeakMap، نگهداری اطلاعاتی است که باید فقط تا زمان وجود یک شیء مشخص (مثلاً یک عنصر DOM) معتبر باشد:
WeakSet: نسخهی ضعیف Set
WeakSet همان رابطهای را با Set دارد که WeakMap با Map دارد: فقط میتواند شیء نگه دارد (نه مقادیر primitive)، Iterable نیست و ویژگی size ندارد. کاربرد رایج آن، علامتگذاری شیءها بدون نگهداشتن دائمی آنها در حافظه است:
const processedItems = new WeakSet();
function processItem(item) {
if (processedItems.has(item)) {
console.log("این آیتم قبلاً پردازش شده");
return;
}
processedItems.add(item);
console.log("در حال پردازش آیتم جدید");
}
جدول مقایسهی نسخههای معمولی و Weak
| ویژگی | Map / Set | WeakMap / WeakSet |
|---|---|---|
| نوع کلید یا مقدار مجاز | هر نوعی | فقط شیء |
| تأثیر بر Garbage Collection | مانع آزادسازی حافظه میشود | مانع نمیشود |
| ویژگی size و قابلیت پیمایش | دارد | ندارد |
نکات کلیدی
- در Map و Set معمولی، وجود یک شیء بهعنوان کلید یا مقدار، مانع Garbage Collection آن میشود.
- WeakMap و WeakSet کلیدها یا مقادیر خود را بهصورت ضعیف نگه میدارند و مانع آزادسازی حافظه نمیشوند.
- هر دو ساختار فقط شیء میپذیرند، ویژگی size ندارند و Iterable نیستند.
- کاربرد رایج این دو، نگهداری دادههای موقت یا متادیتای مرتبط با شیءهایی مثل عناصر DOM است.
جمعبندی
WeakMap و WeakSet برای سناریوهای خاصی طراحی شدهاند که در آنها، ارتباط داده با یک شیء نباید مانع پاکسازی طبیعی آن شیء از حافظه شود. این محدودیت آگاهانه—فقدان size و قابلیت پیمایش—مستقیماً نتیجهی همان ویژگی اصلی این دو ساختار یعنی نگهداری ضعیف کلیدهاست. شناخت این ابزارها، بهویژه در برنامههای بزرگ با تعداد زیادی عنصر پویا، از بروز نشتیهای پنهان حافظه جلوگیری میکند.
