JavaScript
Memory Management
Stack Memory
มอง Stack Memory แบบเห็นภาพ: งานล่าสุดอยู่บนสุด, การเรียก function ซ้อนกันเป็นชั้น, และ primitive values ถูก copy แยกจากกัน
Stack Memory คือที่พักชั่วคราวของค่าที่กำลังใช้อยู่
เวลา JavaScript เริ่มทำงาน มันต้องมีที่ไว้เก็บงานที่กำลังทำอยู่ชั่วคราว Stack Memory คือพื้นที่นั้น ลองนึกภาพเป็นกองบัตรงานที่วางซ้อนกัน งานใหม่จะถูกวางไว้ด้านบนเสมอ และตอนทำงานเสร็จก็จะหยิบบัตรใบบนสุดออกก่อน
Stack เป็นพื้นที่ทำงานชั่วคราวแบบเรียงเป็นชั้น งานล่าสุดจะอยู่บนสุดเสมอ
- Stack จัดวางข้อมูลเป็นชั้นจากล่างขึ้นบน
- สิ่งที่เพิ่งถูกเรียกหรือถูกสร้าง จะไปอยู่ด้านบนสุด
- เมื่อ function จบ ข้อมูลของ function นั้นก็หายจาก stack ได้ทันที
- บทนี้ให้จำภาพรวมก่อน ยังไม่ต้องลงลึกเรื่องภายในของ engine
เรียก function ใหม่ ก็วางงานก้อนใหม่ไว้ด้านบน
ทุกครั้งที่เราเรียก function JavaScript จะสร้างพื้นที่ย่อยก้อนหนึ่งขึ้นมาด้านบนของ stack เพื่อเก็บค่าที่ function นั้นใช้ เช่น parameter และตัวแปรใน function ถ้ามี function เรียก function ต่ออีกชั้น งานก้อนใหม่ก็จะซ้อนขึ้นไปอีก และงานบนสุดต้องเสร็จก่อน จึงจะกลับลงมาทำงานก้อนก่อนหน้าได้
เวลาเรียก function ซ้อนกัน stack จะสูงขึ้นทีละชั้น และจะคืนกลับจากชั้นบนสุดก่อน
ตอน showProfile เรียก formatName จะมีงานก้อนใหม่ซ้อนขึ้นไปก่อน แล้วค่อย return กลับลงมา
function formatName(first, last) {
const fullName = first + " " + last;
return fullName;
}
function showProfile() {
const name = formatName("Mali", "Sukjai");
console.log(name);
}
showProfile();- เริ่มจาก Global ทำงานอยู่ด้านล่าง
- เรียก showProfile แล้วสร้างงานของ showProfile ขึ้นมาด้านบน
- showProfile เรียก formatName ก็สร้างงานของ formatName ซ้อนขึ้นไปอีก
- formatName ทำเสร็จก่อน แล้ว stack ค่อยกลับลงมาที่ showProfile
Primitive บน Stack ถูก copy เป็นคนละค่า
ค่าพื้นฐานอย่าง number, string และ boolean มักใช้ภาพจำว่าเก็บและใช้งานบน stack แบบแยกกัน เพราะฉะนั้นถ้าเราเอาค่าหนึ่งไปเก็บในอีกตัวแปรหนึ่ง JavaScript จะคัดลอกค่าไปเป็นอีกก้อน ไม่ได้แชร์ก้อนเดิมร่วมกัน
เมื่อ copy primitive ค่าใหม่จะถูกเก็บแยกกัน แก้อีกตัวหนึ่งแล้วอีกตัวไม่เปลี่ยนตาม
nextScore ได้สำเนาค่าจาก score จึงเปลี่ยนแยกจากกันได้
let score = 10;
let nextScore = score;
nextScore = 20;
console.log(score); // 10
console.log(nextScore); // 20| คำสั่ง | ภาพที่ควรนึกออก |
|---|---|
| let score = 10 | สร้างค่าของ score ขึ้นมา 1 ก้อน |
| let nextScore = score | คัดลอกค่า 10 ไปเป็นอีกก้อนของ nextScore |
| nextScore = 20 | เปลี่ยนเฉพาะก้อนของ nextScore โดย score ยังเท่าเดิม |
นี่เป็นเหตุผลที่ primitive ดูเข้าใจง่ายกว่า object มาก เพราะเวลาเปลี่ยนค่า เรามักไม่เจอผลกระทบย้อนกลับไปอีกตัวแปรหนึ่ง ส่วน object และ array จะมีภาพจำอีกแบบหนึ่ง ซึ่งเราจะต่อในบท Heap Memory ถัดไป
จำภาพนี้ให้ได้ก็พอสำหรับบทนี้
- Stack คือพื้นที่ทำงานชั่วคราวของสิ่งที่ JavaScript กำลังใช้อยู่ตอนนี้
- งานใหม่จะวางไว้ด้านบนสุดเสมอ และต้องทำงานบนสุดให้เสร็จก่อน
- การเรียก function ซ้อนกัน คือการซ้อนงานหลายชั้นบน stack
- primitive เมื่อ copy แล้วจะกลายเป็นคนละค่า ไม่ใช่ก้อนเดียวกัน
ถ้าจำได้ 4 ข้อนี้ คุณก็พร้อมไปต่อที่ Heap Memory แล้ว เพราะบทถัดไปจะพาเห็นว่าทำไม object กับ array ถึงมีพฤติกรรมต่างจาก primitive