Mục lục

Bản chất JavaScript: Execution Context & Call Stack

Phân tích cách JavaScript thực thi mã lệnh và so sánh với các ngôn ngữ biên dịch như C++.

Để trở thành Senior, bạn phải ngừng nhìn JavaScript như một ngôn ngữ "script đơn giản" và bắt đầu nhìn nó như một hệ thống thực thi phức tạp.

1. Bản chất: Thông dịch (Interpreted) hay Biên dịch (Compiled)?

Khác với C++ (Biên dịch thuần túy sang mã máy trước khi chạy) hay Python (Thông dịch từng dòng), JavaScript hiện đại (V8, SpiderMonkey) sử dụng cơ chế JIT (Just-In-Time) Compilation.

  • C++: Mã nguồn -> Compiler -> Binary (Mã máy) -> Thực thi.
  • JS: Mã nguồn -> Parser (AST) -> Interpreter (Ignition) -> Compiler (TurboFan) -> Mã máy tối ưu.

V8 Engine: 2D Translation Analogy

Tại sao cần biết điều này? Vì V8 sẽ theo dõi code của bạn. Nếu một hàm được gọi nhiều lần với cùng kiểu dữ liệu, nó sẽ "nóng" lên và được biên dịch thành mã máy cực nhanh. Nếu bạn thay đổi kiểu dữ liệu đột ngột (Polymorphism), V8 sẽ phải "de-optimize", khiến hiệu năng sụt giảm 10-100 lần.

2. Execution Context: Trái tim của JS

Mỗi khi JS chạy, nó tạo ra một Execution Context (EC). Hãy tưởng tượng nó như một "hộp chứa" bao gồm:

  1. Variable Environment: Nơi chứa biến, hàm.
  2. Scope Chain: Tham chiếu đến môi trường bên ngoài.
  3. this keyword: Tham chiếu đến object hiện tại.

Thử nghiệm: Hoisting & Execution Context

Hãy chạy mã dưới đây để thấy giai đoạn Create Memory vận hành. Dù callMe() được gọi trước khi định nghĩa, nó vẫn chạy thành công vì hàm đã được lưu vào bộ nhớ trước khi thực thi.

3. Call Stack: Cơ chế quản lý thực thi

JS là Single-threaded (đơn luồng). Nó chỉ làm một việc tại một thời điểm. Call Stack là nơi quản lý các EC này theo cơ chế LIFO (Last In, First Out).

javascript:
function a() { b(); }
function b() { console.log('Hi'); }
a();
  1. Global EC được đẩy vào Stack.
  2. a() được gọi -> EC của a được đẩy vào.
  3. b() được gọi -> EC của b được đẩy vào.
  4. b() xong -> Pop b khỏi Stack.
  5. a() xong -> Pop a khỏi Stack.

Senior Note: Stack Overflow xảy ra khi bạn gọi đệ quy quá sâu mà không có điểm dừng, làm đầy bộ nhớ của Stack.

4. So sánh với các ngôn ngữ khác

Đặc tínhJavaScriptC++ / Rust
Quản lý bộ nhớTự động (Garbage Collection)Thủ công hoặc dùng RAII
Luồng thực thiĐơn luồng (Event Loop)Đa luồng thực sự (Threads)
Kiểu dữ liệuDynamic (Yếu)Static (Mạnh)

Hiểu về Execution Context giúp bạn giải thích được tại sao Closures tồn tại: Khi EC của một hàm bị xóa khỏi Stack, nếu có một hàm con vẫn tham chiếu đến biến của nó, bộ nhớ đó sẽ không bị Garbage Collector thu hồi. Đó chính là bản chất của Senior JS.

Quảng cáo
mdhorizontal