Điều gì xảy ra khi chạy một chương trình JavaScript?

Thực sự thì “chương trình JavaScript được JS Engine thực thi như thế nào?” là câu hỏi mình code JavaScript khá lâu những vẫn chưa thực sự hiểu rõ.

Để hiểu về cách chương trình JavaScript hoạt động, có hai khái niệm cần hiểu là:

“Execution Context” và “CallStack”

Execution Context

Mọi thứ trong JavaScript diễn ra bên trong một “Execution Context”.

(ngữ cảnh thực thi)

Có hai giai đoạn trong “Execution Context” gồm:

Giai đoạn “Memory Creation” (cấp phát bộ nhớ)là lúc tất cả các biến và hàm được cấp phát bộ nhớ dưới dạng key: value. Một tên khác cho phần này là “Variable Environment”

Giai đoạn “Code Execution” (thực thi code) là lúc code được thực thi theo thứ tự từ trên xuống dưới, từng dòng một. Một tên khác cho phần này là “Thread of Execution”

Vì thế:

JavaScript is a synchronous single-threaded language

Mô tả chi tiết một chương trình JS thực thi

Giả sử có chương trình tính bình phương như sau:

1. let n = 2;
2. function square(num) {
3. 	let ans = num. * num;
4. 	return ans;
5. }
6. const square2 = square(n);
7. const square4 = square(4);

Khi chương trình JS khởi chạy, một execution context ở global sẽ được tạo. 

Global Execution Context

Global Execution Context – Giai đoạn “Memory Creation”

Ở giai đoạn “Memory Creation Phase”, bộ nhớ được cấp cho tất cả các biến và các hàm. 

Chương trình bắt đầu đọc từ trên xuống dưới và cấp bộ nhớ:

– Dòng 1 cấp biến n với undefined

– Dòng 2 cấp cho hàm square bộ nhớ cho toàn bộ nội dung của nó,

– Dòng 6 cấp biến square2 với undefined

– Dòng 7 cấp biến square4 với undefined

Global Execution Context – Giai đoạn “Code Execution”

Tiếp tục qua giai đoạn “Code Execution”, code sẽ được thực thi theo thứ tự:

– Dòng 1, gán 2 cho biến n

– Dòng 2 đến 5 bỏ qua vì không có gì để thực thi

– Dòng 6, hàm square(2) được gọi, một execution context giành riêng cho hàm này được tạo, mình tạm gọi là “square execution context”

      

"square(2)" Execution Context - Memory Creation

– Giai đoạn Memory Creation Phase của “square execution context”, cấp bộ nhớ cho num và ans

"square(2)" Execution Context - Code Execution

– Giai đoạn Code Execution của “square execution context”, thực thi:

      – Dòng 2, num được gán giá trị là 2 từ đầu vào khi gọi hàm square(n)

      – Dòng 3, ans được gán giá trị 2 * 2, là 4.

      – Dòng 4, trả về giá trị của ans cho global execution context ở dòng số 6, là nơi gọi hàm square(n). Sau khi trả về giá trị, toàn bộ square execution context bị xóa đi.

"square(4)" Execution Context

– Dòng 7, hàm square(4) được gọi, một execution context giành riêng cho hàm này được tạo, mình tạm gọi là “square 4 execution context”

      – Hai giai đoạn tương tự như khi gọi square(2) ở trên

      – Dòng 4, trả về giá trị của ans cho global execution context ở dòng số 7, là nơi gọi hàm square(4)

CallStack

Đặt các execution context theo thứ tự được thêm vào và bị xóa đi vào một ngăn xếp, ta được 

call stack

– Mỗi execution context được tạo sẽ được bỏ vào(push) vào ngăn xếp(stack)

– Mỗi execution context bị xóa sẽ được lấy ra khỏi(pop) ngăn xếp(stack)

Định nghĩa "callstack"

call stack is a mechanism for an interpreter (like the JavaScript interpreter in a web browser) to keep track of its place in a script that calls multiple functions

Một call stack (ngăn xếp cuộc gọi) là một cơ chế để trình thông dịch (như JS Engine) theo dõi vị trí của nó trong tập lệnh có nhiều hàm – hàm nào đang được chạy, hàm đang chạy gọi từ hàm nào.

Còn hiểu theo những gì mình đã phân tích ở trên với execution context thì:

Call Stack maintain the “order of execution” of execution contex

Callstack chứa toàn bộ các execution contexts theo thứ tự chúng được khởi tạo, và hoạt động theo nguyên lý LIFO (last-in-first-out). Vì thế, mặc định JavaScript hoạt động synchronous.

Giới thiệu stack overflow

stack overflow là hiện tượng không còn bộ nhớ để cấp cho stack, gây lỗi 

“Maximum call stack size exceeded”

Ví dụ như khi một hàm được gọi đệ quy vô tận. 

function callMySelf() {
    callMySelf()
}
callMySelf()

Thử nghiệm trên devtools

Bạn có thể trực tiếp xem được callstack của chương trình với devtools của Chrome. Hình minh họa sau được mình lấy bằng cách chạy chương trình trên với breakpoint ở dòng số 4 trong hàm square khi gọi với square(n)

Call Stack còn có nhiều tên tương tự khác như là: “Execution Context Stack”, “Program Stack”, “Control Stack”, “Runtime Stack”, “Machine Stack”

Đây chính là cách JS Engine thực thi code.

(Ref series Namaste JavaScript)

Nếu bạn nghĩ những nội dung này là hữu ích, bạn có thể khích lệ mình bằng cách:

– Mời mình ☕️ cafe qua Ko-fi hay Momo

– Theo dõi 👀 để nhận các bài viết mới trên: Careerly, fanpage, linkedin

Subscribe channel Youtube BeautyOnCode giúp mình với! 

– 🤘 Nhắn mình nhé 🤘

Hẹn gặp mọi người một ngày nào đó!

Leave a Reply

Your email address will not be published. Required fields are marked *

RELATED POST

Cặp đôi hủy diệt code xấu: DRY và Orthogonality

Bạn có nghĩ giai đoạn bảo trì (maintenance) là sau khi chương trình được phát hành (release)? Và trong giai…

Nguyên tắc SOLID trong lập trình hướng đối tượng (OOP) – thực hành cùng ngôn ngữ Python

SOLID là gì? SOLID là 5 nguyên tắc nền tảng trong lập trình hướng đối tượng OOP (Object Oriented Programming), giúp…

Bốn bước để học và viết trong thời gian dài

Bài blog này mình muốn gửi đến 4 bước mình đã làm để có thể duy trì việc học và rèn luyện…

BeautyOnCode đạt top 1 trên Careerly

Time flies! Nhanh thật, vậy là mình đã đồng hành cùng các đọc giả trên Careerly được hơn 6 tháng,…

%d bloggers like this: