فارسی

با 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:

استفاده پایه از 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:

استفاده پایه از 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 به ویژه در سناریوهای زیر مفید هستند:

بهترین شیوه‌ها برای استفاده از WeakMap و WeakSet

سازگاری با مرورگرها

WeakMap و WeakSet توسط همه مرورگرهای مدرن پشتیبانی می‌شوند، از جمله:

برای مرورگرهای قدیمی‌تری که به طور بومی از WeakMap و WeakSet پشتیبانی نمی‌کنند، می‌توانید از polyfillها برای ارائه این قابلیت استفاده کنید.

نتیجه‌گیری

WeakMap و WeakSet ابزارهای ارزشمندی برای مدیریت کارآمد حافظه در برنامه‌های جاوا اسکریپت هستند. با درک نحوه کارکرد و زمان استفاده از آن‌ها، می‌توانید از نشت حافظه جلوگیری کرده، عملکرد برنامه خود را بهینه‌سازی کنید و کد قوی‌تر و قابل نگهداری‌تری بنویسید. به یاد داشته باشید که محدودیت‌های WeakMap و WeakSet، مانند عدم امکان پیمایش روی کلیدها یا مقادیر را در نظر بگیرید و ساختار داده مناسب را برای مورد استفاده خاص خود انتخاب کنید. با اتخاذ این بهترین شیوه‌ها، می‌توانید از قدرت WeakMap و WeakSet برای ساخت برنامه‌های جاوا اسکریپت با عملکرد بالا که در مقیاس جهانی کار می‌کنند، بهره‌مند شوید.