Skip to content

Lesson 1.1.1 — Execution Context là gì? ​

Bài 1.1.1 — Execution Context là gì?
Execution Context, ECMAScript & Mental Model thực thi • 21 phút
0:00 / 0:00

0. Metadata (Thông tin bài học) ​

TrườngGiá trị
Stage1 — JavaScript Execution Model
Module1.1 — Execution Context
Lesson1.1.1 — Execution Context là gì?
CompetencyC02 — JavaScript Runtime
DepthL1–L2 (Recognize → Explain)
PrerequisitesStage 0: biến (const/let), hàm (function), console.log
Thời gian ước tính25–35 phút

1. Why This Exists (Vì sao cần học) ​

Bạn đã viết được JavaScript. Bạn khai báo biến, định nghĩa hàm, gọi console.log. Nhưng có một câu hỏi mà hầu hết developer junior đều bỏ qua:

Code của bạn chạy ở đâu?

Khi bạn viết:

js
const name = "An";

function greet() {
  const message = "Hello " + name;
  console.log(message);
}

greet();

message xuất hiện ở đâu? Tại sao name bên trong greet() vẫn đọc được dù nó được khai báo bên ngoài? Và tại sao nếu bạn khai báo message bên trong greet(), bên ngoài lại không thấy?

Nếu không có mental model về nơi code thực sự chạy, bạn sẽ:

  • Không hiểu tại sao biến "biến mất" khi hàm kết thúc
  • Không hiểu tại sao biến global lại truy cập được mọi nơi
  • Không thể debug lỗi ReferenceError một cách có hệ thống
  • Không thể học Closure, Scope, hay Call Stack ở các bài sau

Bài này xây nền tảng trả lời: JavaScript không chạy trong khoảng không. Nó chạy bên trong một container gọi là Execution Context.

2. Prerequisites (Yêu cầu đầu vào) ​

Bạn cần đã biết:

  • Khai báo biến bằng const và let
  • Định nghĩa và gọi hàm bằng function
  • console.log để in ra giá trị

Nếu chưa biết các kỹ năng trên, hãy quay lại Stage 0 trước khi tiếp tục.

3. Learning Objectives (Mục tiêu học tập) ​

Sau bài này, bạn có thể:

  1. Giải thích Execution Context bằng lời của chính mình, không đọc lại định nghĩa
  2. Phân biệt Global Execution Context và Function Execution Context trong một đoạn code
  3. Dự đoán thời điểm một Function Execution Context được tạo ra và bị xóa đi
  4. Vẽ sơ đồ đơn giản về ngữ cảnh thực thi cho code có 2–3 lời gọi hàm lồng nhau

4. Mental Model (Mô hình tư duy) ​

Execution Context = Container chạy code

Đừng nghĩ JavaScript "chạy trên máy tính". Hãy nghĩ: mọi dòng code JavaScript đều chạy bên trong một container. Container đó gọi là Execution Context.

Mỗi container có:

  • Các biến và hàm được khai báo trong nó
  • Một "liên kết" ra bên ngoài (để tìm biến không có trong nó)
  • Một điểm bắt đầu và kết thúc

Mô hình đơn giản:

text
JavaScript Code
      ↓
[Execution Context]  ← container chứa code + biến + liên kết ngoài
      ↓
Engine thực thi
      ↓
Result

💡 Hình dung như khách sạn: Global Context là phòng chung (lobby) — ai cũng vào được, đồ để ở đây ai cũng thấy. Function Context là phòng riêng — mỗi lần gọi hàm là nhận một phòng mới, đồ để trong phòng chỉ có người ở phòng đó dùng được. Khi trả phòng (hàm kết thúc), đồ trong phòng biến mất theo.

Có 3 loại container:

text
┌─────────────────────────────────────┐
│  Global Execution Context           │  ← luôn tồn tại đầu tiên
│  (Toàn bộ script/file)              │
├─────────────────────────────────────┤
│  Function Execution Context         │  ← tạo mới mỗi lần gọi hàm
│  (Mỗi lần gọi một function)         │
├─────────────────────────────────────┤
│  Eval Execution Context             │  ← awareness, không khuyến khích
│  (Khi dùng eval())                  │
└─────────────────────────────────────┘

Scope ≠ Execution Context

Trong bài này, bạn chỉ cần biết code chạy trong container. Đừng nhầm container này với "scope" hay "call stack". Scope (phạm vi biến) và Call Stack (ngăn xếp lời gọi) sẽ được dạy riêng ở Module 1.2 và 1.5.

5. Core Concepts (Các khái niệm cốt lõi) ​

Essential (Bắt buộc) ​

  • Execution Context: Môi trường thực thi nơi JavaScript engine đánh giá và thực thi code
  • Global Execution Context (GEC): Context mặc định khi script bắt đầu chạy. Có duy nhất một GEC cho mỗi script (trong trường hợp đơn giản)
  • Function Execution Context (FEC): Context được tạo ra mỗi khi một hàm được gọi. Mỗi lần gọi = một context mới

Supporting (Hỗ trợ) ​

  • Mỗi Function Execution Context tạo ra một "không gian" riêng cho biến local
  • Các context có thể tồn tại đồng thời khi hàm gọi hàm (nested calls)

Awareness (Biết tồn tại) ​

  • Eval Execution Context: Tạo ra khi dùng eval(). Bạn sẽ gặp trong tài liệu nhưng không nên dùng trong production.

Out of Scope (Không học trong bài này) ​

  • Creation Phase vs Execution Phase (bài 1.1.4)
  • Lexical Environment và Environment Record (Module 1.2)
  • Call Stack push/pop chi tiết (Module 1.5)
  • this binding (Stage 2)

6. Worked Example (Ví dụ phân tích từng bước) ​

Xem đoạn code sau:

js
const site = "JEM";

function logStatus(user) {
  const status = "learning";
  console.log(`${user} is ${status} at ${site}`);
}

logStatus("An");
console.log("Done");

Bước 1 — Quan sát (Observation) Script bắt đầu chạy. Engine nhìn thấy const site, function logStatus, và hai lời gọi ở cuối.

Bước 2 — Phân loại (Classification)

  • site và logStatus thuộc Global Execution Context
  • user và status thuộc Function Execution Context của logStatus

Bước 3 — Lý luận từng bước (Reasoning)

text
1. Global Execution Context được tạo
   → site = "JEM"
   → logStatus = function

2. logStatus("An") được gọi
   → Tạo Function Execution Context mới cho logStatus
   → user = "An"
   → status = "learning"

3. Bên trong logStatus, console.log chạy
   → user lấy từ context hiện tại
   → status lấy từ context hiện tại
   → site không có trong context hiện tại
     → engine nhìn ra ngoài (qua liên kết outer) → tìm thấy ở Global Context

4. logStatus kết thúc
   → Function Execution Context của logStatus bị xóa
   → user và status không còn tồn tại

5. console.log("Done") chạy ở Global Context

Bước 4 — Kết luận (Conclusion) Mỗi lần gọi hàm tạo ra một container mới. Biến bên trong container chỉ sống trong container đó. Khi hàm kết thúc, container và biến bên trong biến mất.

7. Prediction Exercise (Bài tập dự đoán) ​

Đừng chạy code. Đọc và trả lời 3 câu hỏi:

js
const level = "global";

function outer() {
  const level = "outer";
  inner();

  function inner() {
    const level = "inner";
    console.log(level);
  }
}

outer();
console.log(level);

Câu hỏi:

  1. Có bao nhiêu Execution Context được tạo ra trong đoạn code này? (kể cả Global)
  2. Dòng console.log(level) bên trong inner() in ra gì? Nó lấy từ context nào?
  3. Dòng console.log(level) cuối cùng in ra gì? Nó lấy từ context nào?
[Đáp án & Giải thích]
  • Bạn nghĩ

    1. Có 3 context: Global, outer(), và inner().
    2. In ra "inner" vì inner() có biến level trong chính context của nó.
    3. In ra "global" vì dòng cuối chạy ở Global Context, nơi level = "global".
  • Giải thích

    • outer() được gọi → tạo FEC cho outer
    • inner() được gọi bên trong outer → tạo FEC cho inner
    • inner có const level = "inner" trong context của chính nó
    • Khi console.log(level) chạy, engine tìm trong context hiện tại (inner) → thấy ngay
    • Dòng cuối cùng chạy ở Global Context, sau khi outer() đã kết thúc. Biến level ở đây là "global".

8. Implementation Lab (Bài lab thực hành) ​

Lab 1 — Gán nhãn Context (Guided) ​

Cho đoạn code sau. Hãy thêm comment vào mỗi dòng để gán nhãn [Global] hoặc [Function: tên-hàm]:

js
const app = "JEM";

function start() {
  const user = "An";
  console.log(user + " starts " + app);
}

function end() {
  console.log("end");
}

start();
end();

Gợi ý

  • Dòng 1 (const app) thuộc Global
  • Dòng 3 (const user) thuộc Function: start
  • console.log bên trong start cũng thuộc Function: start

Lab 2 — Đếm Context (Independent) ​

Lab 2 — Đếm Context (Independent) ​

Viết một đoạn code có đúng 4 Execution Context tồn tại trong quá trình chạy (1 Global + 3 Function). Không dùng recursion.

[Đáp án tham khảo]
  • Ví dụ đạt yêu cầu
js
function a() {
  b();
}

function b() {
  c();
}

function c() {
  console.log("deep");
}

a();
  • Giải thích
    1. Global Context (luôn có)
    2. Context của a() khi chạy
    3. Context của b() khi chạy (bên trong a)
    4. Context của c() khi chạy (bên trong b)
    • Tại thời điểm sâu nhất, có 4 context tồn tại đồng thời.

Lab 3 — Vẽ Context Stack (Independent) ​

Cho đoạn code sau. Hãy vẽ sơ đồ (dùng giấy hoặc text) biểu diễn Execution Context Stack tại thời điểm console.log bên trong deep() đang chạy. Ghi rõ:

  • Tên từng context
  • Biến local có trong mỗi context
  • Hướng "liên kết ra ngoài" (outer reference) của mỗi context
js
const app = "JEM";

function start() {
  const user = "An";
  middle();

  function middle() {
    const token = "abc";
    deep();

    function deep() {
      const level = 3;
      console.log(user + " " + app + " " + level);
    }
  }
}

start();
[Đáp án tham khảo]
text
┌─────────────────────────────┐
│  Global Execution Context   │
│  app = "JEM"                │
│  start = function           │
└──────────────┬──────────────┘
               │ outer
┌──────────────▼──────────────┐
│  Function: start            │
│  user = "An"                │
│  middle = function          │
└──────────────┬──────────────┘
               │ outer
┌──────────────▼──────────────┐
│  Function: middle           │
│  token = "abc"              │
│  deep = function            │
└──────────────┬──────────────┘
               │ outer
┌──────────────▼──────────────┐
│  Function: deep             │
│  level = 3                  │
│  console.log(...)           │
└─────────────────────────────┘
  • deep tìm user → không có trong deep → ra middle → không có → ra start → thấy user = "An"
  • deep tìm app → không có trong deep → ra middle → không có → ra start → không có → ra Global → thấy app = "JEM"

Gợi ý

Đừng lo về việc vẽ đẹp. Mục tiêu là kiểm tra xem bạn có nhận ra:

  • Mỗi hàm lồng nhau tạo thêm một "tầng" container
  • Biến level chỉ ở trong deep, token chỉ ở trong middle, user chỉ ở trong start
  • app nằm ở Global và được tìm thấy qua chuỗi liên kết ra ngoài

9. Edge Cases (Các trường hợp ngoại lệ) ​

Case 1 — Gọi hàm nhiều lần tạo nhiều context khác nhau ​

js
function visit() {
  const count = Math.random();
  console.log(count);
}

visit();
visit();
  • What fails? Không có lỗi, nhưng learner thường tưởng tượng count bị "ghi đè"
  • Why? Hai lần gọi visit() tạo ra hai Function Execution Context hoàn toàn riêng biệt
  • How to observe? Chạy code và thấy hai giá trị count khác nhau, không ảnh hưởng lẫn nhau
  • How to fix/decide? Không cần fix. Đây là behavior đúng. Nhưng cần hiểu để tránh giả định sai về "shared local state". Nếu cần chia sẻ state giữa các lần gọi, bạn sẽ cần pattern khác (sẽ học ở Module 1.4 — Closure).

Case 2 — IIFE tạo context rồi biến mất ngay ​

js
(function setup() {
  const secret = "hidden";
  console.log(secret);
})();
  • What happens? Function Execution Context của setup được tạo, chạy, rồi bị xóa ngay lập tức
  • Why it matters? Biến secret biến mất hoàn toàn sau khi IIFE kết thúc. Đây là nền tảng của pattern "private scope" trước khi ES6 có let/const

10. Debug Lab (Bài lab gỡ lỗi) ​

Không bắt buộc ở Depth L1–L2

Debug Lab chính thức bắt buộc từ L3 trở lên. Tuy nhiên, bài sau đây giúp bạn tập thói quen liên kết lỗi với Context.

Symptom (Triệu chứng): Một đồng nghiệp junior than phiền: "Tôi khai báo const x = 10 trong hàm foo(), sao bên ngoài gọi console.log(x) bị lỗi?"

Reproduction (Tái hiện lỗi):

js
function foo() {
  const x = 10;
}

foo();
console.log(x);

Evidence (Bằng chứng): Lỗi ReferenceError: x is not defined

Hypothesis (Giả thuyết): x chỉ tồn tại trong Function Execution Context của foo(). Khi foo() kết thúc, context bị xóa, biến x không còn tồn tại ở Global Context.

Verification (Xác minh): Di chuyển console.log(x) vào bên trong foo() — lỗi biến mất.

Root Cause (Nguyên nhân gốc rễ): Nhầm lẫn giữa lifetime của biến (gắn với Execution Context) và khả năng nhìn thấy (gắn với Scope). Biến khai báo bằng const/let bên trong hàm chỉ sống trong context của hàm đó.

Prevention (Phòng ngừa): Nếu cần dùng x ở ngoài, phải return giá trị hoặc khai báo ở Global Context (với cân nhắc về global pollution).

11. Design Exercise (Bài tập thiết kế giải pháp) ​

Không áp dụng ở Depth L1–L2. Design Exercise bắt đầu từ L5–L6.

12. Production Scenario (Tình huống thực tế) ​

Context: Một dự án legacy có nhiều file JavaScript được nối với nhau bằng thẻ <script>.

Constraint: Không dùng module system (ESM/CJS). Mọi biến đều rơi vào Global Execution Context.

Symptom: Hai file cùng khai báo const config = {...} ở global scope. File sau đè lên file trước. Ứng dụng chạy sai ở production.

Decision cần đưa ra: Tại sao đặt biến ở Global Context lại nguy hiểm? Và nếu bạn hiểu Execution Context, bạn sẽ chọn giải pháp nào để cô lập biến?

Gợi ý

Nếu mọi thứ đều ở Global Context, mọi file đều "chia sẻ" cùng một container. Một biến trùng tên ở file B sẽ đè lên file A. Giải pháp: dùng IIFE hoặc ESM để tạo Function Execution Context riêng, cô lập biến.

13. AI-Assisted Exercise (Bài tập với AI) ​

Level A — Ask

  1. Tự trả lời trước: Viết ra giấy định nghĩa Execution Context của bạn bằng 2–3 câu. Phân biệt Global và Function Execution Context.
  2. Hỏi AI: Dùng prompt: "Explain JavaScript Execution Context in simple terms. Distinguish Global and Function Execution Context."
  3. So sánh: Đối chiếu câu trả lời của AI với câu trả lời của bạn. AI có nhắc đến "container" hay "where code runs" không? AI có phân biệt rõ "mỗi lần gọi hàm = context mới" không?
  4. Verify: Mở MDN hoặc ECMAScript specification, tìm từ khóa "execution context". Kiểm chứng xem AI có bỏ sót Host Environment hay không.

Gợi ý

Để ý xem AI có dùng từ mơ hồ như "cung cấp môi trường" hay "built-in object" không. Nhiều AI sẽ bỏ qua việc giải thích tại sao Function Execution Context lại được tạo ra, hoặc nhầm lẫn Execution Context với Call Stack. Nếu bạn phát hiện được điểm mù này, bạn đã hiểu sâu hơn AI.

Đáp án tham khảo
  • Bạn nghĩ Execution Context là cái "hộp" chứa code đang chạy. Có hai loại chính: Global (hộp to nhất, chứa toàn bộ script) và Function (hộp nhỏ hơn, tạo ra mỗi khi gọi hàm). Khi hàm kết thúc, hộp nhỏ bị dẹp đi.

  • AI trả lời

    • "An execution context is an abstract concept that holds information about the environment within which the current code is being executed."
    • "There are three types: Global, Function, and Eval."
    • "The global execution context is created by default. Every time a function is invoked, a new function execution context is created."
    • "It contains variable objects, scope chain, and the value of this."
  • So sánh

    • AI đúng về số lượng loại context và cơ chế tạo mới mỗi lần gọi hàm.
    • AI dùng từ "abstract concept" — đúng nhưng khó hình dung. Bạn dùng "hộp/container" — dễ hình dung hơn và vẫn đúng ở level này.
    • AI nhắc đến "variable objects, scope chain, this" — đây là implementation detail thuộc Module 1.2 và Stage 2. Nếu bạn chưa học các khái niệm này, câu trả lời của AI sẽ gây confusion.
  • Điểm AI nói sai hoặc quá mơ hồ

    • AI thường gộp Execution Context với các khái niệm tìm biến giữa các container (sẽ học ở Module 1.2) như thể chúng là cùng một thứ. Trong curriculum này, chúng ta phân biệt rõ: Execution Context là container, còn cơ chế tìm biến giữa các container là chủ đề riêng.
    • AI thường không nhắc đến việc Function Execution Context bị xóa khi hàm return, dẫn đến learner nghĩ biến local vẫn "tồn tại đâu đó".
  • Kết luận Nếu bạn phát hiện AI đang nhét implementation detail (this, scope chain) vào định nghĩa đầu tiên, bạn đã hiểu rằng: mental model cần đơn giản trước, chi tiết sau. Đây chính là nguyên tắc của cả khóa học. Concept này sẽ được mở rộng ở Module 1.2 (Lexical Environment) và Stage 2 (this binding).

14. Teach Back (Dạy lại) ​

Yêu cầu: Giả sử đồng nghiệp mới của bạn hỏi: "Khi tôi gọi một hàm, JavaScript xảy ra chuyện gì? Tại sao biến bên trong hàm lại không ra ngoài được?" Hãy giải thích trong 2 phút, dùng đúng thuật ngữ: Execution Context, Global, Function.

Mô phỏng
  • Bạn nói Khi bạn gọi một hàm, JavaScript tạo ra một Function Execution Context — một container mới chỉ dành cho lần gọi đó. Mọi biến khai báo bằng const/let bên trong hàm đều sống trong container đó. Khi hàm chạy xong và return, container bị dẹp đi, biến bên trong cũng biến mất theo. Đó là lý do bạn không thể truy cập x từ bên ngoài nếu x chỉ được khai báo bên trong hàm. Còn khi script vừa chạy, engine tạo ra Global Execution Context — cái hộp lớn nhất chứa toàn bộ code không nằm trong hàm nào.

💡 Hình dung như thuê một phòng khách sạn: mỗi lần gọi hàm là nhận một phòng mới. Bạn để đồ trong phòng đó. Khi trả phòng, đồ cũng biến mất theo. Phòng chung (Global) thì ai cũng vào được, nhưng đồ để trong phòng riêng (Function) thì chỉ có người ở phòng đó mới lấy được.

Gợi ý đánh giá bản thân

  • Đồng nghiệp có hiểu tại sao biến "biến mất" không? Nếu họ hỏi lại "vậy để đồ ra ngoài hành lang thì sao?" — bạn có thể giải thích bằng return hoặc global variable không?
  • Bạn có dự đoán được case mới không? Ví dụ: nếu hàm A gọi hàm B, có mấy container tồn tại cùng lúc?
  • Bạn có trả lời được câu hỏi phụ: "Vậy gọi hàm 3 lần thì có mấy container?" không?

15. Assessment (Đánh giá) ​

Objective (Mục tiêu)Hình thức đánh giáNội dung
Giải thích Execution ContextExplain (Giải thích)Dùng lời riêng, không đọc định nghĩa
Phân biệt Global vs FunctionClassification (Phân loại)Cho đoạn code 15 dòng, gán nhãn đúng context
Dự đoán tạo/xóa contextPrediction (Dự đoán)Cho 3 đoạn code có lời gọi hàm, đoán số context tồn tại
Vẽ ngữ cảnh thực thiTrace (Truy vết)Vẽ sơ đồ 2–3 lớp context lồng nhau, vẽ stack và biến local

16. Exit Criteria (Tiêu chí qua bài) ​

  • [ ] Có thể giải thích Execution Context là "container nơi code chạy" mà không nhìn tài liệu
  • [ ] Có thể phân biệt Global Execution Context và Function Execution Context trong code thực tế
  • [ ] Có thể dự đoán đúng thời điểm Function Execution Context được tạo và bị xóa (≥ 2/3 scenario đúng)
  • [ ] Có thể vẽ/viết mô tả context stack cho code có 2 lời gọi hàm lồng nhau, bao gồm biến local và outer reference

17. Spiral Connection (Liên kết xoắn ốc) ​

Previous (Trước): Stage 0 — Bạn đã biết viết JavaScript cơ bản: biến, hàm, console.log. Nhưng bạn chưa biết chúng chạy ở đâu.

Current (Hiện tại): Bạn hiểu Execution Context là container chứa code và biến. Bạn biết Global Context luôn tồn tại, và mỗi lần gọi hàm tạo ra Function Context mới.

Next (Tiếp theo):

  • 1.1.2 — Global Execution Context chi tiết
  • 1.1.3 — Function Execution Context chi tiết
  • 1.2.x — Scope & Lexical Environment (biến tìm nhau như thế nào giữa các context)
  • 1.4.x — Closure (hàm "nhớ" context bên ngoài dù context đó đã kết thúc)
  • S3 — Async callbacks (context và thời gian)
  • S8 — React Hooks (functional component = function = context mỗi lần render)
📴 Offline Mode — Content served from cache