Lesson 0.7.1 — Errors
0. Metadata
| Field | Value |
|---|---|
| Stage | 0 — JavaScript Language Foundation |
| Module | 0.7 — Error Handling & Code Quality |
| Lesson | 0.7.1 |
| Competency | C01.8 — Error Handling |
| Depth Target | L2–L3 |
| Prerequisites | Values & Types (0.2.1), Objects (0.6.5), Functions (0.5.1) |
| Estimated Cognitive Load | Medium |
1. Why This Exists (Vì sao cần học)
Bạn viết code, chạy, và console hiện ra:
TypeError: Cannot read properties of undefined (reading 'name')
at getUserName (app.js:15:21)
at renderProfile (app.js:28:12)Phản ứng phổ biến của Junior:
→ Copy toàn bộ dòng lỗi
→ Paste lên Google
→ Thử từng solution trên Stack Overflow
→ Sửa đến khi lỗi biến mấtVấn đề cốt lõi
Error không phải là "một thứ đỏ đỏ cần làm biến mất". Error là một object chứa thông tin có cấu trúc:
- What — chuyện gì xảy ra (
message) - Where — xảy ra ở đâu (
stack) - What kind — loại lỗi gì (
name/ constructor)
Nếu bạn không đọc được thông tin này, bạn sẽ tốn 30 phút Google một câu trả lời mà stack trace đã nói rõ trong 3 giây. Bài này dạy bạn đối xử với error như dữ liệu debug, không phải kẻ thù.
2. Prerequisites (Yêu cầu đầu vào)
Trước khi học bài này, bạn cần:
- Biết object có property và method (0.6.5).
- Biết
typeoftrả về"object"cho object (0.2.5). - Biết function call và argument passing (0.5.1).
- Hiểu
undefinedvànulllà giá trị hợp lệ trong JavaScript (0.2.3).
WARNING
Nếu bạn chưa chắc tại sao typeof null là "object" nhưng null không có property, quay lại 0.2.5. Điều này liên quan trực tiếp đến lỗi "Cannot read properties of null".
3. Learning Objectives (Mục tiêu học tập)
Sau bài này, bạn có thể:
- Giải thích error là một object chứa
message,stack, vàname. - Phân biệt 5 built-in error type phổ biến:
Error,TypeError,ReferenceError,RangeError,SyntaxError. - Dự đoán error type nào sẽ xuất hiện từ một đoạn code lỗi.
- Đọc một stack trace cơ bản để xác định file, function, và dòng gây lỗi.
- Tạo một
Errorobject với message mô tả rõ ràng. - Nhận diện sự khác biệt giữa runtime error và parse-time error.
4. Mental Model (Mô hình tư duy)
Mental Model
Error = Object chứa thông tin về sự cố
name → Loại lỗi (Error, TypeError, ReferenceError...)
message → Mô tả ngắn gọn vấn đề
stack → "Dấu vết" các function call dẫn đến lỗi
Stack Trace = Breadcrumb trail
at getUserName (app.js:15:21)
↓
Function getUserName, file app.js, dòng 15, cột 21
↓
Là nơi lỗi "nổ"
↓
Dòng dưới là function gọi getUserName
Dòng tiếp là function gọi function đó...Quy tắc vàng:
- Error là value. Bạn có thể gán nó vào biến, truyền nó như argument, inspect property của nó.
messagelà string.stacklà string (format phụ thuộc engine).namelà string khớp với tên constructor.- Không phải mọi lỗi đều có thể "bắt" bằng
try/catch(ví dụ: SyntaxError parse-time). throwcó thể ném bất kỳ value nào, nhưng best practice là némErrorobject.
5. Core Concepts (Các khái niệm cốt lõi)
Essential (Bắt buộc)
| Concept | Ý nghĩa |
|---|---|
Error | Constructor cơ bản. new Error("something went wrong"). |
message | String mô tả lỗi. Đọc dòng đầu tiên của stack trace. |
stack | String chứa lịch sử call stack. Giúp trace ngược về nguồn lỗi. |
name | Tên loại lỗi. "Error", "TypeError", "ReferenceError"... |
TypeError | Thao tác không hợp lệ trên một kiểu dữ liệu. Ví dụ: null.foo, undefined() |
ReferenceError | Tham chiếu đến biến không tồn tại. Ví dụ: console.log(notDeclared) |
RangeError | Giá trị nằm ngoài phạm vi cho phép. Ví dụ: new Array(-1) |
SyntaxError | Code vi phạm grammar của JavaScript. Thường xảy ra ở parse time. |
Supporting (Hỗ trợ)
| Concept | Ý nghĩa |
|---|---|
throw | Ném một value (thường là Error object). Preview cho 0.7.2. |
| Parse-time vs Runtime | SyntaxError thường xảy ra trước khi code chạy. Các lỗi khác xảy ra khi chạy. |
Awareness (Biết tồn tại)
| Concept | Lý do chưa đào sâu |
|---|---|
Error.cause | ES2022. Cho phép chain lỗi. Học khi cần production error wrapping (Stage 9/13). |
AggregateError | Gộp nhiều lỗi. Dùng với Promise.any. Thuộc Stage 3 (Async). |
| Engine-specific stack format | V8, SpiderMonkey, JSC format stack khác nhau. Không cần đọc spec ở Stage 0. |
Out of Scope (Không thuộc bài này)
- Custom error class (
class MyError extends Error). Thuộc Stage 2 (Object Model / Class). try/catch/finallymechanism. Thuộc 0.7.3.- Async error handling (Promise rejection, unhandled rejection). Thuộc Stage 3.
- React Error Boundary. Thuộc Stage 8.
6. Worked Example (Ví dụ phân tích từng bước)
Bài toán: Đọc và hiểu một stack trace từ lỗi thực tế.
function getUserName(user) {
return user.profile.name;
}
function renderProfile(user) {
const name = getUserName(user);
return `<h1>${name}</h1>`;
}
const user = { id: 1 };
console.log(renderProfile(user));Output (V8/Node.js):
TypeError: Cannot read properties of undefined (reading 'name')
at getUserName (app.js:2:21)
at renderProfile (app.js:6:20)
at Object.<anonymous> (app.js:10:13)Walkthrough
Step 1 — Xác định Error TypeTypeError. Điều này nghĩa là: JavaScript đang cố thực hiện một thao tác không hợp lệ trên một kiểu dữ liệu. Không phải biến không tồn tại (ReferenceError), cũng không phải cú pháp sai (SyntaxError).
Step 2 — Đọc MessageCannot read properties of undefined (reading 'name'). JavaScript đang cố truy cập .name trên một giá trị undefined. Giá trị undefined này là user.profile, vì user = { id: 1 } không có property profile.
Step 3 — Đọc Stack Trace (từ trên xuống)
at getUserName (app.js:2:21)— Lỗi nổ tại dòng 2, cột 21, trong functiongetUserName. Đây là điểm gốc.at renderProfile (app.js:6:20)—getUserNameđược gọi từrenderProfile, dòng 6.at Object.<anonymous> (app.js:10:13)—renderProfileđược gọi từ top-level, dòng 10.
💡 Key Insight: Stack trace là một snapshot đóng băng tại thời điểm error được tạo. Nó không thay đổi sau đó, dù có function nào khác chạy tiếp. Điều này quan trọng khi bạn log error async — stack trace ghi lại chính xác trạng thái call stack lúc lỗi xảy ra.
Step 4 — Kết luận Không cần Google. Không cần đoán. Dữ liệu đã nói rõ: user thiếu profile. Fix là đảm bảo user có cấu trúc đúng hoặc guard trước khi access.
Key Insight: Stack trace đọc từ trên xuống dưới (gần nhất → xa nhất). Dòng đầu tiên sau message là nơi lỗi thực sự xảy ra.
7. Prediction Exercise (Bài tập dự đoán)
Đừng chạy code. Đọc và dự đoán:
- (1) Có lỗi không?
- (2) Nếu có, error type và message gì?
- (3) Tại sao?
Câu 1
const user = null;
console.log(user.name);Câu 2
const err = new Error("Connection failed");
console.log(typeof err);
console.log(err.message);Câu 3
function divide(a, b) {
if (b === 0) {
throw new RangeError("Division by zero");
}
return a / b;
}
divide(10, 0);Câu 4
console.log(score);Câu 5
const data = {};
data.settings.theme = "dark";Câu 6 (Transfer — Parse-time vs Runtime)
Đoạn code sau thuộc file app.js:
function greet() {
return "hello"
}
const user = {
name: "Alice"
age: 30
};
greet();Câu hỏi:
- Đoạn code trên có chạy được không? Nếu không, lỗi gì xuất hiện ở phase nào (parse-time hay runtime)?
try { ... } catch (e) { ... }bao quanh toàn bộ file có bắt được lỗi này không? Tại sao?
[Đáp án & Giải thích]
Câu 1: TypeError: Cannot read properties of null (reading 'name')
- Giải thích:
userlànull. Truy cập.nametrênnulllà thao tác không hợp lệ.nullkhông có property. Đây làTypeError, không phảiReferenceError, vìuserđã được khai báo (bằngconst).
Câu 2: "object" "Connection failed"
- Giải thích:
new Error(...)tạo một object.typeofobject là"object".err.messagelà string truyền vào constructor. Không có lỗi xảy ra.
Câu 3: RangeError: Division by zero
- Giải thích:
throwném Error object ra ngoài. Vì đây làRangeErrorcustom message, console sẽ inRangeError: Division by zerocùng stack trace. Lưu ý:10 / 0trong JavaScript thực ra trả vềInfinity, không throw. Nhưng ở đây ta throw explicit.
Câu 4: ReferenceError: score is not defined
- Giải thích:
scorechưa được khai báo. JavaScript không tìm thấy binding nào tênscoretrong scope. Đây làReferenceError, khác với Câu 1 nơi biến đã tồn tại nhưng giá trị lànull.
Câu 5: TypeError: Cannot set properties of undefined (setting 'theme')
- Giải thích:
data.settingslàundefined(vìdatakhông có propertysettings). Gán.themetrênundefinedgâyTypeError. Đây là lỗi phổ biến khi access nested property mà level trung gian missing.
Câu 6:
Giải thích:
- Không chạy được.
SyntaxError— thiếu dấu phẩy sau"Alice". Lỗi xảy ra ở parse-time (trước khi dòng đầu tiên thực thi). - Không.
try/catchchỉ bắt lỗi runtime. Vì engine parse toàn bộ file trước khi chạy, SyntaxError ở parse-time làm crash script trước khi execution context được tạo.
Key Insight: Đây là lý do tại sao SyntaxError thường "không thể xử lý graceful" trong production — nó ngăn cả file chạy, không giống runtime error có thể catch và recover.
- Không chạy được.
Transfer Check
Bạn chưa từng gặp đoạn code sau. Dựa vào mental model, hãy trả lời:
const response = JSON.parse('{"status": 200}');
const timestamp = response.data.createdAt;- Nếu đoạn code trên lỗi, dự đoán error type và message.
responsecó phải làundefinedkhông? Tại sao điều này quan trọng để phân biệtTypeErrorvàReferenceError?
[Đáp án & Giải thích]
TypeError: Cannot read properties of undefined (reading 'createdAt').JSON.parsethành công nênresponselà object, nhưngresponse.datalàundefined(vì JSON không có fielddata). Access.createdAttrênundefinedgâyTypeError.responsekhông phảiundefined. Nếuresponselàundefined, lỗi sẽ làReferenceError: response is not defined(nếu chưa khai báo) hoặcTypeErrorkhác. Việc phân biệt "biến chưa khai báo" (ReferenceError) với "biến đã tồn tại nhưng property làundefined" (TypeError) là kỹ năng đọc lỗi cốt lõi.
Bài học: Đừng nhìn TypeError và nghĩ "sai kiểu dữ liệu" một cách mơ hồ. Hãy đọc tiếp message: "Cannot read properties of undefined" nghĩa là giá trị trước dấu chấm là undefined, không phải biến không tồn tại.
8. Implementation Lab (Bài lab thực hành)
Level 1 — Guided (Có hướng dẫn)
Tạo một Error object và inspect các property của nó.
const error = new Error("File not found");
console.log(error.name); // ?
console.log(error.message); // ?
console.log(typeof error.stack); // ?Gợi ý
new Error("...") tạo object với name mặc định là "Error", message là string truyền vào, stack là string chứa trace.
[Đáp án tham khảo]
const error = new Error("File not found");
console.log(error.name); // "Error"
console.log(error.message); // "File not found"
console.log(typeof error.stack); // "string"Giải thích:
namemặc định củaErrorconstructor là"Error".messagelà argument truyền vào constructor.stacklà string được engine tự động gắn vào khi Error được tạo. Format phụ thuộc engine (V8, SpiderMonkey...).
Level 2 — Partial Scaffold (Khung sẵn)
Hoàn thành function validateAge để throw đúng built-in error type.
function validateAge(age) {
if (typeof age !== "number") {
throw new _______("Age must be a number");
}
if (age < 0 || age > 150) {
throw new _______("Age out of valid range");
}
return true;
}
// Test:
validateAge("twenty"); // TypeError?
validateAge(200); // RangeError?[Đáp án tham khảo]
function validateAge(age) {
if (typeof age !== "number") {
throw new TypeError("Age must be a number");
}
if (age < 0 || age > 150) {
throw new RangeError("Age out of valid range");
}
return true;
}Giải thích:
TypeError: Kiểu dữ liệu sai.typeof age !== "number"nghĩa là người dùng truyền string, null, object...RangeError: Giá trị nằm ngoài phạm vi hợp lý.200vẫn là number, nhưng không hợp lệ về mặt domain.
Level 3 — Independent (Tự viết)
Viết các function sau:
// 1. parseConfig(jsonString) → Dùng JSON.parse. Nếu jsonString invalid,
// ném SyntaxError với message "Invalid JSON". Nếu parse ra không phải object,
// ném TypeError với message "Config must be an object".
// 2. getLength(str) → Nếu str không phải string, ném TypeError.
// Nếu str rỗng, ném RangeError với message "String cannot be empty".
// Trả về str.length.
// Sử dụng:
console.log(parseConfig('{"port": 3000}')); // { port: 3000 }
// console.log(parseConfig("not json")); // SyntaxError: Invalid JSON
// console.log(parseConfig("123")); // TypeError: Config must be an object
console.log(getLength("hello")); // 5
// console.log(getLength(123)); // TypeError
// console.log(getLength("")); // RangeError[Đáp án tham khảo]
function parseConfig(jsonString) {
let parsed;
try {
parsed = JSON.parse(jsonString);
} catch (e) {
// Giữ nguyên lỗi gốc `e.message` để không mất `stack trace` từ JSON.parse
throw new SyntaxError("Invalid JSON: " + e.message);
}
if (typeof parsed !== "object" || parsed === null) {
throw new TypeError("Config must be an object");
}
return parsed;
}Hoặc nếu muốn đơn giản hơn:
function parseConfig(jsonString) {
try {
const parsed = JSON.parse(jsonString);
if (typeof parsed !== "object" || parsed === null) {
throw new TypeError("Config must be an object");
}
return parsed;
} catch (e) {
// Nếu JSON.parse đã throw SyntaxError, giữ nguyên hoặc wrap rõ ràng
if (e instanceof SyntaxError) {
throw new SyntaxError("Invalid JSON: " + e.message);
}
throw e;
}
}Hàm getLength:
function getLength(str) {
if (typeof str !== "string") {
throw new TypeError("Expected a string");
}
if (str.length === 0) {
throw new RangeError("String cannot be empty");
}
return str.length;
}Giải thích:
JSON.parsethrowSyntaxErrornếu invalid. Ta bắt và throw lại với message rõ ràng hơn.typeof nulllà"object"nên cần checkparsed === null.getLengthdùngTypeErrorcho sai kiểu,RangeErrorcho giá trị không hợp lệ.
Ở production, việc re-throw new SyntaxError("...") thay thế hoàn toàn lỗi gốc sẽ làm mất stack trace từ JSON.parse.
Cách tốt hơn là wrap message hoặc dùng Error.cause (ES2022, Stage 9). Ở Stage 0, chỉ cần nhận thức: "Không nên âm thầm thay thế lỗi gốc bằng một lỗi mới mà không mang theo thông tin cũ."
9. Edge Cases (Các trường hợp ngoại lệ)
SyntaxError parse-time thường không thể catch
try {
eval("const x =");
} catch (e) {
console.log(e.name); // SyntaxError
}Nhưng:
try {
// const x = // Không thể viết cú pháp sai trực tiếp trong file
} catch (e) {}Tại sao: Nếu file có const x = (thiếu value), engine parse file trước khi chạy và throw SyntaxError ngay. try/catch không bao quát được parse phase. Chỉ eval hoặc Function constructor mới cho phép catch SyntaxError runtime.
Cách nhận biết: SyntaxError trong file thường làm crash toàn bộ script trước khi dòng đầu tiên chạy.
Cách xử lý: Viết cú pháp đúng. SyntaxError không phải lỗi runtime để "xử lý graceful".
throw không bắt buộc phải là Error object
throw "Something went wrong";
throw 404;
throw null;Tại sao: JavaScript cho phép throw bất kỳ value nàu. Nhưng nếu throw primitive, bạn mất stack trace và message chuẩn.
Quan sát sự khác biệt:
try {
throw new Error("With stack");
} catch (e) {
console.log(typeof e); // "object"
console.log(e.stack); // có stack trace
console.log(e.message); // "With stack"
}
try {
throw "Without stack";
} catch (e) {
console.log(typeof e); // "string"
console.log(e.stack); // undefined ❌
console.log(e.message); // undefined ❌
}Cách nhận biết: catch (e) nhận được string/number thay vì object có stack. Debugging tool (DevTools, Sentry) không thể hiển thị trace cho primitive.
Cách xử lý: Luôn throw new Error("...") hoặc built-in subtype. Đây là convention để debugging tool hoạt động đúng.
new Error() vs Error()
const a = new Error("x");
const b = Error("x");Tại sao: Cả hai đều tạo Error object trong JS (khác Java). Nhưng convention là dùng new cho clarity.
Cách nhận biết: Không khác biệt behavior.
Cách xử lý: Luôn dùng new để rõ ràng với người đọc.
10. Debug Lab (Bài lab gỡ lỗi)
Symptom (Triệu chứng): Developer nhận được lỗi và mất 20 phút sửa sai chỗ.
function fetchUser(id) {
const users = { 1: { name: "Alice" } };
return users[id];
}
function greetUser(id) {
const user = fetchUser(id);
return `Hello, ${user.name}!`;
}
console.log(greetUser(2));
// TypeError: Cannot read properties of undefined (reading 'name')Reproduction (Tái hiện lỗi): Chạy code với greetUser(2).
Evidence (Bằng chứng):
- Message:
Cannot read properties of undefined (reading 'name') - Stack trace (V8):text
at greetUser (app.js:7:25) at Object.<anonymous> (app.js:10:13) fetchUser(2)trả vềundefinedvìuserskhông có key2.
Hypothesis (Giả thuyết): Developer nhìn TypeError và nghĩ "có gì đó sai kiểu dữ liệu". Họ đổi user.name thành user["name"], thêm String(user.name), rồi kiểm tra typeof user. Nhưng vấn đề không phải kiểu dữ liệu — vấn đề là user là undefined vì ID không tồn tại.
Verification (Xác minh):
console.log(fetchUser(2)); // undefined
console.log(typeof fetchUser(2)); // "undefined"Root Cause (Nguyên nhân gốc rễ): fetchUser trả về undefined khi ID không tồn tại. greetUser giả định user luôn là object và truy cập .name ngay lập tức. TypeError ở đây là triệu chứng của missing data, không phải sai kiểu dữ liệu nội tại.
Fix (Sửa):
function greetUser(id) {
const user = fetchUser(id);
if (!user) {
throw new Error(`User ${id} not found`);
}
return `Hello, ${user.name}!`;
}Hoặc defensive hơn:
function greetUser(id) {
const user = fetchUser(id);
if (typeof user !== "object" || user === null) {
throw new TypeError("Expected user object");
}
return `Hello, ${user.name}!`;
}Prevention (Phòng ngừa):
- Đọc message cẩn thận:
"Cannot read properties of undefined"nghĩa là giá trị làundefined, không phải "sai type" theo nghĩa class/kind. - Luôn hỏi: "Value này có thể là
undefined/nullkhông trước khi access property?" - Dùng guard clause hoặc early return (0.4.4).
11. Design Exercise (Bài tập thiết kế giải pháp)
Bạn đang viết một hàm calculateDiscount(price, rate).
function calculateDiscount(price, rate) {
if (price < 0 || rate < 0 || rate > 1) {
throw new Error("Invalid input");
}
return price * rate;
}function calculateDiscount(price, rate) {
if (typeof price !== "number" || typeof rate !== "number") {
throw new TypeError("Price and rate must be numbers");
}
if (price < 0) {
throw new RangeError("Price cannot be negative");
}
if (rate < 0 || rate > 1) {
throw new RangeError("Rate must be between 0 and 1");
}
return price * rate;
}Câu hỏi:
- Option A có lợi gì về đơn giản?
- Option B có lợi gì khi debug hoặc log lỗi production?
- Nếu bạn cần catch lỗi và xử lý khác nhau cho "sai kiểu" và "sai phạm vi", option nào cho phép điều đó?
[Đáp án tham khảo]
Bạn nghĩ:
- Option A: Ngắn gọn, ít code, dễ viết nhanh.
- Option B:
error.namecho biết ngay loại vấn đề. Khi log production, bạn có thể filterTypeErrorkhácRangeError. Message cụ thể giúp debug nhanh hơn "Invalid input". - Option B cho phép catch phân biệt:js
try { calculateDiscount("100", 0.2); } catch (e) { if (e instanceof TypeError) { /* handle type issue */ } if (e instanceof RangeError) { /* handle range issue */ } }
Kết luận: Dùng built-in error type đúng semantic. Đừng dùng generic
Errorkhi built-in type đã mô tả chính xác vấn đề. Điều này giúp stack trace "self-documenting".
12. Production Scenario (Tình huống thực tế)
Context: Production log từ monitoring tool:
TypeError: Cannot read properties of undefined (reading 'map')
at renderList (components/List.js:14:15)
at renderDashboard (pages/Dashboard.js:42:10)
at mountComponent (react-dom.js:...)Symptom: Trang Dashboard trắng với lỗi này cho một số user.
Constraint: Bạn không thể debug trên máy user. Chỉ có log.
Câu hỏi:
- Dựa vào message, giá trị nào là
undefined? - Dựa vào stack trace, dòng code nào gây lỗi trực tiếp?
- Tại sao lỗi chỉ xảy ra với "một số user" mà không phải tất cả?
[Đáp án tham khảo]
Bạn nghĩ:
- Một giá trị nào đó trước
.maplàundefined. Có thể làitemshoặcdata.list. renderListtạiList.js:14:15. Đây là nơi.mapđược gọi.- Chỉ một số user có data thiếu field (ví dụ: API trả về
nullchoitemsthay vì[]). Với user có data đầy đủ,itemslà array và.maphoạt động.
- Một giá trị nào đó trước
Bài học: Stack trace trong production là dữ liệu quý giá. Message + top frame thường cho bạn 80% thông tin cần thiết. Đừng chỉ nhìn "TypeError" và bối rối — đọc tiếp
Cannot read properties of undefined.
13. AI-assisted Exercise (Bài tập với AI)
Level B — Challenge
- Tự trả lời trước: Dự đoán output:js
const e = new TypeError("fail"); console.log(e.name); console.log(e instanceof Error); - Hỏi AI: "
TypeErrorvàErrorkhác nhau thế nào?TypeErrorcó phải làErrorkhông?" - So sánh câu trả lời AI với nhận định của bạn. AI có nhấn mạnh rằng built-in error types là subtype của
Errorvà kế thừaname/message/stack? - Verify bằng MDN: tìm "Error" trên MDN, đọc phần "Error types".
Gợi ý
AI thường biết TypeError extends Error nhưng đôi khi giải thích mơ hồ bằng cách nói "TypeError là một loại lỗi". Nếu AI không chỉ ra rằng e instanceof Error là true và tại sao điều này quan trọng cho catch (e) generic, bạn đã tìm ra điểm mù. AI cũng có thể nhầm lẫn TypeError với static type checking (TypeScript).
[Đáp án tham khảo]
Bạn nghĩ:
e.namelà"TypeError".e instanceof ErrorlàtruevìTypeErrorkế thừa từError. Điều này nghĩa là bạn có thểcatch (e)và truy cậpe.messagedù không biết chính xác subtype.AI trả lời (typical):
TypeErroris a built-in error type in JavaScript.- It is thrown when an operation is performed on a value of an inappropriate type.
TypeErroris a subclass ofError.- Therefore,
e instanceof Errorreturnstrue.
So sánh: AI thường đúng về inheritance nhưng đôi khi không giải thích tại sao điều này quan trọng trong production: bạn có thể viết
catch (e) { log(e.message); report(e.stack); }và nó hoạt động cho mọi built-in error type.Điểm AI nói sai hoặc quá mơ hồ: "TypeError is for wrong types" — đúng nhưng chưa đủ. AI hiếm khi nói rõ: "
TypeErrorcũng xảy ra khi access property trênnull/undefined, không chỉ khi bạn truyền string thay vì number."Kết luận: Nếu bạn chỉ ra được rằng built-in error types tạo thành một hierarchy và điều này cho phép xử lý lỗi generic, bạn đã hiểu sâu hơn AI. Concept này sẽ quay lại ở Stage 2 (Prototype chain / Class inheritance), Stage 3 (Promise rejection type), và Stage 8 (Error Boundary catch behavior).
14. Teach Back (Dạy lại)
Giả sử một junior developer hỏi bạn:
"Em thấy console báo đỏ lòm
ReferenceError: user is not defined. Em sợ quá nên refresh trang rồi lại. Em nên làm gì với cái lỗi này?"
Hãy giải thích trong 2 phút, dùng đúng terminology: error object, message, stack trace, error type.
Mô phỏng
- Bạn nói: Đừng refresh. Đừng sợ. Cái đỏ đỏ đó là thông tin, không phải lỗi hệ thống.
Đầu tiên, đọc error type: ReferenceError. Điều này nghĩa là JavaScript không tìm thấy biến user ở bất kỳ scope nào. Khác với TypeError — nếu là TypeError thì biến có tồn tại nhưng bạn đang dùng sai kiểu.
Thứ hai, đọc message: user is not defined. Nghĩa là bạn dùng user nhưng chưa khai báo const user, let user, hoặc var user. Hoặc bạn khai báo ở scope khác.
Thứ ba, nếu có stack trace, đọc dòng đầu tiên sau message. Nó sẽ chỉ file và dòng code gây lỗi. Ví dụ app.js:24:10 nghĩa là dòng 24, cột 10.
Cách fix: Tìm đến dòng đó, kiểm tra xem user có được khai báo không. Nếu có thể là typo — bạn định viết users nhưng gõ thiếu s.
💡 Tưởng tượng error như một bưu phẩm bị trả lại. Trên bìa ghi rõ: "Người nhận
userkhông tồn tại, gửi từ dòng 24". Bạn không cần Google — bạn cần kiểm tra địa chỉ người nhận.
Gợi ý đánh giá bản thân
- Đồng nghiệp có hiểu tại sao không nên refresh và bỏ qua lỗi không?
- Bạn có tránh được việc nói "lỗi đỏ nghĩa là code hỏng" không?
- Nếu đồng nghiệp hỏi: "Vậy
ReferenceErrorkhácTypeErrornhư thế nào?" — bạn trả lời được không? (Gợi ý: ReferenceError = biến không tồn tại. TypeError = thao tác sai trên giá trị đã tồn tại.) - Nếu đồng nghiệp hỏi: "Tại sao đôi khi lỗi không có stack trace?" — bạn trả lời được không? (Gợi ý:
throw "string"không có stack. Hoặc SyntaxError parse-time.)
15. Assessment (Đánh giá)
| Objective (Mục tiêu) | Hình thức đánh giá | Task |
|---|---|---|
| Error là object | Prediction (Dự đoán) | Prediction Câu 2 |
| Phân biệt error types | Prediction (Dự đoán) | Prediction Câu 1, 4, 5 |
| Đọc stack trace | Debug Lab | Debug Lab Section |
| Tạo Error object | Implementation (Thực hành) | Implementation Lab Level 1 |
| Throw đúng built-in type | Implementation (Thực hành) | Implementation Lab Level 2–3 |
| Chọn error type đúng semantic | Design Exercise | Design Exercise Section |
| Explain error hierarchy | Teach Back | Teach Back Section |
16. Exit Criteria (Tiêu chí qua bài)
- [ ] Có thể giải thích error là object với
name,message,stack. - [ ] Có thể phân biệt
TypeError,ReferenceError,RangeError,SyntaxError, vàErrorgeneric. - [ ] Có thể dự đoán error type từ một đoạn code lỗi (≥ 4/5 scenarios đúng).
- [ ] Có thể đọc stack trace cơ bản để xác định function và dòng gây lỗi.
- [ ] Có thể tạo
Errorobject với message mô tả rõ ràng. - [ ] Có thể chọn built-in error type phù hợp cho từng tình huống (type mismatch vs range vs reference).
- [ ] Có thể debug lỗi
TypeErrordoundefined/nullproperty access và phân biệt vớiReferenceError.
17. Spiral Connection (Liên kết xoắn ốc)
Previous (Trước): Data Structures (0.6.x) — bạn đã biết object là reference value có property. Error object cũng là object — nó chứa
message,stack,namenhư một object bình thường.
Current (Hiện tại): Errors — Error object, built-in types, message, stack trace. Error là dữ liệu debug, không phải kẻ thù.
Next (Tiếp theo):
- 0.7.2 (Throw) — Tại sao và khi nào nên chủ động ném error?
throwlà cơ chế propagation.- 0.7.3 (Try/Catch) — Bắt và xử lý error. Stack trace đi kèm với error khi được catch.
- Stage 1 (Execution Model) — Call stack. Stack trace chính là snapshot của call stack tại thời điểm lỗi xảy ra. Hiểu execution context giúp đọc stack trace chính xác hơn.
- Stage 3 (Async) — Promise rejection là một dạng async error.
UnhandledPromiseRejection.- Stage 8 (React) — Error Boundaries. Catch error trong component tree và hiển thị fallback UI.
- Stage 9/13 (Production) — Error logging, monitoring, incident response. Stack trace là evidence đầu tiên trong production debugging.