Skip to content

Lesson 0.3.4 — Coercion Lab ​

Bài 0.3.4 — Coercion Lab
Dự đoán & Giải thích cơ chế chuyển đổi kiểu • 33 phút
0:00 / 0:00

0. Metadata ​

FieldValue
Stage0 — JavaScript Language Foundation
Module0.3 — Coercion & Equality
Lesson0.3.4
CompetencyC01.3 — Coercion & Equality
DepthL3 (Use) — Integration lab, không có lý thuyết mới
PrerequisitesLesson 0.3.1 — Truthy & Falsy; Lesson 0.3.2 — Equality; Lesson 0.3.3 — Type Conversion
Cognitive LoadHigh

Bài này KHÔNG đi sâu vào:

  • ToPrimitive algorithm internals (valueOf, toString, Symbol.toPrimitive) → Stage 2
  • BigInt coercion → Stage 2
  • Symbol coercion → Stage 2
  • HTMLDDA (document.all) coercion edge case → Awareness only

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

Bạn đã học:

  • 8 giá trị falsy.
  • == cho phép coercion, === không cho phép.
  • + nối chuỗi nếu có string, - luôn chuyển sang số.

Nhưng production không tách biệt từng khái niệm. Một dòng code có thể trigger cả boolean context, numeric context, và equality coercion cùng lúc:

js
if (input == null) { }
if (count + "") { }
if (items.length == 0) { }

Lesson này không dạy thêm lý thuyết. Đây là integration lab — bạn phải áp dụng cả Module 0.3 vào các tình huống thực tế, trace từng bước coercion, và viết code không bị coercion đánh lừa.

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

Trước khi học bài này, bạn cần hoàn thành:

  • Lesson 0.3.1: danh sách 8 falsy, boolean context.
  • Lesson 0.3.2: == vs === vs Object.is, Abstract Equality Algorithm cơ bản.
  • Lesson 0.3.3: explicit conversion (Number, String, Boolean) và implicit context (+ string, - numeric).

Nếu chưa hoàn thành, quay lại 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ể:

  1. Trace từng bước coercion trong ít nhất 6 biểu thức == phổ biến.
  2. Viết hàm xử lý input động mà không bị implicit coercion đánh lừa.
  3. Debug bug production xuất phát từ == hoặc + với mixed types.
  4. Chọn đúng công cụ (==, ===, Object.is, explicit conversion) cho 4 scenario khác nhau.
  5. Giải thích cơ chế coercion bằng lời của riêng mình, không dùng từ "JavaScript nó thế".

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

Mental Model — Coercion: JavaScript cố gắng về số

Khi JavaScript gặp mixed types, mục tiêu cuối cùng của nó là đưa mọi thứ về cùng type để thực hiện phép toán. Và trong hầu hết trường hợp, type đích là number.

Tại sao? Vì JavaScript được thiết kế xung quanh số học (ngôn ngữ ban đầu chạy trong 10 ngày). Khi == so sánh một string với một boolean, spec không so sánh string vs boolean trực tiếp — nó chuyển cả hai về number rồi so sánh.

Rule of thumb:

  1. Boolean luôn được chuyển thành number đầu tiên (true→1, false→0)
  2. Object được chuyển thành primitive (thường là string) qua ToPrimitive
  3. String vs number → string chuyển thành number
  4. null và undefined là ngoại lệ: chỉ bằng nhau, không chuyển thêm

Pipeline nhận diện ngữ cảnh:

Mental model về JavaScript coercion: từ mixed types, JavaScript trước hết nhận diện Boolean, String, Numeric hoặc Equality context, sau đó áp dụng conversion phù hợp như ToBoolean, String concatenation, ToNumber hoặc các nhánh của Abstract Equality Algorithm.

Bạn đã học từng ngăn riêng lẻ. Bây giờ bạn phải nhận diện ngăn nào đang active và type đích là gì trong một dòng code.

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

Tóm tắt Module 0.3

Bài này không có khái niệm mới. Dưới đây là checklist để tra cứu khi làm lab:

Khái niệmNguồn
8 giá trị falsyLesson 0.3.1
== coercion: boolean → number trướcLesson 0.3.2
== coercion: object → primitiveLesson 0.3.2
== coercion: string ↔ number (string thành number)Lesson 0.3.2
null == undefined là true, không coercion thêmLesson 0.3.2
+ với string → string contextLesson 0.3.3
-, *, / → numeric contextLesson 0.3.3
Number("") là 0, Number(null) là 0, Number(undefined) là NaNLesson 0.3.3

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

Xét biểu thức:

js
console.log([] == false);

Step 1 — Xác định operator== → loose equality. Type khác nhau (object vs boolean). Cần coercion.

Step 2 — Abstract Equality Algorithm Rule: Nếu một bên là boolean, chuyển boolean thành number trước.

text
[] == false
     ↓
[] == 0        (false → 0)

Step 3 — Object → Primitive[] là object. Chuyển object thành primitive bằng ToPrimitive. Array rỗng trở thành empty string.

text
[] == 0
  ↓
"" == 0        ([] → "")

Step 4 — String → Number"" là string, 0 là number. Chuyển string thành number.

text
"" == 0
  ↓
0 == 0         ("" → 0)

Step 5 — Same type comparison

text
0 == 0 → true

Kết luận: [] == false là true vì qua 3 bước coercion: boolean→number, object→string, string→number.

Code Review Lens

Khi thấy [] == false trong code, đừng hỏi "tại sao JavaScript ngu vậy". Hãy hỏi: "Tác giả có đang cố ý dùng coercion, hay đây là accident?"

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

Đừng chạy code. Dự đoán kết quả và trace từng bước coercion. Không được trả lời "JavaScript nó thế". Phải chỉ ra: value nào → chuyển thành gì → tại sao.

P1

js
console.log("" == false);

P2

js
console.log(0 == "");

P3

js
console.log(null == undefined);

P4

js
console.log(NaN === NaN);
console.log(Object.is(NaN, NaN));

P5

js
console.log("5" - 3);
console.log("5" + 3);

P6

js
console.log([] + []);
console.log([] + {});
[Đáp án & Giải thích]

P1 — "" == false

  1. false là boolean → chuyển thành number: 0.
  2. "" == 0: "" là string → chuyển thành number: 0.
  3. 0 == 0 → true.

P2 — 0 == ""

  1. "" là string → chuyển thành number: 0.
  2. 0 == 0 → true.

P3 — null == undefined

  1. Theo spec, null và undefined được định nghĩa là tương đương dưới ==. Không có coercion thêm.
  2. Kết quả: true.

P4 — NaN === NaN và Object.is(NaN, NaN)

  1. NaN === NaN: cùng type (number), nhưng IEEE 754 định nghĩa NaN không bằng chính nó. Kết quả: false.
  2. Object.is(NaN, NaN): dùng SameValue algorithm, NaN được coi là bằng nhau. Kết quả: true.

P5 — "5" - 3 và "5" + 3

  1. "5" - 3: - là numeric context. "5" → 5. 5 - 3 = 2.
  2. "5" + 3: + thấy string bên trái. 3 → "3". Nối chuỗi: "53".

P6 — [] + [] và [] + {}

  1. [] + []: + trigger string context (không có number). Cả hai [] chuyển thành "". "" + "" = "".
  2. [] + {}: [] → "". {} → "[object Object]". Nối chuỗi: "[object Object]".

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

Lab A — Guided (Có hướng dẫn) ​

Viết hàm safeCompare nhận hai giá trị và trả về kết quả so sánh theo logic:

  • Nếu cả hai đều là null hoặc undefined (kể cả mixed), trả về true.
  • Nếu một trong hai là null/undefined và còn lại không, trả về false.
  • Nếu cả hai đều là number (không phải string), so sánh số.
  • Nếu cả hai đều là string số hợp lệ (ví dụ "5" và "5" hoặc "5" và 5), so sánh số.
  • Nếu một bên là empty string "" và bên còn lại là number, trả về false (không coerce).
  • Nếu không, so sánh strict ===.
js
console.log(safeCompare(null, undefined)); // true
console.log(safeCompare("5", 5));          // true (cả hai đều là string-number hợp lệ)
console.log(safeCompare("5", "6"));        // false
console.log(safeCompare(0, ""));           // false (không coerce, strict compare)
console.log(safeCompare("", ""));          // true (cùng type, strict equal)
[Đáp án tham khảo]
js
function safeCompare(a, b) {
  // Rule 1: null/undefined handling
  if (a == null && b == null) return true;
  if (a == null || b == null) return false;

  // Rule 2: both are actual numbers
  if (typeof a === 'number' && typeof b === 'number') {
    return a === b;
  }

  // Rule 3: both are string representations of valid numbers
  const aStr = String(a);
  const bStr = String(b);
  const aNum = Number(a);
  const bNum = Number(b);
  
  if (aStr !== '' && bStr !== '' && !Number.isNaN(aNum) && !Number.isNaN(bNum)) {
    return aNum === bNum;
  }

  // Rule 4: strict comparison for everything else
  return a === b;
}

Giải thích tại sao không dùng Number(a) trực tiếp:Number("") trả về 0, nên nếu chỉ dùng Number(a) và Number(b), safeCompare(0, "") sẽ trả về true (vì cả hai đều thành 0). Nhưng 0 và "" là hai giá trị semantically khác nhau — một là số 0, một là chuỗi rỗng. Hàm này phải tôn trọng type gốc khi không có lý do rõ ràng để coerce.

Lab B — Partial Scaffold (Khung sườn) ​

Hoàn thành hàm coerceReport nhận hai giá trị JavaScript thực tế và trả về chuỗi mô tả từng bước coercion nếu dùng ==. Không dùng if để hard-code từng case — hãy inspect type thực sự của a và b.

Yêu cầu: chỉ cần hỗ trợ 4 trường hợp phổ biến:

  1. Một bên là boolean → mô tả: boolean→number: X→Y
  2. Một bên là object (array) → mô tả: object→primitive: X→Y
  3. Một bên là string, một bên là number → mô tả: string→number: X→Y
  4. null == undefined → mô tả: spec rule: null == undefined
js
function coerceReport(a, b) {
  // TODO: nếu a hoặc b là boolean, trả về bước boolean→number
  // TODO: nếu a hoặc b là object (dùng typeof), trả về bước object→primitive
  // TODO: nếu một bên string và một bên number, trả về bước string→number
  // TODO: nếu (a === null && b === undefined) hoặc ngược lại, trả về spec rule
  // TODO: nếu cùng type, trả về 'same type, no coercion'
  // implement here
}

// Test cases
console.log(coerceReport([], false));   // "boolean→number: false→0; object→primitive: []→''; string→number: ''→0; 0==0→true"
console.log(coerceReport(0, ""));       // "string→number: ''→0; 0==0→true"
console.log(coerceReport(null, undefined)); // "spec rule: null == undefined"
console.log(coerceReport(5, 5));        // "same type, no coercion"

Gợi ý

Dùng typeof để detect boolean/object/string/number. Dùng String() để mô phỏng object→primitive (đủ cho bài này). Dùng Number() để mô phỏng string→number.

[Đáp án tham khảo]
js
function coerceReport(a, b) {
  const steps = [];
  
  // Case: null == undefined
  if ((a === null && b === undefined) || (a === undefined && b === null)) {
    return "spec rule: null == undefined";
  }
  
  // Case: same type
  if (typeof a === typeof b) {
    return "same type, no coercion";
  }
  
  // Step 1: boolean always converts to number first
  if (typeof a === 'boolean') {
    const num = Number(a);
    steps.push(`boolean→number: ${a}→${num}`);
    a = num;
  } else if (typeof b === 'boolean') {
    const num = Number(b);
    steps.push(`boolean→number: ${b}→${num}`);
    b = num;
  }
  
  // Step 2: object converts to primitive (string for arrays)
  if (typeof a === 'object' && a !== null) {
    const prim = String(a);
    steps.push(`object→primitive: [object]→'${prim}'`);
    a = prim;
  } else if (typeof b === 'object' && b !== null) {
    const prim = String(b);
    steps.push(`object→primitive: [object]→'${prim}'`);
    b = prim;
  }
  
  // Step 3: string vs number
  if (typeof a === 'string' && typeof b === 'number') {
    const num = Number(a);
    steps.push(`string→number: '${a}'→${num}`);
    a = num;
  } else if (typeof b === 'string' && typeof a === 'number') {
    const num = Number(b);
    steps.push(`string→number: '${b}'→${num}`);
    b = num;
  }
  
  // Final comparison
  steps.push(`${a}==${b}→${a == b}`);
  return steps.join('; ');
}

console.log(coerceReport([], false));
// "boolean→number: false→0; object→primitive: [object]→''; string→number: ''→0; 0==0→true"

console.log(coerceReport(0, ""));
// "string→number: ''→0; 0==0→true"

console.log(coerceReport(null, undefined));
// "spec rule: null == undefined"

Lab C — Independent (Tự làm) ​

Viết hàm normalizeAndSum nhận mảng các giá trị động từ form/API và trả về tổng số. Yêu cầu:

  • Bỏ qua null và undefined (coi như 0).
  • Chuyển string thành number. Nếu string không hợp lệ, bỏ qua (coi như 0).
  • Không dùng == để check "thiếu dữ liệu".
js
normalizeAndSum([1, "2", null, "abc", undefined, 3]); // 6
normalizeAndSum(["10", "", "5"]); // 15 ("" → 0)
[Đáp án tham khảo]
js
function normalizeAndSum(values) {
  return values.reduce((sum, val) => {
    if (val === null || val === undefined) return sum;
    const num = Number(val);
    if (Number.isNaN(num)) return sum;
    return sum + num;
  }, 0);
}

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

[] == false nhưng [] là truthy

js
if ([] == false) console.log("A"); // A
if ([]) console.log("B");          // B

[] == false là true vì coercion. Nhưng [] trong boolean context là true vì object luôn truthy. Đừng nhầm lẫn hai ngữ cảnh.

0 == "" nhưng 0 === "" là false

js
0 == ""   // true
0 === ""  // false

== coerce. === không coerce. Đây là lý do tại sao form input (luôn string) dễ gây bug với ==.

NaN không bằng bất kỳ thứ gì, kể cả chính nó

js
NaN == NaN   // false
NaN === NaN  // false

Nếu bạn cần check NaN, dùng Number.isNaN(). Không dùng == hoặc ===.

null và undefined chỉ tương đương với nhau

js
null == undefined  // true
null == 0          // false
undefined == false // false

Đừng tưởng null falsy nên null == false. null chỉ == với null và undefined.

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

Symptom (Triệu chứng): Khách hàng phàn nàn: "Tôi không nhập mã giảm giá nhưng hệ thống vẫn tự động giảm 10%."

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

js
function applyDiscount(price, discountCode) {
  if (discountCode == false) {
    return price * 0.9;
  }
  return price;
}

// Test cases
console.log(applyDiscount(100, "SAVE10")); // 100 ✓ (có mã, không giảm)
console.log(applyDiscount(100, ""));       // 90  ✗ (không có mã, vẫn giảm!)
console.log(applyDiscount(100, 0));        // 90  ✗ (mã là 0, vẫn giảm!)

Evidence (Bằng chứng):

  • "" == false → true (empty string bị coerced thành 0, false thành 0, 0 == 0)
  • 0 == false → true (số 0 bị match với boolean false)

Hypothesis (Giả thuyết): Developer dùng == false với ý nghĩa "nếu không có mã giảm giá" (falsy check). Nhưng == false không phải là falsy check — nó là coerce-to-number-then-compare. Empty string "" và số 0 đều bị chuyển thành 0, nên bị bắt nhầm.

Verification (Kiểm chứng):

js
console.log("" == false);  // true
console.log(0 == false);   // true
console.log(null == false); // false ← null KHÔNG bị bắt, vì null chỉ == với null/undefined

Root Cause (Nguyên nhân gốc):== false là equality coercion, không phải boolean context. Trong boolean context (if (value)), "" và 0 đều là falsy — đúng. Nhưng trong equality coercion, "" và 0 được chuyển thành số 0 rồi so sánh với 0 — kết quả là true, dù "" và 0 là hai giá trị semantically khác nhau.

Fix (Sửa):

js
function applyDiscount(price, discountCode) {
  // Cách 1: Dùng boolean context nếu ý là "falsy = không có mã"
  if (!discountCode) {
    return price; // không giảm
  }
  return price * 0.9; // có mã, giảm 10%
  
  // Cách 2: Dùng explicit check nếu cần phân biệt rõ ràng
  // if (discountCode === undefined || discountCode === "") {
  //   return price;
  // }
  // return price * 0.9;
}

Prevention (Phòng ngừa):

  • Không bao giờ dùng == false hoặc == true để check "falsy/truthy".
  • == false coerce cả hai về number — "", 0, [] (sau ToPrimitive) đều có thể thành 0.
  • Nếu muốn check "không có giá trị", dùng:
    • Boolean context: if (!value) (bắt tất cả falsy: "", 0, null, undefined, NaN)
    • Explicit null/undefined: if (value == null) (chỉ bắt null và undefined)
    • Explicit empty string: if (value === "") (chỉ bắt chuỗi rỗng)
  • Chọn công cụ phù hợp với loại "không có giá trị" bạn muốn bắt.

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

Context

Bạn đang review một module xử lý config. Đồng nghiệp dùng == ở 3 chỗ. Hãy quyết định: giữ nguyên, sửa thành ===, hay thay bằng explicit check.

js
// 1. Check timeout chưa set
if (config.timeout == null) {
  timeout = 5000;
}

// 2. Check retry bằng 0
if (config.retry == 0) {
  disableRetry();
}

// 3. Check mode là "strict"
if (config.mode == "strict") {
  enableStrict();
}
[Đáp án tham khảo]
ChỗĐánh giáLý do
timeout == nullGiữ nguyên== null là pattern chủ đích để match cả null và undefined. An toàn, ngắn gọn.
retry == 0Sửa thành ===== 0 sẽ match "", false, [] (sau coercion). Nếu chỉ muốn số 0, dùng === 0.
mode == "strict"Sửa thành ===Không cần coercion giữa string và string. === nhanh hơn, rõ ràng hơn, không có rủi ro.

Quy tắc: == null cho null/undefined check: OK. == với boolean hoặc number: tránh. == với string khi cả hai đều là string: dùng ===.

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

API trả về dữ liệu động:

json
{
  "count": "0",
  "enabled": "false",
  "items": [],
  "rate": null
}

Frontend xử lý:

js
function processConfig(data) {
  const count = data.count ? Number(data.count) : 10;
  const enabled = data.enabled == false ? false : true;
  const hasItems = data.items == true;
  const rate = data.rate == null ? 1.0 : data.rate;

  return { count, enabled, hasItems, rate };
}

Câu hỏi:

  1. count sẽ là bao nhiêu? Tại sao "0" gây bug?
  2. enabled sẽ là true hay false? Tại sao?
  3. hasItems sẽ là gì? Tại sao [] == true là kết quả đó?
  4. rate có đúng không?
  5. Viết lại hàm với explicit conversion và strict comparison.
[Đáp án tham khảo]
  1. count: data.count là "0". Non-empty string là truthy. data.count ? ... → truthy → Number("0") = 0. Kết quả: 0. Đúng về mặt kết quả nhưng fragile vì dùng truthiness, không phải explicit check. Nếu API trả về "", "" là falsy nên đi nhánh fallback 10 — dù "" có thể là input hợp lệ (count = 0). Vấn đề là truthiness check (?) không phân biệt được "0" (truthy) và "" (falsy), chứ không phải lỗi của Number("").

  2. enabled: data.enabled là "false". "false" == false → string "false" → number NaN, false → number 0. NaN == 0 → false. Vậy enabled = true (vì condition là == false ? false : true). BUG: "false" string bị coi là true.

  3. hasItems: [] == true → [] → "" → 0, true → 1. 0 == 1 → false. Kết quả: false. Đúng về mặt kết quả nhưng extremely fragile — dựa vào coercion chain phức tạp.

  4. rate: data.rate là null. null == null → true. Kết quả: 1.0. Đúng. Đây là trường hợp == null hoạt động đúng.

  5. Fix:

js
function processConfig(data) {
  const count = data.count !== undefined && data.count !== ""
    ? Number(data.count)
    : 10;
  const enabled = data.enabled === "true" || data.enabled === true;
  const hasItems = Array.isArray(data.items) && data.items.length > 0;
  const rate = data.rate == null ? 1.0 : Number(data.rate);

  return { count, enabled, hasItems, rate };
}

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

Level C — Delegate & Inspect (Giao việc & Kiểm tra)

  1. Tự trả lời trước: Trace [] == false từng bước trên giấy. Viết rõ: bước nào convert type nào thành gì.
  2. Hỏi AI: "Explain step by step why [] == false is true in JavaScript."
  3. Inspect: AI có đủ 3 bước không? (boolean→number, object→primitive, string→number). AI có nói [] chuyển thành "" không, hay AI nói [] chuyển thành 0?
  4. Verify: Đọc ECMAScript Abstract Equality Comparison spec (hoặc MDN summary). Xem thứ tự conversion có đúng như AI nói không?

Gợi ý

Hãy để ý xem AI có nói [] trở thành 0 trực tiếp không. Nếu AI nói vậy, đó là lỗi: [] trước tiên trở thành "" (qua ToPrimitive), rồi "" mới trở thành 0 (qua ToNumber). Nhiều AI sẽ bỏ qua bước object→string và nói object→number, gây hiểu sai về algorithm.

Đáp án tham khảo
  • Bạn nghĩ

    • Bước 1: [] == false — boolean bên phải. Theo spec, boolean được chuyển thành number trước. false → 0.
    • Bước 2: [] == 0 — object bên trái. Object được chuyển thành primitive (ToPrimitive). Array rỗng → "".
    • Bước 3: "" == 0 — string bên trái, number bên phải. String được chuyển thành number. "" → 0.
    • Bước 4: 0 == 0 → true.
  • AI trả lời

    • AI thường nói: "First, false is converted to a number, becoming 0. Then [] is converted to a primitive value. An empty array becomes an empty string ''. Then '' is converted to 0. Finally 0 == 0 is true."
    • AI tốt sẽ đủ 3 bước.
    • AI kém có thể nói: "[] becomes 0 and false becomes 0, so 0 == 0 is true." — Bỏ qua bước [] → "".
  • So sánh

    • AI đúng về kết quả cuối.
    • AI thiếu (nếu có): bước trung gian [] → "". Điều này quan trọng vì nếu [1] thì thành "1", không phải 0.
  • Điểm AI nói sai hoặc quá mơ hổ

    • Câu "[] becomes 0" là sai về bước trung gian. [] không trực tiếp thành 0. Nó thành "" rồi "" thành 0.
    • AI có thể nói "arrays are converted to numbers in comparisons" — quá mơ hồ, không đúng cho mọi trường hợp.
  • Kết luận

    • Nếu bạn chỉ ra được bước trung gian [] → "" → 0, bạn đã hiểu sâu hơn AI. Điều này giúp bạn dự đoán đúng [1] == true ("1" → 1, true → 1, 1 == 1 → true) thay vì đoán mò.
    • Concept này sẽ quay lại ở Stage 2 (ToPrimitive algorithm chi tiết) và Stage 6 (TypeScript type narrowing).

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 '' == false là true, nhưng '' === false là false? Và tại sao tôi không nên dùng == false để check 'không có giá trị'?"

Yêu cầu:

  • Dùng đúng từ: coercion, type conversion, strict equality, boolean context.
  • Trace ít nhất 2 bước conversion trong '' == false.
Mô phỏng
  • Bạn nói
    • "== và === hỏi hai câu khác nhau. == hỏi: 'Nếu tôi chuyển đổi chúng về cùng type, chúng có giống nhau không?' Còn === hỏi: 'Chúng có cùng type VÀ cùng value không?'"
    • "Với '' == false: type khác nhau — string và boolean. Theo algorithm, boolean được chuyển thành number trước. false thành 0."
    • "Giờ ta có '' == 0. String và number — string được chuyển thành number. '' thành 0."
    • "Cuối cùng 0 == 0 → true. Còn === thấy string vs boolean, khác type, trả false ngay."
    • "Vì vậy, tôi không dùng == false để check 'không có giá trị'. '' là empty string (có thể là input hợp lệ), 0 là số hợp lệ, nhưng cả hai đều bị == false bắt nhầm. Tôi dùng explicit check: === "", === 0, hoặc == null nếu chỉ muốn bắt null/undefined."

💡 Liên tưởng: == false giống như một cái rây có lỗ quá to. Bạn định lọc ra "không có gì", nhưng rây cũng bắt luôn empty string và số 0 vì chúng đều "lọt" qua cùng một quá trình coercion. === giống như một cái rây có kích thước lỗ chính xác — bạn phải chọn đúng kích thước cho đúng loại "không có gì" bạn muốn lọc.

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

  • Đồng nghiệp có hỏi "vậy [1] == true là gì?" không? Nếu có, bạn đã truyền đạt đủ rõ để họ muốn apply mental model.
  • Bạn có thể trace 0 == "" theo cùng logic không? (String "" → number 0, rồi 0 == 0.)

15. Assessment (Đánh giá) ​

Objective (Mục tiêu)Hình thức đánh giá
Trace coercion từng bướcExplanation (Giải thích)
Dự đoán kết quả == phức tạpPrediction (Dự đoán)
Chọn == / === / explicit conversion đúngDesign Decision (Quyết định thiết kế)
Viết hàm xử lý mixed-type inputImplementation (Thực hành)
Debug bug từ == falseDebug Lab (Gỡ lỗi)

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

  • [ ] Có thể trace đúng 6/6 biểu thức trong Prediction Exercise với đủ bước coercion.
  • [ ] Có thể giải thích [] == false qua 3 bước: boolean→number, object→primitive, string→number.
  • [ ] Có thể viết hàm xử lý input động mà không bị implicit coercion đánh lừa.
  • [ ] Có thể debug trường hợp == false hoặc == 0 bắt nhầm empty string.
  • [ ] Có thể chọn == null đúng context và giải thích tại sao nó khác == false.
  • [ ] Không còn trả lời "JavaScript nó thế" khi được hỏi về coercion.

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

Previous (Trước): Lesson 0.3.1 — Truthy & Falsy; Lesson 0.3.2 — Equality; Lesson 0.3.3 — Type Conversion. Bạn đã học từng khía cạnh của coercion riêng lẻ.

Current (Hiện tại): Lesson 0.3.4 — Coercion Lab. Bạn tích hợp cả Module 0.3: trace multi-step coercion, viết code an toàn, debug production bug.

Next (Tiếp theo):

  • Module 0.4 — Operators & Control Flow (&&, ||, ??, guard clauses, short-circuit — tất cả dựa trên boolean context và coercion).
  • Stage 2 — Object Model: ToPrimitive chi tiết (object→string/number algorithm, valueOf, toString, Symbol.toPrimitive).
  • Stage 6 — TypeScript: Type system loại bỏ hầu hết coercion bug tại compile time.
  • Stage 9 — Production Frontend: Form validation patterns để tránh coercion từ input string.
📴 Offline Mode — Content served from cache