JavaScript
Memory Management
Heap Memory
เห็นภาพว่า Heap เป็นที่อยู่ของ object และตัวแปรใช้ reference เพื่อชี้ไปหาข้อมูลเหล่านั้น
Heap คือบ้านของ object
เวลาคุณสร้าง `object`, `array` หรือ `function` ข้อมูลเหล่านี้จะถูกเก็บไว้ใน Heap สำหรับบทนี้ยังไม่ต้องคิดลึกเรื่องภายในของ engine มากนัก ให้จำภาพง่าย ๆ ก่อนว่า Heap คือพื้นที่ที่เอาไว้เก็บข้อมูลก้อนที่ซับซ้อนกว่า primitive
ให้มอง Heap เป็นบ้านของ object, array และ function ส่วนตัวแปรเป็นเหมือนป้ายที่ชี้ไปหาข้อมูลเหล่านั้น
- สิ่งที่มักอยู่ใน Heap คือ `object`, `array` และ `function`
- ตัวข้อมูลจริงอยู่ใน Heap ไม่ได้ลอยอยู่ในชื่อตัวแปรโดยตรง
- บทนี้เน้นให้เห็นภาพการเก็บข้อมูลก่อน ยังไม่ต้องลงลึกเรื่องการเก็บคืน memory
- ถ้าจำว่า primitive กับ object มีภาพการเก็บคนละแบบ คุณจะอ่านโค้ดได้ง่ายขึ้นมาก
ตัวแปรถือ reference ไม่ได้ถือ object ตรง ๆ
เมื่อเราเขียน `let user = { name: "Mali" }` JavaScript จะสร้าง object ไว้ใน Heap แล้วให้ตัวแปร `user` ถือ reference เพื่อชี้ไปหาก้อนข้อมูลนั้น คำว่า `reference` ในบทนี้ให้คิดง่าย ๆ ว่าเป็นทางเชื่อมจากชื่อตัวแปรไปยังข้อมูลจริง
เวลาเราอ่านหรือแก้ `user.name` เรากำลังตาม reference ไปทำงานกับ object ก้อนเดิม
let user = { name: "Mali" };
console.log(user.name); // "Mali"
user.name = "Fah";
console.log(user.name); // "Fah"ตัวแปรไม่ได้อุ้ม object ไว้ในชื่อของมัน แต่ใช้ reference เพื่อชี้ไปยังข้อมูลจริงใน Heap
- สร้าง object `{ name: "Mali" }` ขึ้นมาใน Heap
- สร้างตัวแปร `user` ขึ้นมาเพื่อใช้เรียกข้อมูลก้อนนี้
- ให้ `user` ถือ reference ที่ชี้ไปหา object ก้อนเดิม
- ทุกครั้งที่ใช้ `user.name` เรากำลังเข้าถึง object เดิมก้อนนั้น
ทำไม copy แล้วอีกตัวเปลี่ยนตาม
จุดที่คนเริ่มต้นงงบ่อยคือ `let user2 = user1` ดูเหมือนเป็นการคัดลอก object แต่จริง ๆ แล้วมันคัดลอก reference ผลคือ `user1` และ `user2` จะมองไปที่ object เดียวกัน ถ้าแก้ผ่านตัวแปรใดตัวแปรหนึ่ง อีกตัวก็เห็นค่าที่เปลี่ยนตามไปด้วย
ครึ่งบนของตัวอย่างคือการแชร์ reference ส่วนครึ่งล่างคือการคัดลอกค่าของ primitive
let user1 = { name: "Mali" };
let user2 = user1;
user2.name = "Fah";
console.log(user1.name); // "Fah"
console.log(user2.name); // "Fah"
let score1 = 10;
let score2 = score1;
score2 = 20;
console.log(score1); // 10
console.log(score2); // 20primitive แยกค่ากัน แต่ object มักทำให้หลายตัวแปรมองไปที่ของชิ้นเดียวกัน
- `let user2 = user1` คือการคัดลอก reference ไม่ใช่สร้าง object ใหม่
- เพราะทั้งสองตัวแปรชี้ object เดียวกัน การแก้ผ่านตัวหนึ่งจึงสะท้อนอีกตัวหนึ่ง
- primitive อย่าง number หรือ string จะให้ภาพจำอีกแบบ คือคัดลอกค่าออกไปเป็นคนละก้อน
- เวลาโค้ด object เปลี่ยนตามกันแบบไม่คาดคิด ให้สงสัยเรื่อง shared reference ก่อนเป็นอย่างแรก
จำแค่นี้พอแล้วค่อยไปต่อ GC
- Heap คือที่อยู่ของ `object`, `array` และ `function`
- ตัวแปรใช้ `reference` เพื่อชี้ไปหาข้อมูลจริงใน Heap
- การคัดลอกตัวแปรแบบ object มักเป็นการคัดลอก reference
- จึงเกิดอาการแก้ผ่านตัวหนึ่งแล้วอีกตัวเปลี่ยนตามได้
ถ้าจำ 4 ข้อนี้ได้ คุณก็พร้อมไปต่อบท `Garbage Collection` แล้ว เพราะบทถัดไปจะอธิบายว่าข้อมูลใน Heap จะถูกเก็บคืนเมื่อไร และเพราะอะไร