Lesson 0.1.3 — JavaScript Execution at Conceptual Level (JavaScript Execution ở mức khái niệm)
0. Metadata (Thông tin bài học)
| Key | Value |
|---|---|
| Stage | 0 — JavaScript Language Foundation |
| Module | 0.1 — JavaScript & Runtime Fundamentals |
| Lesson | 0.1.3 — JavaScript Execution ở mức khái niệm |
| Competency | C01 — JavaScript Language, C02 — JavaScript Runtime |
| Depth Target | L2–L3 (Explain → Use) |
| Prerequisites | Lesson 0.1.1 — JavaScript là gì?, Lesson 0.1.2 — Browser vs Node.js |
| Estimated Cognitive Load | Low–Medium |
1. Why This Exists (Vì sao cần học)
Bạn viết file app.js:
const user = { name: "A"
console.log(user.name);Chạy file → lỗi ngay. Nhưng để ý: console.log chưa kịp chạy. Thậm chí nếu bạn đặt console.log("START") ở dòng đầu tiên, nó cũng không được in ra.
Tại sao? Vì JavaScript không "chạy từng dòng" theo nghĩa đen như interpreter đọc sách. Engine có một phase chuẩn bị trước khi bất kỳ dòng code nào được thực thi.
Nếu không hiểu flow này, bạn sẽ:
- Không hiểu tại sao lỗi syntax ở cuối file lại dừng cả file.
- Confuse giữa lỗi viết sai chính tả (syntax) và lỗi logic chạy (runtime).
- Không giải thích được tại sao function declaration có thể gọi trước định nghĩa.
Lesson này xây mental model sơ cấp về cách engine xử lý code từ file text đến behavior.
2. Prerequisites (Yêu cầu đầu vào)
Trước khi học bài này, bạn cần biết:
- JavaScript Engine là gì và nó nằm ở đâu trong runtime.
- ECMAScript là language rules, không phụ thuộc runtime.
- Browser và Node.js đều dùng engine để chạy code.
Nếu chưa rõ, quay lại Lesson 0.1.1 và 0.1.2.
3. Learning Objectives (Mục tiêu học tập)
Sau bài này, bạn có thể:
- Giải thích flow cơ bản: Source Code → Parse → Execute.
- Phân biệt Syntax Error (parse phase) và Runtime Error (execution phase).
- Dự đoán một lỗi xảy ra ở phase nào trước khi chạy code.
- Giải thích tại sao lỗi syntax ở dòng cuối có thể dừng cả file.
4. Mental Model (Mô hình tư duy)
Three-Phase Execution Model (Mô hình 3 giai đoạn xử lý)
Đây là mental model pedagogical, không phải formal specification đầy đủ của ECMAScript.
Ở mức khái niệm, quá trình thực thi JavaScript có thể được hình dung qua ba bước chính.
- Đầu tiên là Source Code, tức mã JavaScript mà lập trình viên viết trong file
.js. - Tiếp theo là Parse Phase, khi JavaScript Engine phân tích cấu trúc cú pháp của mã nguồn, kiểm tra xem code có hợp lệ và xây dựng biểu diễn nội bộ để engine có thể xử lý. Nếu gặp Syntax Error, chương trình không thể tiếp tục thực thi theo luồng bình thường.
- Cuối cùng là Execution Phase, khi engine thực thi chương trình, chẳng hạn tạo và cập nhật biến, gọi function và thực hiện các phép toán. Trong quá trình này, các Runtime Error có thể phát sinh khi một operation không thể thực hiện tại runtime, chẳng hạn truy cập thuộc tính trên
null.
Đây là một mental model ở mức khái niệm; các bước xử lý thực tế bên trong từng JavaScript Engine có thể phức tạp hơn.
Quy tắc nhớ:
- Parse Phase xảy ra trước và nhanh hơn bạn nghĩ.
- Syntax Error = engine không hiểu code viết gì → dừng trước khi execute.
- Runtime Error = code viết đúng chính tả nhưng operation không hợp lệ → lỗi khi đang chạy.
5. Core Concepts (Các khái niệm cốt lõi)
Essential (Bắt buộc)
5.1 Source Code (Mã nguồn)
File .js là plain text. Engine không "chạy" text — engine phải đọc và biên dịch thành cấu trúc nội bộ.
5.2 Parse Phase (Giai đoạn phân tích)
Engine đọc toàn bộ file từ đầu đến cuối:
- Kiểm tra cú pháp có hợp lệ không (dấu ngoặc có đủ không, từ khóa có đúng không).
- Nếu hợp lệ: tạo internal representation để chuẩn bị execute.
- Nếu không hợp lệ: throw
SyntaxErrorvà dừng hoàn toàn.
TIP
Parse phase giống như biên tập viên đọc lại toàn bộ bản thảo trước khi in. Nếu thấy lỗi chính tả ở trang cuối, bản thảo không được in dù trang đầu hoàn hảo.
5.3 Execution Phase (Giai đoạn thực thi)
Sau khi parse thành công, engine bắt đầu:
- Chạy từng dòng từ trên xuống.
- Tạo bindings (variables).
- Thực hiện operations.
- Gọi functions.
Lỗi ở phase này gọi là Runtime Error (ví dụ: TypeError, ReferenceError).
5.4 Syntax Error vs Runtime Error
| Đặc điểm | Syntax Error | Runtime Error |
|---|---|---|
| Phase | Parse Phase | Execution Phase |
| Ví dụ | Thiếu }, sai từ khóa | null.toString(), gọi hàm undefined |
| Dừng code? | Dừng toàn bộ file | Dừng tại dòng lỗi |
| Catch được? | Không (trong cùng file) | Có (bằng try/catch) |
Supporting (Hỗ trợ)
- Tokenization: Engine chia text thành các "token" nhỏ nhất (keywords, identifiers, operators). Đây là bước đầu tiên của parse.
- AST (Abstract Syntax Tree): Cấu trúc cây nội bộ engine tạo ra từ source code. Bạn không cần vẽ AST, chỉ cần biết engine chuyển text thành cấu trúc có ý nghĩa trước khi chạy.
Awareness (Biết tồn tại)
- Hoisting: Trước khi dòng code đầu tiên chạy, engine đã tạo sẵn binding cho function declarations. Điều này không xảy ra trong Parse Phase — lý do thực sự thuộc về Execution Context creation, sẽ đào sâu ở Stage 1.
- JIT (Just-In-Time) Compilation: Engine có thể biên dịch code thành machine code trong lúc chạy để tối ưu. Thuộc Stage 11.
- Bytecode: Một số engine tạo bytecode trung gian thay vì chạy trực tiếp AST. Thuộc Stage 11.
Out of Scope (Không học trong bài này)
- Execution Context internals.
- Lexical Environment / Scope chain.
- Hoisting mechanism chi tiết.
- V8 hidden classes, TurboFan, Ignition.
- Garbage Collection internals.
6. Worked Example (Ví dụ phân tích từng bước)
Đoạn code:
const greeting = "hello";
console.log(greeting);Phân tích từng bước:
Step 1 — Source Code: File text chứa 2 dòng code.
Step 2 — Parse Phase:
- Dòng 1:
const(keyword hợp lệ) +greeting(identifier) +=(operator) +"hello"(string literal) +;(terminator). Syntax OK. - Dòng 2:
console(identifier) +.(member access) +log(property) +(+greeting+)+;. Syntax OK. - Engine tạo internal representation. Không có lỗi → chuyển sang Execution.
Step 3 — Execution Phase:
- Dòng 1: Tạo binding
greetingtrong memory, gán value"hello". - Dòng 2: Resolve
console→ tìm thấy host API. Gọilogvới argument"hello". In ra terminal.
Kết luận: Code chạy thành công qua cả 2 phase.
7. Prediction Exercise (Bài tập dự đoán)
Đừng chạy code. Xác định: (a) Có lỗi không? (b) Nếu có, là lỗi gì? (c) Xảy ra ở phase nào?
Câu 1:
const message = "hi"
console.log(message[Đáp án & Giải thích]
(a) Có lỗi. (b) SyntaxError: missing ) after argument list. (c) Parse Phase — thiếu dấu ) ở cuối dòng 2. Engine parse toàn file, phát hiện unbalanced parentheses trước khi execute. Dòng 1 chưa kịp chạy.
Câu 2:
const num = 10;
console.log(num.toUpperCase());[Đáp án & Giải thích]
(a) Có lỗi. (b) TypeError: num.toUpperCase is not a function. (c) Execution Phase — syntax hoàn toàn hợp lệ. Lỗi xảy ra khi engine đang chạy dòng 2: number không có method toUpperCase().
Câu 3:
greet();
function greet() {
console.log("hello");
}[Đáp án & Giải thích]
(a) Không lỗi. In ra "hello". (b) Không có lỗi. (c) Execution Phase — nhưng function declaration đã được "chuẩn bị" trước khi dòng đầu tiên thực thi. Đây là awareness preview cho hoisting ở Stage 1. Mental model hiện tại: engine có một bước chuẩn bị (trước khi chạy từng dòng) để tạo sẵn các function bindings. Lý do chính xác thuộc về Execution Context creation, học ở Stage 1.
Câu 4:
const data = null;
console.log(data.name);[Đáp án & Giải thích]
(a) Có lỗi. (b) TypeError: Cannot read properties of null. (c) Execution Phase — syntax đúng. Lỗi xảy ra khi dereference null.
8. Implementation Lab (Bài lab thực hành)
Nhiệm vụ: Cho 4 đoạn code, xác định lỗi (nếu có) và phase xảy ra lỗi.
// A
const x = {// B
const x = 1;
x.toUpperCase();// C
function greet() {
return "hi";
}
greet();// D
const nums = [1, 2, 3];
console.log(nums[5].toString());[Đáp án & Giải thích]
| Đoạn | Lỗi? | Loại lỗi | Phase |
|---|---|---|---|
| A | Có | SyntaxError | Parse |
| B | Có | TypeError | Execution |
| C | Không | — | — |
| D | Có | TypeError | Execution |
Giải thích D: nums[5] là undefined. undefined.toString() là runtime error. Syntax hoàn toàn hợp lệ.
9. Edge Cases (Các trường hợp ngoại lệ)
"Chạy từ trên xuống" là oversimplified
Mental model "JavaScript chạy từ trên xuống" chỉ đúng trong Execution Phase. Nhưng engine đã đọc toàn bộ file trong Parse Phase trước đó.
Hệ quả:
console.log("A");
// ... 100 dòng code ...
const config = {
port: 3000
// thiếu }; để đóng object literalDù lỗi syntax ở dòng cuối, "A" cũng không được in. Vì parse phase xảy ra trước execution phase.
try/catch không bắt được SyntaxError trong cùng file
try {
const config = {
port: 3000,
host: "localhost" // thiếu }; để đóng object literal
const timeout = 5000;
} catch (e) {
console.log("caught");
}Đoạn này vẫn throw SyntaxError và crash.
Tại sao? Lỗi syntax nằm ở object literal chưa đóng (thiếu } sau "localhost"). Khi parser đọc đến const timeout = 5000;, nó đang ở trong ngữ cảnh object literal — const không hợp lệ ở đó. Parse phase thất bại tại dòng này.
try/catch là cấu trúc execution-time. Nếu parse phase đã thất bại trước khi file được biên dịch thành internal representation, execution phase không bao giờ bắt đầu, và try/catch không bao giờ được thiết lập để bắt lỗi.
Phân biệt quan trọng: Lỗi ở đây không phải do try/catch viết sai — cấu trúc try/catch hoàn toàn hợp lệ. Lỗi là do expression bên trong try block có syntax error, và expression đó được parse cùng lúc với toàn bộ file.
10. Debug Lab (Bài lab gỡ lỗi)
Tình huống: Bạn chạy file server.js và nhận lỗi ngay lập tức. Không một dòng console.log nào được in ra.
Symptom (Triệu chứng):
SyntaxError: Unexpected end of input
at Object.compileFunction (vm:360:18)Reproduction (Tái hiện lỗi):
const config = {
port: 3000,
host: "localhost"
// thiếu dấu };
console.log("Server starting...");Evidence (Bằng chứng): "Server starting..." không xuất hiện trong output. Stack trace trỏ đến cuối file.
Hypothesis (Giả thuyết): Lỗi syntax trong Parse Phase. Engine không thể xây dựng internal representation hợp lệ nên dừng lại hoàn toàn trước Execution Phase.
Verification (Xác minh):
- Thêm
};vào cuối object literal. - Chạy lại →
"Server starting..."được in ra.
Root Cause (Nguyên nhân gốc rễ): Nhầm lẫn giữa thứ tự đọc code của engine với thứ tự chạy code. Tưởng rằng console.log ở trên sẽ chạy trước khi engine "đọc đến" dòng dưới.
Fix (Sửa): Thêm }; để object literal syntax hợp lệ.
Prevention (Phòng ngừa):
- Khi thấy lỗi trước cả khi code logic bắt đầu → nghĩ đến Parse Phase / Syntax Error.
- Dùng linter (ESLint) để bắt lỗi syntax trước khi chạy.
11. Decision Exercise (Bài tập quyết định)
Không áp dụng ở depth L2–L3 cho conceptual lesson này.
12. Production Scenario (Tình huống thực tế)
Context (Bối cảnh): CI/CD pipeline build project frontend bị fail. Log hiển thị:
SyntaxError: Unexpected token '}'
File: /src/utils/config.jsSymptom (Triệu chứng): Build dừng ngay lập tức. Không có artifact được tạo ra.
Root Cause (Nguyên nhân): File config.js có lỗi syntax (dư dấu }). Build tool (Vite/Webpack/Node.js) phải parse file trước khi bundle/execute. Parse phase error là blocking — toàn bộ build fail.
Lesson: Parse phase errors are infrastructure-blocking. Không thể "runtime handle" syntax error. Đây là lý do linter và type-checking phải chạy trước build trong production pipeline.
13. AI-Assisted Exercise (Bài tập với AI)
Level A — Ask (Hỏi)
- Tự trả lời: Viết 2 ví dụ: một gây
SyntaxError, một gâyTypeError. Giải thích phase nào xảy ra lỗi. - Hỏi AI: "Hãy giải thích sự khác biệt giữa Syntax Error và Runtime Error trong JavaScript."
- So sánh: AI có nhắc đến Parse Phase và Execution Phase không? Nếu AI chỉ nói "một cái viết sai, một cái chạy sai", bạn hãy bổ sung mental model 2 phase của bản thân.
- Verify: Dùng MDN hoặc ECMAScript specification để xác nhận.
Gợi ý
AI thường trả lời kiểu "SyntaxError là lỗi cú pháp, RuntimeError là lỗi khi chạy". Hãy kiểm tra xem AI có nhắc đến engine phải parse toàn bộ file trước khi execute dòng đầu tiên không. Nếu không, bạn đang hiểu sâu hơn AI.
Đáp án tham khảo
- Bạn nghĩ
Ví dụ 1 — SyntaxError:
const x = ;Engine đọc dòng này, không hiểu = ; là gì. Lỗi xảy ra ngay ở Parse Phase — engine chưa chạy một dòng code nào cả.
Ví dụ 2 — TypeError:
const x = null;
x.toUpperCase();Dòng 1 parse OK. Dòng 2 cũng parse OK (cú pháp đúng). Nhưng khi engine execute dòng 2, nó phát hiện null không có method toUpperCase. Lỗi xảy ra ở Execution Phase.
- AI trả lời
AI thường nói:
"SyntaxError là lỗi cú pháp, xảy ra khi bạn viết code sai quy tắc ngôn ngữ — ví dụ thiếu dấu ngoặc hoặc dấu chấm phẩy. Runtime Error (như TypeError, ReferenceError) là lỗi xảy ra khi chương trình đang chạy, thường do thao tác trên giá trị không hợp lệ hoặc truy cập biến không tồn tại."
- So sánh
AI đúng về bề mặt: một cái "viết sai", một cái "chạy sai". Nhưng AI hiếm khi nhắc đến Parse Phase và Execution Phase như hai giai đoạn riêng biệt trong engine. AI không giải thích tại sao SyntaxError luôn xảy ra trước bất kỳ dòng code nào được chạy — kể cả dòng đó nằm ở cuối file.
- Điểm AI nói sai hoặc quá mơ hồ
Câu "SyntaxError xảy ra khi bạn viết code sai" gợi ý rằng engine đọc đến đâu thì báo lỗi đến đó. Thực tế, engine phải parse toàn bộ scope trước khi execute. Nghĩa là một SyntaxError ở dòng cuối cùng của file sẽ khiến toàn bộ file không chạy được, dù dòng đầu tiên hoàn toàn đúng. AI hiếm khi nhấn mạnh điểm này.
Ngoài ra, AI thường gộp chung TypeError, ReferenceError, RangeError thành "Runtime Error" mà không phân biệt chúng xảy ra ở Execution Phase — không phải một phase chung chung gọi là "lúc chạy".
- Kết luận
Nếu AI không nhắc đến Parse Phase và Execution Phase như hai bước engine thực hiện tuần tự, bạn đã có mental model chính xác hơn. Đây là nền tảng để hiểu tại sao hoisting và TDZ hoạt động như vậy ở Stage 1.
14. Teach Back (Dạy lại)
Trong 2 phút, giải thích cho một đồng nghiệp junior:
"Tại sao khi file JavaScript có lỗi thiếu dấu ngoặc nhọn ở cuối file, không một dòng code nào trong file đó được chạy — kể cả dòng
console.logở đầu file?"
Yêu cầu:
- Có nhắc đến Parse Phase.
- Có giải thích engine đọc toàn file trước khi chạy.
- Có phân biệt syntax error vs runtime error.
- Người nghe hiểu tại sao
try/catchkhông bắt được syntax error.
Mô phỏng
- Bạn nói
Tại sao lỗi thiếu ngoặc ở cuối file khiến cả dòng console.log đầu file cũng không chạy?
Vì Engine không chạy code ngay khi đọc đến đâu. Nó làm theo 2 phase: Parse Phase rồi mới đến Execution Phase.
Ở Parse Phase, Engine đọc toàn bộ file từ đầu đến cuối để kiểm tra cú pháp. Nó kiểm tra: dấu ngoặc có đủ không? Từ khóa có đúng chỗ không? Cấu trúc có hợp lệ không?
Nếu đến cuối file, Engine phát hiện thiếu dấu ngoặc nhọn — đó là SyntaxError. Parse Phase thất bại. Engine dừng luôn, chưa bước vào Execution Phase.
Nên dù dòng đầu tiên là console.log("hello") hoàn toàn đúng, nó cũng không được chạy. Vì Engine chưa bao giờ bắt đầu chạy.
Còn nếu là lỗi runtime, ví dụ null.toUpperCase(), thì khác. Parse Phase vẫn pass vì cú pháp đúng. Execution Phase chạy được dòng đầu, đến dòng lỗi mới dừng.
Nói chung: SyntaxError là lỗi trước khi chạy. Runtime error là lỗi khi đang chạy. Vì vậy try/catch không bắt được SyntaxError — try/catch hoạt động ở Execution Phase, mà SyntaxError xảy ra ở Parse Phase, trước cả khi try/catch được thiết lập.
💡 Dễ nhớ: Giống như đạo diễn đọc kịch bản phim. Nếu kịch bản thiếu trang cuối — dù cảnh đầu viết hay cỡ nào — đạo diễn cũng không bấm máy. Còn nếu kịch bản đầy đủ nhưng diễn viên quên lời ở cảnh giữa, cảnh đầu vẫn quay xong rồi mới cut.
Gợi ý đánh giá bản thân
- Đồng nghiệp có hiểu tại sao lỗi ở cuối file lại "giết" cả file không?
- Họ có phân biệt được SyntaxError (parse) và TypeError (runtime) không?
- Nếu họ hỏi "vậy
try/catchbao quanh cả file thì sao?" — bạn giải thích được không?
15. Assessment (Đánh giá)
| Objective (Mục tiêu) | Format (Hình thức đánh giá) | Depth |
|---|---|---|
| Giải thích 3-phase model | Explain (Giải thích) | L2 |
| Dự đoán phase xảy ra lỗi | Prediction (Dự đoán) | L2 |
| Phân biệt syntax/runtime error | Classification (Phân loại) | L2 |
| Debug parse-phase failure | Debug (Gỡ lỗi) | L3 |
16. Exit Criteria (Tiêu chí qua bài)
- [ ] Có thể vẽ hoặc viết flow Source → Parse → Execute.
- [ ] Có thể dự đoán đúng một lỗi xảy ra ở Parse Phase hay Execution Phase với độ chính xác ≥ 4/5 scenarios.
- [ ] Có thể giải thích tại sao SyntaxError dừng toàn bộ file trước khi bất kỳ dòng code nào chạy.
- [ ] Có thể phân biệt Syntax Error và Runtime Error qua stack trace và behavior.
- [ ] Có thể giải thích tại sao
try/catchkhông bắt được SyntaxError trong cùng file.
17. Spiral Connection (Liên kết xoắn ốc)
Previous (Trước): Browser vs Node.js — Bạn đã biết cùng engine parse & execute code, nhưng host APIs khác nhau. Giờ bạn hiểu cách engine xử lý code trước khi đụng đến API.
Current (Hiện tại): Execution Flow — Bạn có mental model 2 phase (Parse + Execute). Bạn biết engine không chạy code ngay mà có giai đoạn chuẩn bị.
Next (Tiếp theo):
- Stage 1 — Execution Model: Execution Context, Scope, Hoisting, TDZ. Bạn sẽ hiểu bên trong Execution Phase engine tạo ra gì và tại sao function declaration có thể gọi trước định nghĩa.
- Stage 1 — Call Stack: Bạn sẽ thấy code "chạy từ trên xuống" phức tạp hơn nhiều khi có function calls.
- Stage 11 — Performance: JIT, bytecode, hidden classes. Bạn sẽ hiểu engine optimize execution phase như thế nào.