بیشتر ما برنامهنویسهای جاوااسکریپت، به محض اینکه میخواهیم لیستی از دادهها را ذخیره کنیم یک Array میسازیم و اگر دادههایمان ساختار کلید-مقدار (Key-Value) داشته باشند، سراغ Object میرویم. این ترکیب برای ۹۰ درصد مواقع عالی کار میکند؛ اما در مصاحبههای استخدامی شرکتهای بزرگ (و پروژههایی که روی هزاران رکورد کار میکنند)، گیر افتادن در همین عادت ساده میتواند باعث ریجکت شدن رزومه یا افت شدید پرفورمنس شود.
در این مقاله، ساختارهای داده مدرن جاوااسکریپت (Map و Set) را کالبدشکافی میکنیم تا ببینیم چرا، کجا و چگونه باید جایگزین روشهای سنتی شوند.
آبجکتها پایهایترین ساختار در جاوااسکریپت هستند، اما در واقع برای ساختار دادهایِ “دیکشنری” (Dictionary) ساخته نشدهاند. Map در ES6 معرفی شد تا یک “هشمپ” (Hash Map) واقعی در اختیار ما قرار دهد.
تفاوتهای مرگبار در مصاحبه و پرفورمنس:
محدودیت کلیدها (Keys): در یک Object، کلیدها فقط میتوانند String یا Symbol باشند. اگر یک عدد یا یک آبجکت دیگر را به عنوان کلید بدهید، جاوااسکریپت در پسزمینه آن را به رشته (استرینگ) تبدیل میکند! (مثلاً [object Object]). اما در Map، هر چیزی میتواند کلید باشد.
ترتیب پیمایش (Iteration Order): یکی از باگهای اعصابخردکن آبجکتها این است که تضمین صددرصدی برای حفظ ترتیب کلیدها نمیدهند (به خصوص اگر کلیدها عدد باشند، جاوااسکریپت آنها را مرتب میکند). اما Map دقیقاً ترتیب اضافه شدن (Insertion Order) را حفظ میکند.
سایز و اندازه: برای پیدا کردن تعداد آیتمهای یک آبجکت باید بنویسید Object.keys(obj).length که در پسزمینه یک آرایه جدید میسازد و پیچیدگی زمانی آن O(n)O(n)O(n) است. اما Map یک پراپرتی آماده به نام size دارد که در زمان O(1)O(1)O(1) سایز را به شما میدهد.
پرفورمنس در حذف و اضافه: اگر سناریویی دارید که مدام در حال set و delete کردن کلیدها هستید (مثل پیادهسازی یک سیستم Caching)، موتور V8 جاوااسکریپت Map را برای این کار بهینهسازی کرده است و سرعت به مراتب بالاتری دارد.
کد نمونه:
javascript// استفاده از آبجکت به عنوان کلید در Map برای ذخیره دیتای اضافی const userMetaData = new Map(); const userDomElement = document.getElementById('btn-login'); // بدون اینکه خود عنصر DOM را دستکاری کنیم، به آن دیتا وصل میکنیم userMetaData.set(userDomElement, { clickCount: 0, lastClicked: Date.now() }); console.log(userMetaData.get(userDomElement).clickCount); // 0
نقطه ضعف Map (تله مصاحبه):
متد JSON.stringify به صورت پیشفرض Map را نمیفهمد و خروجی خالی {} میدهد! برای تبدیل Map به JSON باید ابتدا آن را به آرایه یا آبجکت تبدیل کنید (Object.fromEntries(myMap)).
فرض کنید یک آرایه از اعداد دارید و میخواهید چک کنید آیا شناسه 100 در آن وجود دارد یا نه. یا اینکه میخواهید رکوردهای تکراری را از آن حذف کنید. اینجاست که Set وارد بازی میشود. Set کالکشنی است که هیچ مقدار تکراری در آن مجاز نیست.
چرا Set پرفورمنس شما را نجات میدهد؟
سرعت جستجو (The O(1)O(1)O(1) Magic): وقتی از array.includes(100) استفاده میکنید، جاوااسکریپت باید از خانه اول تا آخر را بگردد تا آن را پیدا کند. پیچیدگی این کار O(n)O(n)O(n) است. اما در Set، وقتی از set.has(100) استفاده میکنید، به لطف الگوریتم Hash، جستجو با پیچیدگی زمانی O(1)O(1)O(1) انجام میشود. در دیتاستهای بزرگ، این تفاوت یعنی تبدیل شدن یک سرچ کُند به یک سرچ آنی!
حذف تکراریها (یک خط کد جادویی): پرتکرارترین سوال مصاحبه: “چطور آرایه زیر را یونیک کنیم؟”
javascriptconst duplicates = [1, 1, 2, 3, 3, 4]; const uniqueArray = [...new Set(duplicates)]; // [1, 2, 3, 4]
عملیاتهای پیشرفته با Set (برای مصاحبههای Senior):
آرایهها متدهای آمادهای برای عملیات ریاضی مجموعهها ندارند، اما با Set میتوانید به راحتی اشتراک (Intersection) و تفاضل (Difference) دو مجموعه داده را پیدا کنید:
javascriptconst setA = new Set([1, 2, 3]); const setB = new Set([3, 4, 5]); // اشتراک (دادههای مشترک): [3] const intersection = new Set([...setA].filter(x => setB.has(x))); // تفاضل (دادههایی که فقط در A هستند): [1, 2] const difference = new Set([...setA].filter(x => !setB.has(x)));
اگر در مصاحبهای تسلط خود را روی Map نشان دهید، سوال بعدی ۱۰۰٪ این خواهد بود: “خب، فرق Map با WeakMap چیست؟”
جواب در یک مفهوم حیاتی خلاصه میشود: Garbage Collection (زبالهروب حافظه).
در Map معمولی، تا زمانی که کلید شما داخل Map حضور دارد، آن دیتای مرجع از مموری رم پاک نمیشود. این مسئله وقتی روی عناصر DOM یا آبجکتهای سنگین کار میکنید میتواند باعث Memory Leak (نشت حافظه) شود.
اما در WeakMap (و WeakSet):
کلیدها الزاماً باید Object باشند (عدد یا استرینگ قبول نمیکند).
پیوند “ضعیفی” (Weak) با مموری دارند. اگر آن آبجکت در هیچ کجای دیگر از برنامه شما استفاده نشود، موتور جاوااسکریپت منتظر WeakMap نمیماند و آن را کاملاً از رم و همچنین از داخل WeakMap پاک میکند.
کاربرد واقعی: پیادهسازی متغیرهای Private در کلاسها یا ذخیره دیتای موقت برای عناصر DOM (که وقتی کاربر از صفحه رفت و آن عنصر DOM حذف شد، دیتای متصل به آن هم خودکار از حافظه آزاد شود).
برای اینکه در کسری از ثانیه بهترین ابزار را انتخاب کنید:
🔹 کی از Object استفاده کنیم؟
ساختار دادهتان از قبل مشخص است (مثل اطلاعات یک User).
دادهها قرار است مستقیماً به JSON تبدیل شده و به بکاند ارسال شوند.
نیاز به تعریف متد (Functions) روی دادهها دارید.
🔹 کی از Map استفاده کنیم؟
دیکشنری بزرگی دارید که مدام در حال آپدیت، حذف و اضافه شدن کلیدهاست.
نیاز دارید کلیدی غیر از String داشته باشید (مثل ذخیره کانفیگ برای یک Object).
پرفورمنس و حفظ ترتیبِ اضافه شدن کلیدها برایتان حیاتی است.
🔹 کی از Array استفاده کنیم؟
ترتیب و ایندکس (Index 0, 1, 2) دادهها مهم است.
دادههای تکراری مشکلی ندارند.
نیاز به متدهای زنجیرهای مثل map(), filter(), reduce() دارید.
🔹 کی از Set استفاده کنیم؟
با لیستی از مقادیر یکتا (Unique) سروکار دارید.
نیاز دارید با بالاترین سرعت ممکن (O(1)) چک کنید که یک آیتم در لیست وجود دارد یا خیر (جایگزین array.includes).