เจาะลึก JavaScript Execution Context พื้นฐานสำคัญเบื้องหลังความโต้ตอบของเว็บ
ทำความเข้าใจระบบการทำงานเบื้องหลังของ JavaScript ทั้ง Execution Context, Stack, Hoisting และ Temporal Dead Zone ที่นักพัฒนาทุกคนต้องรู้

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
- Execution Context เปรียบเสมือนพื้นที่ทำงานที่เก็บตัวแปร ฟังก์ชัน และค่า this ไว้
- การทำงานของ Context ถูกจัดเก็บในรูปแบบ Stack ตามหลักการ LIFO
- Hoisting เกิดขึ้นในขั้นตอน Creation Phase ก่อนที่โค้ดจะเริ่มรันจริง
- ตัวแปรที่ประกาศด้วย let และ const จะอยู่ใน Temporal Dead Zone จนกว่าจะถึงบรรทัดประกาศ
ความท้าทายที่ยากที่สุดในการเรียนรู้ JavaScript คือการทำความเข้าใจว่าชิ้นส่วนต่างๆ ร่วมมือกันอย่างไรเพื่อสร้างความโต้ตอบบนหน้าเว็บไซต์ ซึ่งหัวใจสำคัญที่อยู่เบื้องหลังทั้งหมดนี้ก็คือสิ่งที่เรียกว่า Execution Context หรือบริบทการดำเนินการนั่นเอง
หากเปรียบเทียบให้เห็นภาพง่ายๆ Execution Context ก็คือพื้นที่ทำงานส่วนตัว เมื่อใดก็ตามที่ JavaScript เริ่มรันโค้ด ระบบจะสร้างพื้นที่นี้ขึ้นมาเพื่อรวบรวมทุกสิ่งที่จำเป็นต่อการทำงานให้สำเร็จ ภายในพื้นที่จะประกอบไปด้วยตัวแปร ฟังก์ชัน ค่าของ this และข้อมูลที่บอกว่าโปรแกรมกำลังทำงานอยู่ถึงจุดไหนและต้องไปต่อที่ไหน
พื้นที่ทำงานเหล่านี้ถูกจัดเก็บไว้ในโครงสร้างข้อมูลที่เรียกว่า Call Stack ซึ่งมีคุณสมบัติเฉพาะตัวแบบ LIFO หรือ Last in, First out เปรียบเสมือนกองจานที่เราจะหยิบจานใบบนสุดออกมาก่อนเสมอ และไม่สามารถดึงจานจากด้านล่างสุดออกมาก่อนได้

ภาพประกอบจากคลังภาพสต็อก ไม่ใช่ภาพจากเหตุการณ์จริง
การทำความเข้าใจ Call Stack มีความสำคัญอย่างยิ่งในการเขียนโค้ด JavaScript แบบ Asynchronous เพราะช่วยให้เราวิเคราะห์ปัญหา Call Stack Overflow ที่เกิดจากการเรียกฟังก์ชันวนซ้ำไม่สิ้นสุด (Infinite Recursion) ได้อย่างแม่นยำ รวมถึงเข้าใจลำดับการทำงานของ Event Loop อีกด้วย
ในทุกๆ Execution Context จะแบ่งการทำงานออกเป็น 2 เฟสหลักๆ ได้แก่:
- Creation Phase: เฟสแรกที่ JavaScript กวาดสายตาอ่านโค้ดและเตรียมหน่วยความจำ พร้อมทั้งระบุตัวแปรและฟังก์ชันต่างๆ ไว้ล่วงหน้า
- Execution Phase: เฟสถัดมาที่โค้ดจะถูกรันทีละบรรทัด พร้อมทั้งกำหนดค่าให้กับตัวแปรและประเมินคำสั่งต่างๆ
คำศัพท์คำหนึ่งที่มักจะสร้างความกังวลใจให้กับนักพัฒนาเว็บก็คือ Hoisting ซึ่งปรากฏการณ์นี้เกิดขึ้นมาจากช่วง Creation Phase นั่นเอง เนื่องจาก JavaScript ทำการสแกนโค้ด ทำให้มันรับรู้การมีอยู่ของตัวแปรและฟังก์ชันก่อนที่การรันโค้ดจะเริ่มต้นขึ้นเสียอีก เช่น เมื่อเราสั่งพิมพ์ค่าตัวแปร a ที่ประกาศด้วย var ผลลัพธ์ที่ได้จะไม่ใช่ข้อผิดพลาด แต่จะได้ค่า undefined เนื่องจาก JavaScript ได้จองพื้นที่และกำหนดค่าเริ่มต้นให้เรียบร้อยแล้ว ในขณะที่ Function Declaration จะถูกยกขึ้นไปทั้งหมดทำให้สามารถเรียกใช้งานก่อนที่จะเขียนประกาศฟังก์ชันนั้นเสียอีก
ต่อมาในยุค ES6 ได้มีการแนะนำคีย์เวิร์ดใหม่อย่าง let และ const เข้ามา หากเราเขียนโค้ดในลักษณะเดียวกันแต่เปลี่ยนมาใช้ let เราจะพบว่ามันโยนข้อผิดพลาด ReferenceError ออกมาทันที เกิดอะไรขึ้นกันแน่?
ความจริงคือ JavaScript ยังคงรู้ว่าตัวแปร a มีอยู่ แต่ความแตกต่างคือค่าดังกล่าวจะไม่ถูกบันทึกไว้ใน context ทันทีเหมือนกรณีของ var หากแต่ถูกเก็บไว้ในพื้นที่อีกแห่งหนึ่งที่เรียกว่า Temporal Dead Zone หรือ TDZ
Temporal Dead Zone คือพื้นที่ภายในบล็อกโค้ดที่ตัวแปรมีตัวตนอยู่แต่ยังไม่สามารถเข้าถึงได้ โดย TDZ จะเริ่มต้นตั้งแต่จุดเริ่มต้นของบล็อกโค้ดลากยาวไปจนกระทั่งถึงบรรทัดที่มีการประกาศและกำหนดค่าให้กับตัวแปรนั้นๆ
ที่มา: Dev.to
พบข้อมูลผิดพลาดในบทความนี้? แจ้งปัญหาบทความนี้
ความคิดเห็น
แสดงความคิดเห็น