Lesson 0.4.2 — Conditional Logic
0. Metadata
| Field | Value |
|---|---|
| Stage | 0 — JavaScript Language Foundation |
| Module | 0.4 — Operators & Control Flow |
| Lesson | 0.4.2 |
| Competency | C01.4 — Control Flow |
| Depth | L2–L3 (Explain → Use) |
| Prerequisites | Lesson 0.4.1 — Operators; Lesson 0.3.1 — Truthy & Falsy |
| Cognitive Load | Medium |
1. Why This Exists (Vì sao cần học)
Bạn viết:
function getStatus(user) {
if (user) {
if (user.active) {
if (user.profile) {
if (user.profile.age >= 18) {
return "adult";
} else {
return "minor";
}
} else {
return "no profile";
}
} else {
return "inactive";
}
} else {
return "no user";
}
}Code chạy đúng. Nhưng khi bạn quay lại sau 2 tuần, bạn mất 30 giây để đếm dấu ngoặc nhọn và xác định else nào thuộc về if nào. Đồng nghiệp review code cũng phải cuộn màn hình để theo dõi.
Conditional logic là nơi code dễ "chìm" nhất. Không phải vì if khó. Mà vì nested conditions tạo ra cognitive load theo cấp số nhân. Mỗi lớp if lồng nhau là một nhánh bạn phải giữ trong đầu.
Lesson này dạy bạn không chỉ viết if đúng — mà viết if sao cho người đọc không phải giữ nhiều trạng thái trong đầu.
2. Prerequisites (Yêu cầu đầu vào)
Trước khi học bài này, bạn cần:
- Biết 8 giá trị falsy (Lesson 0.3.1).
- Hiểu operators
&&,||,!(Lesson 0.4.1). - Phân biệt expression và statement (awareness từ các lesson trước).
3. Learning Objectives (Mục tiêu học tập)
Sau bài này, bạn có thể:
- Viết
if/else if/elseđúng thứ tự ưu tiên. - Phân biệt statement (
if) và expression (ternary? :). - Dùng
switchđúng, bao gồmbreakvàdefault. - Nhận diện "pyramid of doom" và refactor thành guard clauses.
- Chọn đúng giữa
if,switch, và ternary trong ít nhất 3 scenario.
4. Mental Model (Mô hình tư duy)
Mental Model — Conditional là Đường Rẽ, không phải Mê Cung
Hãy hình dung conditional logic như một ngã ba/ngã tư:
if (condition) {
// Đường A
} else {
// Đường B
}Mỗi lần lồng if bên trong if, bạn không tạo thêm đường rẽ — bạn tạo hầm mà người đọc phải đi xuống.
if (a) { ← Tầng 1
if (b) { ← Tầng 2
if (c) { ← Tầng 3
// Đường sâu
}
}
}Quy tắc then chốt: Mỗi tầng lồng nhau là một unit of cognitive load. Giữ tối đa 1-2 tầng.
Code Review Lens
Khi thấy if lồng quá 2 tầng, hãy tự hỏi: "Tôi có thể đảo ngược điều kiện và return sớm để giảm nesting không?"
5. Core Concepts (Các khái niệm cốt lõi)
Essential (Bắt buộc)
| Khái niệm | Ý nghĩa |
|---|---|
if statement | Chạy block nếu condition truthy. |
else if chain | Kiểm tra nhiều điều kiện loại trừ lẫn nhau theo thứ tự. |
else | Block fallback khi tất cả điều kiện trên đều falsy. |
Ternary ? : | Expression (trả về value), không phải statement. Dùng cho assignment đơn giản. |
switch | So sánh value với nhiều case bằng strict equality (===). |
break | Thoát khỏi switch. Thiếu break gây fallthrough. |
| Guard clause | if (bad) return; ở đầu hàm để giảm nesting. |
Supporting (Hỗ trợ)
switchcó thể dùng vớidefaultcase.- Ternary có thể nest, nhưng không nên quá 1 cấp.
Awareness (Biết tồn tại)
switchso sánh bằng===, không phải==.switch (true)pattern để viết guard-like switch. Không bắt buộc ở Stage 0.
Out of Scope (Không học trong bài này)
- Pattern matching: Không có trong JavaScript core (có proposal). Không cần.
- Labelled statements (
label: while...break label): Advanced control flow, không cần ở Stage 0.
6. Worked Example (Ví dụ phân tích từng bước)
Xét hai cách viết cùng một logic:
Cách 1 — Nested (Pyramid of Doom)
function getAccessLevel(user) {
if (user) {
if (user.role) {
if (user.role === "admin") {
return "full";
} else {
if (user.role === "editor") {
return "write";
} else {
return "read";
}
}
} else {
return "none";
}
} else {
return "none";
}
}Step 1 — Đếm tầng nesting Tầng 1: if (user) Tầng 2: if (user.role) Tầng 3: if (user.role === "admin") Tầng 4: if (user.role === "editor") (bên trong else)
Step 2 — Nhận diện vấn đề Để đến return "write", reader phải giữ 4 trạng thái trong đầu: user tồn tại, user.role tồn tại, không phải admin, và là editor.
Cách 2 — Guard Clauses (Flat)
function getAccessLevel(user) {
if (!user) return "none";
if (!user.role) return "none";
if (user.role === "admin") return "full";
if (user.role === "editor") return "write";
return "read";
}Step 3 — Đếm tầng nesting Tất cả if ở cùng một tầng (top-level). Không có nesting.
Step 4 — Đọc từng dòng
- Dòng 1: Không có user → xong.
- Dòng 2: Không có role → xong.
- Dòng 3: Admin → full.
- Dòng 4: Editor → write.
- Dòng 5: Còn lại → read.
Mỗi dòng là một quyết định độc lập. Reader không cần nhớ trạng thái từ dòng trước.
7. Prediction Exercise (Bài tập dự đoán)
Đừng chạy code. Dự đoán output và giải thích luồng conditional.
P1
const score = 75;
let grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else if (score >= 70) {
grade = "C";
} else {
grade = "F";
}
console.log(grade);P2
const role = "guest";
const access = role === "admin" ? "full"
: role === "editor" ? "write"
: "read";
console.log(access);P3
const type = "b";
switch (type) {
case "a":
console.log("A");
case "b":
console.log("B");
case "c":
console.log("C");
default:
console.log("D");
}P4
function check(value) {
if (value) {
if (value > 0) {
return "positive";
}
return "zero";
}
return "falsy";
}
console.log(check(0));
console.log(check(-1));[Đáp án & Giải thích]
P1
score = 75.>= 90? No.>= 80? No.>= 70? Yes.grade = "C".
P2
role = "guest". Không phải"admin". Không phải"editor".- Fallback:
"read".
P3
type = "b". Matchcase "b". In"B".- Không có
break→ fallthrough sangcase "c". In"C". - Fallthrough tiếp sang
default. In"D". - Output:
B,C,D.
P4
check(0):0là falsy → vàoif (value)? No. Return"falsy".check(-1):-1truthy → vàoif.-1 > 0? No. Return"zero".
8. Implementation Lab (Bài lab thực hành)
Lab A — Guided (Có hướng dẫn)
Refactor đoạn code sau từ nested if thành guard clauses:
function processOrder(order) {
if (order) {
if (order.items) {
if (order.items.length > 0) {
return "processing";
} else {
return "empty";
}
} else {
return "invalid";
}
} else {
return "invalid";
}
}[Đáp án tham khảo]
function processOrder(order) {
if (!order) return "invalid";
if (!order.items) return "invalid";
if (order.items.length === 0) return "empty";
return "processing";
}Lab B — Partial Scaffold (Khung sườn)
Hoàn thành hàm getHttpStatusMessage dùng switch:
function getHttpStatusMessage(code) {
// TODO: dùng switch với các case: 200, 404, 500
// TODO: mỗi case trả về message tương ứng
// TODO: default trả về "Unknown"
// implement here
}[Đáp án tham khảo]
function getHttpStatusMessage(code) {
switch (code) {
case 200:
return "OK";
case 404:
return "Not Found";
case 500:
return "Server Error";
default:
return "Unknown";
}
}Lab C — Independent (Tự làm)
Viết hàm classifyNumber nhận một số và trả về:
"positive even"nếu dương và chẵn"positive odd"nếu dương và lẻ"negative"nếu âm"zero"nếu là 0
Yêu cầu: không dùng nested if, chỉ dùng guard clauses và tối đa 1 cấp ternary.
console.log(classifyNumber(4)); // "positive even"
console.log(classifyNumber(3)); // "positive odd"
console.log(classifyNumber(-2)); // "negative"
console.log(classifyNumber(0)); // "zero"[Đáp án tham khảo]
function classifyNumber(n) {
if (n === 0) return "zero";
if (n < 0) return "negative";
return n % 2 === 0 ? "positive even" : "positive odd";
}9. Edge Cases (Các trường hợp ngoại lệ)
Switch fallthrough
switch (x) {
case "a":
doA();
case "b":
doB();
}Nếu x = "a", cả doA() và doB() đều chạy. Đây là feature (fallthrough) nhưng thường là bug nếu quên break. Chỉ dùng fallthrough khi cố ý và comment rõ.
Ternary nesting quá sâu
const result = a ? b ? c ? d : e : f : g;Khó đọc, khó debug. Quy tắc: ternary nest không quá 1 cấp. Nếu phức tạp hơn, dùng if hoặc hàm riêng.
else if thứ tự quan trọng
if (score >= 60) {
grade = "D";
} else if (score >= 80) {
grade = "B";
}score = 90 sẽ vào nhánh >= 60 trước, không bao giờ đến >= 80. Điều kiện cụ thể hơn phải đặt trước.
if (value) với 0 và ""
if (count) { } // skip khi count = 0
if (name) { } // skip khi name = ""Đã học ở Lesson 0.3.1, nhưng cần nhắc lại vì conditional logic là nơi bug này xuất hiện nhiều nhất.
10. Debug Lab (Bài lab gỡ lỗi)
Symptom (Triệu chứng): Hàm getDayType trả về "weekday" cho "Sunday" thay vì "weekend".
Reproduction (Tái hiện lỗi):
function getDayType(day) {
switch (day) {
case "Saturday":
return "weekend";
case "Sunday":
console.log("weekend");
case "Monday":
return "weekday";
default:
return "unknown";
}
}
console.log(getDayType("Sunday")); // "weekday" ← BUG: đáng lẽ là "weekend"Evidence (Bằng chứng):
case "Sunday"match đúng.- Nhưng output là
"weekday"thay vì"weekend".
Hypothesis (Giả thuyết): Developer quên break hoặc return ở case "Sunday", gây fallthrough xuống case "Monday".
Verification (Xác minh):
// Trace execution
function getDayType(day) {
switch (day) {
case "Saturday":
console.log("hit Saturday");
return "weekend";
case "Sunday":
console.log("hit Sunday"); // ← log này chạy
console.log("weekend");
// ← KHÔNG có break/return ở đây!
case "Monday":
console.log("hit Monday"); // ← fallthrough đến đây
return "weekday";
default:
return "unknown";
}
}
getDayType("Sunday");
// Output: "hit Sunday" → "weekend" → "hit Monday" → "weekday"Root Cause (Nguyên nhân gốc):switch trong JavaScript dùng strict equality (===) để match case, sau đó execution rơi xuống (fallthrough) cho đến khi gặp break hoặc return. case "Sunday" chỉ có console.log, không có break/return, nên code tiếp tục chạy vào case "Monday".
Fix (Sửa):
function getDayType(day) {
switch (day) {
case "Saturday":
case "Sunday":
return "weekend";
case "Monday":
case "Tuesday":
case "Wednesday":
case "Thursday":
case "Friday":
return "weekday";
default:
return "unknown";
}
}Prevention (Phòng ngừa):
- Mỗi
casephải cóbreak,return, hoặc comment// fallthroughrõ ràng nếu cố ý. - Nếu nhiều case cùng kết quả (như
"Saturday"và"Sunday"đều là weekend), nhóm chúng lại và dùng mộtreturn/breakduy nhất. - Trong code review, kiểm tra đặc biệt các
casecó body ngắn (1 dòng) — dễ quênbreaknhất.
11. Design Exercise (Bài tập thiết kế giải pháp)
Context
Bạn cần viết hàm getPrice dựa trên type và quantity:
type = "standard": $10 mỗi cái.type = "premium": $20 mỗi cái, nhưng nếuquantity >= 10giảm 10%.type = "enterprise": liên hệ (trả vềnull).
Câu hỏi: Nên dùng if/else if, switch, hay ternary? Viết hàm và bảo vệ lựa chọn.
[Đáp án tham khảo]
function getPrice(type, quantity) {
if (type === "enterprise") return null;
if (type === "standard") return 10 * quantity;
if (type === "premium") {
const base = 20 * quantity;
return quantity >= 10 ? base * 0.9 : base;
}
return undefined;
}Lý do chọn if thay vì switch:
switchkhông hỗ trợ range check (quantity >= 10) dễ dàng.ifvới guard clauses dễ đọc hơnswitchkhi có logic phức tạp bên trong mỗi nhánh.- Ternary không phù hợp vì có nhiều nhánh và logic tính toán.
12. Production Scenario (Tình huống thực tế)
Một hệ thống xác thực:
function checkAccess(user, resource) {
if (user.role === "admin") {
return true;
} else if (user.role === "editor") {
if (resource.type === "article") {
return true;
} else {
return false;
}
} else if (user.role === "viewer") {
if (resource.type === "article" && resource.status === "published") {
return true;
} else {
return false;
}
} else {
return false;
}
}Câu hỏi:
- Hàm này có bao nhiêu tầng nesting tối đa?
- Refactor thành guard clauses để giảm nesting xuống 1 tầng.
[Đáp án tham khảo]
Tối đa 3 tầng:
checkAccess→if (role)→if (resource.type).Refactor:
function checkAccess(user, resource) {
if (user.role === "admin") return true;
if (user.role === "editor" && resource.type === "article") return true;
if (user.role === "viewer" && resource.type === "article" && resource.status === "published") return true;
return false;
}13. AI-assisted Exercise (Bài tập với AI)
Level B — Challenge (Thách thức AI)
- Tự trả lời trước: Viết ra giấy: tại sao guard clause tốt hơn nested if? Dùng từ "cognitive load" và "nesting depth".
- Hỏi AI: "When should I use guard clauses instead of nested if statements?"
- So sánh: AI có nhắc đến cognitive load và mất track của else branch không? Hay AI chỉ nói "guard clauses make code cleaner"?
- Verify: Tìm bài viết về "guard clause" trên MDN hoặc blog kỹ thuật uy tín. Xem lý do chính thức là gì.
Gợi ý
Hãy để ý xem AI có nói guard clause chỉ là "style preference" không. Nếu AI nói vậy, đó là điểm mù: guard clause không chỉ là style — nó là cognitive load reduction và bug prevention (tránh nhầm lẫn else branch).
Đáp án tham khảo
Bạn nghĩ
- Guard clause giảm nesting depth, giảm cognitive load.
- Mỗi guard clause là một điều kiện độc lập, reader không cần nhớ context từ dòng trước.
- Giảm bug từ việc gán nhầm else cho if (đặc biệt khi có nhiều dòng code giữa if và else).
AI trả lời
- AI thường nói: "Guard clauses reduce nesting and make code more readable by handling edge cases early."
- AI có thể thêm: "They help avoid the 'pyramid of doom'."
- Tuy nhiên, AI hiếm khi dùng từ cognitive load hoặc giải thích tại sao nesting gây bug (working memory limit của human brain).
So sánh
- AI đúng về surface benefit (readability, less nesting).
- AI thiếu: neurological reason — human working memory chỉ giữ được ~4 items. Mỗi tầng if lồng là một item. Guard clause giảm items xuống 1.
Điểm AI nói sai hoặc quá mơ hồ
- AI có thể nói guard clause là "a matter of preference" — mơ hồ. Nó không phải preference; nó là evidence-based practice để giảm bug.
- AI có thể gợi ý dùng guard clause cho mọi trường hợp, không nhắc đến khi nào
if/elsebình thường phù hợp hơn (ví dụ: hai nhánh đối xứng, cùng complexity).
Kết luận
- Nếu bạn chỉ ra được rằng guard clause giảm working memory load và ngăn else-branch misalignment, bạn đã hiểu sâu hơn AI.
- Concept này sẽ quay lại ở Lesson 0.4.4 (Guard Clauses & Early Return) và Stage 12 (Architecture: function design và readability).
14. Teach Back (Dạy lại)
Giải thích cho một đồng nghiệp junior trong 2 phút, không dùng từ "nó thế":
"Tại sao đoạn code này khó đọc? Và tại sao viết lại bằng guard clauses lại dễ đọc hơn?"
// Code gốc
function canEdit(doc, user) {
if (doc) {
if (user) {
if (doc.owner === user.id) {
return true;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}Yêu cầu:
- Dùng đúng từ: nesting, cognitive load, guard clause, early return.
- Viết lại bằng guard clauses trong lúc giải thích.
Mô phỏng
- Bạn nói
- "Đoạn code này có 3 tầng lồng nhau. Để hiểu
return true, tôi phải giữ trong đầu: doc tồn tại, user tồn tại, và owner khớp. Đó là 3 item trong working memory." - "Nếu tôi viết lại bằng guard clauses:
if (!doc) return false; if (!user) return false; if (doc.owner !== user.id) return false; return true;" - "Bây giờ mỗi dòng là một quyết định độc lập. Tôi không cần nhớ trạng thái từ dòng trước. Nếu đọc dòng 3, tôi biết chắc doc và user đều tồn tại vì nếu không, hàm đã return rồi."
- "Guard clause không chỉ là 'sạch hơn' — nó giảm số thứ tôi phải giữ trong đầu từ 3 xuống 1."
- "Đoạn code này có 3 tầng lồng nhau. Để hiểu
💡 Liên tưởng: Nested if giống như một ngôi nhà nhiều tầng với nhiều phòng. Để biết bạn đang ở đâu, bạn phải nhớ bạn đã đi qua những cánh cửa nào. Guard clause giống như một bảo vệ ở cổng: "Không có giấy tờ? Về luôn. Có giấy? Bước vào. Không đủ tuổi? Về luôn. Đủ tuổi? Bước vào." Bạn không cần nhớ bạn đã qua bao nhiêu cổng — chỉ cần biết bạn đã vào được.
Gợi ý đánh giá bản thân
- Đồng nghiệp có hỏi "vậy khi nào KHÔNG nên dùng guard clause?" không? Nếu có, bạn đã truyền đạt đủ rõ để họ tư duy critical. (Gợi ý: khi hai nhánh đối xứng và cùng phức tạp,
if/elsecó thể rõ ràng hơn.) - Bạn có thể đếm số tầng nesting của bất kỳ hàm nào không?
15. Assessment (Đánh giá)
| Objective (Mục tiêu) | Hình thức đánh giá |
|---|---|
Viết if/else if/else đúng thứ tự | Implementation (Thực hành) |
| Phân biệt statement và expression | Explanation (Giải thích) |
Dùng switch đúng với break | Prediction + Implementation |
| Refactor nested if thành guard clauses | Implementation (Thực hành) |
Chọn if/switch/ternary đúng scenario | Design Decision (Quyết định thiết kế) |
16. Exit Criteria (Tiêu chí qua bài)
- [ ] Có thể viết
if/else if/elsevới điều kiện cụ thể đặt trước. - [ ] Có thể dùng
switchvớibreakvàdefaultđúng cách. - [ ] Có thể phân biệt khi nào dùng ternary (expression, đơn giản) và khi nào dùng
if(statement, phức tạp). - [ ] Có thể refactor hàm có nesting 3+ tầng thành guard clauses.
- [ ] Có thể debug bug fallthrough trong
switch. - [ ] Không còn viết
iflồng quá 2 tầng trong code mới.
17. Spiral Connection (Liên kết xoắn ốc)
Previous (Trước): Lesson 0.4.1 — Operators (
&&,||,!, truthy/falsy trong boolean context).
Current (Hiện tại): Lesson 0.4.2 — Conditional Logic (
if,else if,switch, ternary, guard clauses).
Next (Tiếp theo):
- Lesson 0.4.3 — Short-circuit Evaluation (
&&,||,??trong patterns thực tế).- Lesson 0.4.4 — Guard Clauses & Early Return (đào sâu engineering style từ conditional logic).
- Stage 1 — Execution Model: Conditionals tạo execution branches, liên quan đến call stack trace.
- Stage 8 — React: Conditional rendering (
{condition && <Component />}— kết hợp logical operator + conditional logic).