Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> JavaScript

WeakMap ใน JavaScript - Key-Value Pair ที่ Key เป็น Object และ Garbage Collect ได้

WeakMap คล้าย Map แต่ Key ต้องเป็น Object เท่านั้น และ Weak Reference - ถ้า Key Object ไม่มี Reference อื่นอ้างอิง จะถูก Garbage Collector ลบออกอัตโนมัติ เหมาะสำหรับ Metadata/Private Data

คุณสมบัติหลักของ WeakMap

คุณสมบัติMapWeakMap
Key TypeAny (Primitive/Object)✅ Object เท่านั้น
ReferenceStrong (ป้องกัน GC)✅ Weak (ไม่ป้องกัน GC)
Iterable✅ for...of, keys(), values()❌ ไม่ได้ (Non-enumerable)
Size Property✅ .size❌ ไม่มี
clear()✅ มี❌ ไม่มี
Use CaseGeneral PurposeMetadata, 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)

เหมือน 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";
}