با WeakMap و WeakSet در جاوا اسکریپت، ابزارهای قدرتمند مدیریت بهینه حافظه، آشنا شوید. بیاموزید چگونه از نشت حافظه جلوگیری کرده و برنامههای خود را با مثالهای عملی بهینهسازی کنید.
راهنمای جامع WeakMap و WeakSet در جاوا اسکریپت برای مدیریت حافظه
مدیریت حافظه یک جنبه حیاتی در ساخت برنامههای جاوا اسکریپت قوی و کارآمد است. ساختارهای داده سنتی مانند Objectها و Arrayها گاهی اوقات میتوانند منجر به نشت حافظه شوند، به خصوص هنگام کار با ارجاعات به اشیاء. خوشبختانه، جاوا اسکریپت WeakMap
و WeakSet
را ارائه میدهد، دو ابزار قدرتمند که برای مقابله با این چالشها طراحی شدهاند. این راهنمای جامع به بررسی پیچیدگیهای WeakMap
و WeakSet
میپردازد، نحوه کارکرد، مزایا و مثالهای عملی آنها را توضیح میدهد تا به شما کمک کند از آنها به طور مؤثر در پروژههای خود استفاده کنید.
درک نشت حافظه در جاوا اسکریپت
قبل از پرداختن به WeakMap
و WeakSet
، مهم است مشکلی که آنها حل میکنند را درک کنیم: نشت حافظه. نشت حافظه زمانی اتفاق میافتد که برنامه شما حافظهای را تخصیص میدهد اما در بازگرداندن آن به سیستم، حتی زمانی که دیگر نیازی به آن حافظه نیست، ناموفق است. با گذشت زمان، این نشتها میتوانند انباشته شده و باعث کند شدن و در نهایت از کار افتادن برنامه شما شوند.
در جاوا اسکریپت، مدیریت حافظه عمدتاً به صورت خودکار توسط زبالهروب (garbage collector) انجام میشود. زبالهروب به صورت دورهای حافظه اشغال شده توسط اشیائی که دیگر از اشیاء ریشه (شیء سراسری، پشته فراخوانی و غیره) قابل دسترسی نیستند را شناسایی و آزاد میکند. با این حال، ارجاعات ناخواسته به اشیاء میتوانند مانع از جمعآوری زباله شوند و منجر به نشت حافظه گردند. بیایید یک مثال ساده را در نظر بگیریم:
let element = document.getElementById('myElement');
let data = {
element: element,
value: 'Some data'
};
// ... later
// Even if the element is removed from the DOM, 'data' still holds a reference to it.
// This prevents the element from being garbage collected.
در این مثال، شیء data
یک ارجاع به عنصر DOM یعنی element
را نگه میدارد. اگر element
از DOM حذف شود اما شیء data
همچنان وجود داشته باشد، زبالهروب نمیتواند حافظه اشغال شده توسط element
را آزاد کند زیرا هنوز از طریق data
قابل دسترسی است. این یک منبع رایج نشت حافظه در برنامههای وب است.
معرفی WeakMap
WeakMap
مجموعهای از جفتهای کلید-مقدار است که در آن کلیدها باید شیء (object) باشند و مقادیر میتوانند هر مقداری باشند. کلمه «ضعیف» (weak) به این واقعیت اشاره دارد که کلیدها در یک WeakMap
به صورت ضعیف نگهداری میشوند، به این معنی که آنها مانع از جمعآوری حافظه اشغال شده توسط آن کلیدها توسط زبالهروب نمیشوند. اگر یک شیء کلید دیگر از هیچ بخش دیگری از کد شما قابل دسترسی نباشد و فقط توسط WeakMap
به آن ارجاع داده شود، زبالهروب آزاد است که حافظه آن شیء را بازپس گیرد. هنگامی که کلید جمعآوری زباله میشود، مقدار متناظر آن در WeakMap
نیز برای جمعآوری زباله واجد شرایط میشود.
ویژگیهای کلیدی WeakMap:
- کلیدها باید Object باشند: فقط اشیاء میتوانند به عنوان کلید در یک
WeakMap
استفاده شوند. مقادیر اولیه مانند اعداد، رشتهها یا بولینها مجاز نیستند. - ارجاعات ضعیف: کلیدها به صورت ضعیف نگهداری میشوند، که امکان جمعآوری زباله را زمانی که شیء کلید دیگر از جای دیگری قابل دسترسی نیست، فراهم میکند.
- عدم امکان پیمایش:
WeakMap
متدهایی برای پیمایش روی کلیدها یا مقادیر خود (مانندforEach
،keys
،values
) ارائه نمیدهد. این به این دلیل است که وجود این متدها نیازمند نگهداری ارجاعات قوی به کلیدها توسطWeakMap
است که هدف ارجاعات ضعیف را نقض میکند. - ذخیرهسازی دادههای خصوصی:
WeakMap
اغلب برای ذخیرهسازی دادههای خصوصی مرتبط با اشیاء استفاده میشود، زیرا دادهها فقط از طریق خود شیء قابل دسترسی هستند.
استفاده پایه از WeakMap:
در اینجا یک مثال ساده از نحوه استفاده از WeakMap
آورده شده است:
let weakMap = new WeakMap();
let element = document.getElementById('myElement');
weakMap.set(element, 'Some data associated with the element');
console.log(weakMap.get(element)); // Output: Some data associated with the element
// If the element is removed from the DOM and no longer referenced elsewhere,
// the garbage collector can reclaim its memory, and the entry in the WeakMap will also be removed.
مثال عملی: ذخیره دادههای عناصر DOM
یکی از موارد استفاده رایج برای WeakMap
، ذخیرهسازی دادههای مرتبط با عناصر DOM بدون جلوگیری از جمعآوری زباله آن عناصر است. سناریویی را در نظر بگیرید که میخواهید برخی فرادادهها را برای هر دکمه در یک صفحه وب ذخیره کنید:
let buttonMetadata = new WeakMap();
let button1 = document.getElementById('button1');
let button2 = document.getElementById('button2');
buttonMetadata.set(button1, { clicks: 0, label: 'Button 1' });
buttonMetadata.set(button2, { clicks: 0, label: 'Button 2' });
button1.addEventListener('click', () => {
let data = buttonMetadata.get(button1);
data.clicks++;
console.log(`Button 1 clicked ${data.clicks} times`);
});
// If button1 is removed from the DOM and no longer referenced elsewhere,
// the garbage collector can reclaim its memory, and the corresponding entry in buttonMetadata will also be removed.
در این مثال، buttonMetadata
تعداد کلیکها و برچسب هر دکمه را ذخیره میکند. اگر یک دکمه از DOM حذف شود و دیگر از جای دیگری ارجاع داده نشود، زبالهروب میتواند حافظه آن را بازپس گیرد و ورودی متناظر در buttonMetadata
نیز به طور خودکار حذف میشود و از نشت حافظه جلوگیری میکند.
ملاحظات بینالمللیسازی
هنگام کار با رابطهای کاربری که از چندین زبان پشتیبانی میکنند، WeakMap
میتواند به ویژه مفید باشد. شما میتوانید دادههای مختص هر زبان را با عناصر DOM مرتبط کنید:
let localizedStrings = new WeakMap();
let heading = document.getElementById('heading');
// English version
localizedStrings.set(heading, {
en: 'Welcome to our website!',
fr: 'Bienvenue sur notre site web!',
es: '¡Bienvenido a nuestro sitio web!'
});
function updateHeading(locale) {
let strings = localizedStrings.get(heading);
heading.textContent = strings[locale];
}
updateHeading('fr'); // Updates the heading to French
این رویکرد به شما اجازه میدهد تا رشتههای محلیسازی شده را با عناصر DOM مرتبط کنید بدون اینکه ارجاعات قوی نگه دارید که بتواند مانع از جمعآوری زباله شود. اگر عنصر `heading` حذف شود، رشتههای محلیسازی شده مرتبط در `localizedStrings` نیز واجد شرایط جمعآوری زباله میشوند.
معرفی WeakSet
WeakSet
شبیه به WeakMap
است، اما مجموعهای از اشیاء است به جای جفتهای کلید-مقدار. مانند WeakMap
، WeakSet
اشیاء را به صورت ضعیف نگه میدارد، به این معنی که مانع از جمعآوری حافظه اشغال شده توسط آن اشیاء توسط زبالهروب نمیشود. اگر یک شیء دیگر از هیچ بخش دیگری از کد شما قابل دسترسی نباشد و فقط توسط WeakSet
به آن ارجاع داده شود، زبالهروب آزاد است که حافظه آن شیء را بازپس گیرد.
ویژگیهای کلیدی WeakSet:
- مقادیر باید Object باشند: فقط اشیاء میتوانند به یک
WeakSet
اضافه شوند. مقادیر اولیه مجاز نیستند. - ارجاعات ضعیف: اشیاء به صورت ضعیف نگهداری میشوند، که امکان جمعآوری زباله را زمانی که شیء دیگر از جای دیگری قابل دسترسی نیست، فراهم میکند.
- عدم امکان پیمایش:
WeakSet
متدهایی برای پیمایش روی عناصر خود (مانندforEach
،values
) ارائه نمیدهد. این به این دلیل است که پیمایش نیازمند ارجاعات قوی است که هدف را نقض میکند. - ردیابی عضویت:
WeakSet
اغلب برای ردیابی اینکه آیا یک شیء به گروه یا دستهبندی خاصی تعلق دارد، استفاده میشود.
استفاده پایه از WeakSet:
در اینجا یک مثال ساده از نحوه استفاده از WeakSet
آورده شده است:
let weakSet = new WeakSet();
let element1 = document.getElementById('element1');
let element2 = document.getElementById('element2');
weakSet.add(element1);
weakSet.add(element2);
console.log(weakSet.has(element1)); // Output: true
console.log(weakSet.has(element2)); // Output: true
// If element1 is removed from the DOM and no longer referenced elsewhere,
// the garbage collector can reclaim its memory, and it will be automatically removed from the WeakSet.
مثال عملی: ردیابی کاربران فعال
یکی از موارد استفاده برای WeakSet
، ردیابی کاربران فعال در یک برنامه وب است. شما میتوانید اشیاء کاربر را هنگامی که به طور فعال از برنامه استفاده میکنند به WeakSet
اضافه کنید و هنگامی که غیرفعال میشوند آنها را حذف کنید. این به شما امکان میدهد کاربران فعال را بدون جلوگیری از جمعآوری زباله آنها ردیابی کنید.
let activeUsers = new WeakSet();
function userLoggedIn(user) {
activeUsers.add(user);
console.log(`User ${user.id} logged in. Active users: ${activeUsers.has(user)}`);
}
function userLoggedOut(user) {
// No need to explicitly remove from WeakSet. If the user object is no longer referenced,
// it will be garbage collected and automatically removed from the WeakSet.
console.log(`User ${user.id} logged out.`);
}
let user1 = { id: 1, name: 'Alice' };
let user2 = { id: 2, name: 'Bob' };
userLoggedIn(user1);
userLoggedIn(user2);
userLoggedOut(user1);
// After some time, if user1 is no longer referenced elsewhere, it will be garbage collected
// and automatically removed from the activeUsers WeakSet.
ملاحظات بینالمللی برای ردیابی کاربر
هنگام کار با کاربران از مناطق مختلف، ذخیره ترجیحات کاربر (زبان، واحد پول، منطقه زمانی) در کنار اشیاء کاربر میتواند یک عمل رایج باشد. استفاده از WeakMap
در ترکیب با WeakSet
امکان مدیریت کارآمد دادههای کاربر و وضعیت فعال بودن آنها را فراهم میکند:
let activeUsers = new WeakSet();
let userPreferences = new WeakMap();
function userLoggedIn(user, preferences) {
activeUsers.add(user);
userPreferences.set(user, preferences);
console.log(`User ${user.id} logged in with preferences:`, userPreferences.get(user));
}
let user1 = { id: 1, name: 'Alice' };
let user1Preferences = { language: 'en', currency: 'USD', timeZone: 'America/Los_Angeles' };
userLoggedIn(user1, user1Preferences);
این تضمین میکند که ترجیحات کاربر فقط تا زمانی که شیء کاربر زنده است ذخیره میشوند و از نشت حافظه در صورت جمعآوری زباله شیء کاربر جلوگیری میکند.
مقایسه WeakMap با Map و WeakSet با Set: تفاوتهای کلیدی
درک تفاوتهای کلیدی بین WeakMap
و Map
، و WeakSet
و Set
مهم است:
ویژگی | WeakMap |
Map |
WeakSet |
Set |
---|---|---|---|---|
نوع کلید/مقدار | فقط اشیاء (کلیدها)، هر مقداری (مقادیر) | هر نوعی (کلیدها و مقادیر) | فقط اشیاء | هر نوعی |
نوع ارجاع | ضعیف (کلیدها) | قوی | ضعیف | قوی |
پیمایش | غیرمجاز | مجاز (forEach ، keys ، values ) |
غیرمجاز | مجاز (forEach ، values ) |
جمعآوری زباله | کلیدها در صورتی که هیچ ارجاع قوی دیگری وجود نداشته باشد، واجد شرایط جمعآوری زباله هستند | کلیدها و مقادیر تا زمانی که Map وجود دارد، واجد شرایط جمعآوری زباله نیستند | اشیاء در صورتی که هیچ ارجاع قوی دیگری وجود نداشته باشد، واجد شرایط جمعآوری زباله هستند | اشیاء تا زمانی که Set وجود دارد، واجد شرایط جمعآوری زباله نیستند |
چه زمانی از WeakMap و WeakSet استفاده کنیم
WeakMap
و WeakSet
به ویژه در سناریوهای زیر مفید هستند:
- مرتبط کردن داده با اشیاء: زمانی که نیاز به ذخیره دادههای مرتبط با اشیاء (مانند عناصر DOM، اشیاء کاربر) دارید بدون اینکه مانع از جمعآوری زباله آن اشیاء شوید.
- ذخیرهسازی دادههای خصوصی: زمانی که میخواهید دادههای خصوصی مرتبط با اشیاء را ذخیره کنید که فقط باید از طریق خود شیء قابل دسترسی باشند.
- ردیابی عضویت اشیاء: زمانی که نیاز به ردیابی این دارید که آیا یک شیء به یک گروه یا دستهبندی خاص تعلق دارد بدون اینکه مانع از جمعآوری زباله آن شیء شوید.
- کش کردن عملیات پرهزینه: میتوانید از یک WeakMap برای کش کردن نتایج عملیات پرهزینه انجام شده روی اشیاء استفاده کنید. اگر شیء جمعآوری زباله شود، نتیجه کش شده نیز به طور خودکار حذف میشود.
بهترین شیوهها برای استفاده از WeakMap و WeakSet
- از اشیاء به عنوان کلید/مقدار استفاده کنید: به یاد داشته باشید که
WeakMap
وWeakSet
فقط میتوانند به ترتیب اشیاء را به عنوان کلید یا مقدار ذخیره کنند. - از ارجاعات قوی به کلیدها/مقادیر خودداری کنید: اطمینان حاصل کنید که ارجاعات قوی به کلیدها یا مقادیر ذخیره شده در
WeakMap
یاWeakSet
ایجاد نمیکنید، زیرا این کار هدف ارجاعات ضعیف را نقض میکند. - جایگزینها را در نظر بگیرید: ارزیابی کنید که آیا
WeakMap
یاWeakSet
انتخاب مناسبی برای مورد استفاده خاص شماست. در برخی موارد، یکMap
یاSet
معمولی ممکن است مناسبتر باشد، به خصوص اگر نیاز به پیمایش روی کلیدها یا مقادیر دارید. - به طور کامل تست کنید: کد خود را به طور کامل تست کنید تا اطمینان حاصل کنید که نشت حافظه ایجاد نمیکنید و
WeakMap
وWeakSet
شما همانطور که انتظار میرود رفتار میکنند.
سازگاری با مرورگرها
WeakMap
و WeakSet
توسط همه مرورگرهای مدرن پشتیبانی میشوند، از جمله:
- Google Chrome
- Mozilla Firefox
- Safari
- Microsoft Edge
- Opera
برای مرورگرهای قدیمیتری که به طور بومی از WeakMap
و WeakSet
پشتیبانی نمیکنند، میتوانید از polyfillها برای ارائه این قابلیت استفاده کنید.
نتیجهگیری
WeakMap
و WeakSet
ابزارهای ارزشمندی برای مدیریت کارآمد حافظه در برنامههای جاوا اسکریپت هستند. با درک نحوه کارکرد و زمان استفاده از آنها، میتوانید از نشت حافظه جلوگیری کرده، عملکرد برنامه خود را بهینهسازی کنید و کد قویتر و قابل نگهداریتری بنویسید. به یاد داشته باشید که محدودیتهای WeakMap
و WeakSet
، مانند عدم امکان پیمایش روی کلیدها یا مقادیر را در نظر بگیرید و ساختار داده مناسب را برای مورد استفاده خاص خود انتخاب کنید. با اتخاذ این بهترین شیوهها، میتوانید از قدرت WeakMap
و WeakSet
برای ساخت برنامههای جاوا اسکریپت با عملکرد بالا که در مقیاس جهانی کار میکنند، بهرهمند شوید.