WeakMap คล้าย Map แต่ Key ต้องเป็น Object เท่านั้น และ Weak Reference - ถ้า Key Object ไม่มี Reference อื่นอ้างอิง จะถูก Garbage Collector ลบออกอัตโนมัติ เหมาะสำหรับ Metadata/Private Data
คุณสมบัติหลักของ WeakMap
| คุณสมบัติ | Map | WeakMap |
|---|---|---|
| Key Type | Any (Primitive/Object) | ✅ Object เท่านั้น |
| Reference | Strong (ป้องกัน GC) | ✅ Weak (ไม่ป้องกัน GC) |
| Iterable | ✅ for...of, keys(), values() | ❌ ไม่ได้ (Non-enumerable) |
| Size Property | ✅ .size | ❌ ไม่มี |
| clear() | ✅ มี | ❌ ไม่มี |
| Use Case | General Purpose | Metadata, Cache, Private Data |
ตัวอย่าง: WeakMap พื้นฐานและ Garbage Collection
<!DOCTYPE html>
<html lang="th">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WeakMap</title>
<style>
body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; }
.result { font-size: 18px; font-weight: 500; color: rebeccapurple; }
button { padding: 8px 16px; margin: 5px; cursor: pointer; }
.btn-danger { background: #dc3545; color: white; border: none; border-radius: 4px; }
</style>
</head>
<body>
<h1>WeakMap ใน JavaScript</h1>
<div class="result" id="result"></div>
<button class="Btn" id="showBtn">แสดง WeakMap</button>
<button class="Btn btn-danger" id="removeBtn">ลบ Reference (Trigger GC)</button>
<h3>กด "แสดง WeakMap" แล้วกด "ลบ Reference" เพื่อดู GC ทำงาน</h3>
<script>
const resultEle = document.getElementById("result");
const showBtn = document.getElementById("showBtn");
const removeBtn = document.getElementById("removeBtn");
// Object ที่จะเป็น Key
let personObj = {
name: "Rohan Sharma",
age: 22,
class: 9
};
// สร้าง WeakMap
const weakMap1 = new WeakMap();
weakMap1.set(personObj, "Rohan Object Data");
showBtn.addEventListener("click", () => {
const value = weakMap1.get(personObj);
resultEle.innerHTML = `personObj: ${value || "ไม่พบ (ถูกลบแล้ว)"}
`;
resultEle.innerHTML += `weakMap1.has(personObj): ${weakMap1.has(personObj)}`;
});
removeBtn.addEventListener("click", () => {
// ลบ Reference เดียวที่เหลือ (WeakMap ไม่นับ)
personObj = null;
// บังคับ GC (ใน DevTools: Collect Garbage) - ใน Code ไม่บังคับได้โดยตรง
resultEle.innerHTML += `
🗑️ personObj = null (Reference เดียวหาย)`;
resultEle.innerHTML += `
weakMap1.has(personObj): ${weakMap1.has(personObj)}`;
resultEle.innerHTML += `
💡 เปิด DevTools → Memory → "Collect Garbage" แล้วกดแสดงอีกครั้ง`;
});
</script>
</body>
</html>
ผลลัพธ์
กด "แสดง WeakMap": personObj: Rohan Object Data, has: true
กด "ลบ Reference": personObj = null
กด "Collect Garbage" ใน DevTools → กด "แสดง WeakMap" อีกครั้ง: has: false (Entry หายไป)
Use Cases จริงของ WeakMap
1. Private Data / Internal State (Encapsulation)
const privateData = new WeakMap();
class Person {
constructor(name, age) {
privateData.set(this, { name, age, secret: "hidden" });
}
getName() {
return privateData.get(this).name;
}
getSecret() {
return privateData.get(this).secret; // เข้าถึงได้แค่ใน Class
}
}
const p = new Person("John", 30);
console.log(p.getName()); // "John"
// privateData ไม่อยู่ใน p - ซ่อนได้จริง
2. Caching / Memoization สำหรับ Object Keys
const cache = new WeakMap();
function expensiveOperation(obj) {
if (cache.has(obj)) {
console.log("Cache hit!");
return cache.get(obj);
}
// Heavy computation...
const result = heavyCompute(obj);
cache.set(obj, result);
return result;
}
// ถ้า obj ไม่ถูกใช้งานที่อื่น → GC ลบ cache entry อัตโนมัติ
3. DOM Element Metadata (ไม่ทำให้ Memory Leak)
const elementData = new WeakMap();
function trackElement(element) {
elementData.set(element, {
createdAt: Date.now(),
clicks: 0
});
element.addEventListener("click", () => {
const data = elementData.get(element);
data.clicks++;
console.log(`Element clicked ${data.clicks} times`);
});
}
// เมื่อ Element ถูกลบออกจาก DOM และไม่มี Ref อื่น
// elementData entry จะหายไปอัตโนมัติ (No Memory Leak!)
4. เทียบกับ Map (เมื่อไหร่ใช้ไหน)
// ใช้ Map เมื่อ:
// - ต้อง Iterate ทุกค่า
// - ต้องการ .size
// - Key เป็น Primitive (String, Number)
// - Cache ที่ต้องควบคุมอายุการใช้งานเอง
// ใช้ WeakMap เมื่อ:
// - Key เป็น Object
// - ไม่อยากป้องกัน GC (Metadata, Private Data)
// - ไม่ต้อง Iterate / ไม่ต้องรู้ Size
// - DOM Element Associations
WeakSet (Related)
// ใช้ Map เมื่อ:
// - ต้อง Iterate ทุกค่า
// - ต้องการ .size
// - Key เป็น Primitive (String, Number)
// - Cache ที่ต้องควบคุมอายุการใช้งานเอง
// ใช้ WeakMap เมื่อ:
// - Key เป็น Object
// - ไม่อยากป้องกัน GC (Metadata, Private Data)
// - ไม่ต้อง Iterate / ไม่ต้องรู้ Size
// - DOM Element Associationsเหมือน WeakMap แต่เก็บแค่ Values (Object เท่านั้น) ใช้สำหรับ Tag/Mark Objects
const visited = new WeakSet();
function visit(obj) {
if (visited.has(obj)) return "Already visited";
visited.add(obj);
return "First visit";
}