Skip to content

Lesson 0.2.1 — Values ​

Bài 0.2.1 — Values
Primitive, Object, Identity & Equality • 22 phút
0:00 / 0:00

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

KeyValue
Stage0 — JavaScript Language Foundation
Module0.2 — Values, Variables & Types
Lesson0.2.1 — Values
CompetencyC01 — JavaScript Language
Depth TargetL2–L3 (Explain → Use)
PrerequisitesLesson 0.1.3 — JavaScript Execution ở mức khái niệm
Estimated Cognitive LoadLow–Medium

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

Bạn chạy đoạn code sau:

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

Kết quả là false. Nhưng hai bên đều là array rỗng — tại sao không bằng nhau?

Hoặc đoạn này:

js
const a = "hello";
const b = a;
// b thay đổi? Không — string là immutable.

Nhưng:

js
const x = { count: 1 };
const y = x;
y.count = 2;
console.log(x.count); // 2

Junior thường nhìn code và thấy "biến a bằng 5", "biến x là object". Nhưng đằng sau đó, JavaScript không làm việc với "biến" — nó làm việc với value. Biến chỉ là cái tên gắn vào value.

Nếu không phân biệt được value là gì, primitive khác object như thế nào, và identity có nghĩa là gì, bạn sẽ:

  • So sánh object bằng === và ngạc nhiên kết quả sai.
  • Nghĩ const ngăn object thay đổi.
  • Không hiểu tại sao typeof null là "object".

Lesson này xây mental model về value trước khi bạn học variable, type, và equality.

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 parse rồi mới execute code.
  • typeof là toán tử cấp language.
  • Có sự khác biệt giữa Browser và Node.js runtime.

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

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

  1. Phân biệt value và variable ở mức khái niệm.
  2. Phân loại một value thành Primitive hoặc Object.
  3. Giải thích tại sao [] === [] là false bằng khái niệm identity.
  4. Dự đoán kết quả của typeof và === cho các value phổ biến.
  5. Nhận diện immutable primitive và mutable object.

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

Value vs Variable (Giá trị vs Biến)

Value là "thứ" thực sự tồn tại trong memory khi JavaScript chạy. Variable là tên (binding) gắn vào value đó.

js
const score = 100;
  • 100 là value.
  • score là variable trỏ đến value 100.

Two Kinds of Values (Hai loại giá trị)

Value, Variable và Object Identity trong JavaScript

Primitive: Đơn giản, không thể thay đổi nội bộ (immutable), không có "danh tính" riêng. Số 5 ở đâu cũng là số 5.

Object: Cấu trúc phức tạp, có thể thay đổi nội bộ (mutable), có "danh tính" riêng. Mỗi lần bạn viết {}, JavaScript tạo ra một object hoàn toàn mới trong memory — dù nội dung nhìn giống hệt nhau.

Identity (Danh tính)

Identity là cách engine phân biệt hai object trong memory.

js
{} === {} // false

Tại sao? Vì bên trái tạo ra object A, bên phải tạo ra object B. === kiểm tra identity: "Đây có phải cùng một object trong memory không?" → Không.

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

Essential (Bắt buộc) ​

5.1 What Is a Value? (Value là gì?) ​

Value là đơn vị dữ liệu cơ bản mà JavaScript engine thao tác. Mọi expression trong JavaScript đều evaluate về một value.

js
42;        // value: number 42
"hello";   // value: string "hello"
{};        // value: object
[];        // value: array (một loại object)

5.2 Primitive Values (Giá trị nguyên thủy) ​

Các primitive value trong JavaScript:

TypeVí dụĐặc điểm
string"hello"Immutable
number42, 3.14Immutable
booleantrue, falseImmutable
nullnullImmutable
undefinedundefinedImmutable
bigint9007199254740991nImmutable
symbolSymbol("id")Immutable

Immutable có nghĩa là value không thể thay đổi từ bên trong. Bạn không thể sửa ký tự thứ 3 của string:

js
const name = "Ada";
name[0] = "B"; // Không có lỗi, nhưng không có tác dụng
console.log(name); // "Ada"

Awareness — Ngoại lệ Symbol:

Symbol là primitive, nhưng mỗi Symbol("id") tạo ra value duy nhất trong suốt vòng đời program. Vì vậy Symbol("a") !== Symbol("a"). Đây là exception cho mental model "compared by value" — sẽ đào sâu ở Lesson 0.2.3.

5.3 Object Values (Giá trị object) ​

Object value bao gồm:

CategoryVí dụĐặc điểm
Plain Object{ name: "A" }Mutable, có identity
Array[1, 2, 3]Mutable, có identity
Function() => {}Mutable, có identity, callable
Datenew Date()Mutable, có identity
RegExp/abc/Mutable, có identity
Map / Setnew Map()Mutable, có identity

Mutable có nghĩa là nội dung bên trong có thể thay đổi mà value đó vẫn là "chính nó":

js
const user = { name: "A" };
user.name = "B"; // Object vẫn là object cũ, nhưng nội dung đổi

5.4 Equality & Identity (Bằng nhau và Danh tính) ​

Với primitive, === so sánh value:

js
5 === 5;        // true
"hi" === "hi";  // true

Với object, === so sánh identity (không phải nội dung):

js
{ a: 1 } === { a: 1 }; // false
[1] === [1];           // false

Practical Tip (Quyết định thực tế)

Khi so sánh hai array hoặc object bằng ===, bạn đang hỏi: "Chúng có phải là cùng một thực thể trong memory không?" — không phải "Nội dung của chúng có giống nhau không?"

Supporting (Hỗ trợ) ​

  • Wrapper Objects: String, Number, Boolean là object constructors. Khi bạn gọi "hello".toUpperCase(), engine tạm thời wrap primitive string thành object để truy cập method, rồi unwrap lại. Bạn không cần tự tạo new String().
  • Function là object: Mọi function đều là object value, nhưng typeof trả về "function" vì tiện lợi.

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

  • BigInt và Symbol là primitive types đặc biệt. Sẽ đào sâu ở Lesson 0.2.3.
  • typeof null === "object" là lỗi lịch sử của JavaScript, không thể sửa vì break legacy code.

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

  • Reference mechanics chi tiết (sẽ học ở Lesson 0.2.2).
  • == vs === vs Object.is (sẽ học ở Lesson 0.3.2).
  • Prototype chain.
  • Deep clone / shallow copy (sẽ học ở Lesson 0.6.8).
  • Memory allocation internals.

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

Đoạn code:

js
const a = 100;
const b = 100;
console.log(a === b);

const x = { id: 1 };
const y = { id: 1 };
console.log(x === y);

Phân tích từng bước:

Step 1 — Primitive comparison:

  • 100 là primitive value.
  • a gắn vào value 100.
  • b gắn vào value 100.
  • a === b so sánh value: 100 === 100 → true.

Step 2 — Object creation:

  • { id: 1 } đầu tiên tạo ra một object value mới trong memory. x gắn vào object này.
  • { id: 1 } thứ hai tạo ra một object value khác trong memory. y gắn vào object khác này.

Step 3 — Object comparison:

  • x === y so sánh identity: "Có phải cùng một object trong memory không?"
  • Không — đây là hai object riêng biệt → false.

Kết luận: Sự khác biệt nằm ở loại value. Primitive so sánh bằng nội dung. Object so sánh bằng identity.

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

Đừng chạy code. Dự đoán output và giải thích tại sao.

Câu 1:

js
console.log(typeof "hello");
console.log(typeof 42);
console.log(typeof true);
[Đáp án & Giải thích]
"string"
"number"
"boolean"

Cả ba đều là primitive values. typeof trả về tên type tương ứng.

Câu 2:

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

{} và [] đều là object values. typeof [] là "object" (array không có type riêng trong typeof). typeof function là "function" — đây là ngoại lệ lịch sử tiện lợi, dù function vẫn là object.

Câu 3:

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

Mỗi object literal tạo ra một object value mới với identity riêng. === so sánh identity, không phải nội dung.

Câu 4:

js
const a = null;
console.log(typeof a);
[Đáp án & Giải thích]
"object"

null là primitive value, nhưng typeof null trả về "object" vì lỗi đầu tiên của JavaScript specification (không thể sửa vì backward compatibility). Đây là edge case bắt buộc phải nhớ.

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

Nhiệm vụ: Phân loại các value sau vào bảng.

js
42
"hello"
true
null
undefined
{}
[]
() => {}
new Date()
ValuePrimitive / ObjecttypeofImmutable?
42
"hello"
true
null
undefined
{}
[]
() => {}
new Date()
[Đáp án & Giải thích]
ValuePrimitive / ObjecttypeofImmutable?
42Primitive"number"Yes
"hello"Primitive"string"Yes
truePrimitive"boolean"Yes
nullPrimitive"object"Yes
undefinedPrimitive"undefined"Yes
{}Object"object"No
[]Object"object"No
() => {}Object"function"No
new Date()Object"object"No

Lưu ý: typeof null === "object" là ngoại lệ duy nhất trong bảng — primitive nhưng typeof báo object.

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

typeof null === "object"

js
typeof null; // "object"

null là primitive value đại diện cho "không có object". Nhưng vì lý do lịch sử trong ECMAScript, typeof null trả về "object".

Hậu quả: Kiểm tra if (typeof x === "object") sẽ match cả null. Trong production, cần loại trừ:

js
if (x !== null && typeof x === "object") {
  // x thực sự là object
}

typeof [] === "object"

js
typeof []; // "object"

Array là object value. typeof không phân biệt array và plain object. Để kiểm tra array, dùng:

js
Array.isArray([]); // true

Function là object nhưng typeof đặc biệt

js
typeof function() {}; // "function"

Function là object value (có identity, có thể thêm property), nhưng typeof có riêng chuỗi "function" để tiện kiểm tra.

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

Tình huống: Bạn viết hàm kiểm tra cấu hình:

js
function isSameConfig(a, b) {
  return a === b;
}

const configA = { theme: "dark" };
const configB = { theme: "dark" };

console.log(isSameConfig(configA, configB));

Symptom (Triệu chứng): Luôn trả về false dù hai object có nội dung giống hệt nhau.

Reproduction (Tái hiện lỗi): Chạy code trên.

Evidence (Bằng chứng): Output là false. Không có lỗi thrown.

Hypothesis (Giả thuyết): === giữa hai object so sánh identity, không phải nội dung. configA và configB là hai object value riêng biệt trong memory.

Verification (Xác minh):

js
console.log(configA === configA); // true — cùng identity
console.log(configA === configB); // false — khác identity

Root Cause (Nguyên nhân gốc rễ): Nhầm lẫn mental model equality. Người viết mong đợi === so sánh "nội dung giống nhau" như primitive, nhưng object comparison dựa trên identity.

Fix (Sửa): Nếu cần so sánh nội dung object, phải so sánh từng property hoặc dùng utility (JSON.stringify cho simple case, hoặc deep equality library). Ở Stage 0, cách đơn giản nhất:

js
function isSameConfig(a, b) {
  return a.theme === b.theme;
}

Prevention (Phòng ngừa):

  • Khi so sánh object, luôn tự hỏi: "Tôi muốn so sánh identity hay nội dung?"
  • Nếu so sánh nội dung, đừng dùng === trực tiếp trên object.

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

Context (Bối cảnh): Trong React component, bạn memoize một prop object:

js
const config = { apiKey: "secret" };

function App() {
  return <Child config={config} />;
}

Sau đó bạn refactor:

js
function App() {
  return <Child config={{ apiKey: "secret" }} />;
}

Symptom: Component Child re-render liên tục dù prop nhìn giống hệt.

Root Cause: Mỗi lần App render, { apiKey: "secret" } tạo ra một object value mới với identity mới. React so sánh identity của props (shallow comparison) và thấy khác → re-render.

Lesson: Object identity ảnh hưởng trực tiếp đến rendering behavior trong production. Sẽ đào sâu ở Stage 8.

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

Level A — Ask (Hỏi)

  1. Tự trả lời: "Tại sao typeof null là "object"?" Viết câu trả lời của bạn.
  2. Hỏi AI cùng câu hỏi đó.
  3. So sánh: AI có nhắc đến "lỗi lịch sử của ECMAScript implementation" và "backward compatibility" không?
  4. Verify: Tra MDN hoặc ECMAScript specification để xác nhận.

Gợi ý

Nhiều AI sẽ đoán mò hoặc giải thích sai về typeof null. Đây là bài kiểm tra kỹ năng verify thông tin kỹ thuật.

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

typeof null là "object" vì đây là một lỗi lịch sử trong implementation đầu tiên của JavaScript. Hồi đó, engine đánh dấu type bằng các bit flag ở đầu vùng nhớ. Object có tag bắt đầu bằng 000, và null cũng được biểu diễn bằng toàn bit 0 (0x00). Khi kiểm tra type, engine nhìn vào các bit đầu, thấy 000 thì tưởng là object. Lỗi này không thể sửa vì sửa sẽ break hàng triệu dòng code đang chạy — đó là backward compatibility. Nên đến giờ typeof null vẫn là "object", dù null hoàn toàn không phải object.

  • AI trả lời

AI thường nói:

"typeof null trả về "object" vì null được coi là một kiểu object đặc biệt trong JavaScript. Đây là một đặc điểm của ngôn ngữ. Một số người cho rằng đây là bug, nhưng thực tế nó đã tồn tại từ lâu và không thể thay đổi vì lý do tương thích ngược."

Hoặc AI có thể nói:

"null đại diện cho một object rỗng, nên typeof null là "object"."

  • So sánh

Câu trả lời của AI có thể đúng ở chỗ nhắc đến "backward compatibility" hoặc "lỗi lịch sử", nhưng thường thiếu chi tiết kỹ thuật cụ thể. AI hiếm khi giải thích tại sao lỗi này xảy ra ở mức implementation — vì null được biểu diễn bằng 0x00 (tất cả bit 0), trùng với bit pattern của object pointer trong engine đời đầu.

Nếu AI nói "null đại diện cho object rỗng" — đây là hiểu nhầm nghiêm trọng. null không phải object, không có prototype, không có property. Nó là một primitive value đại diện cho "không có giá trị".

  • Điểm AI nói sai hoặc quá mơ hồ

AI thường dùng từ "được coi là một kiểu object đặc biệt" hoặc "đại diện cho object rỗng". Điều này gợi ý rằng null có ý nghĩa semantic là object — hoàn toàn sai. null là primitive, không phải object.

Ngoài ra, AI thường không nhắc đến bit pattern hoặc type tag trong engine implementation — đây là nguyên nhân gốc rễ. Câu trả lời của AI mang tính "bề mặt" và đôi khi gây hiểu nhầm về bản chất của null.

  • Kết luận

Nếu AI không phân biệt rõ: null là primitive, không phải object; và lỗi typeof null xuất phát từ bit representation trong engine đời đầu chứ không phải từ ý định thiết kế — bạn đã verify được thông tin chính xác hơn AI. Đây là lý do ta luôn kiểm chứng bằng spec thay vì tin hoàn toàn vào AI.

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

Yêu cầu: Giải thích trong 2 phút (viết hoặc nói):

"Primitive value khác Object value như thế nào? Và tại sao [] === [] lại là false?"

Yêu cầu:

  • Có nhắc đến immutable vs mutable không?
  • Có nhắc đến identity không?
  • Có giải thích === behavior khác nhau giữa hai loại không?
  • Người nghe có thể dự đoán {} === {} không?
Mô phỏng
  • Bạn nói

Primitive là giá trị đơn giản: string, number, boolean, null, undefined, symbol, bigint. Đặc điểm là immutable — tạo ra rồi thì không sửa được, chỉ có thể thay thế bằng giá trị mới.

Object là container: object, array, function. Đặc điểm là mutable — tạo ra rồi vẫn có thể thêm, xóa, sửa bên trong.

Khi so sánh bằng ===, primitive so sánh theo giá trị. 5 === 5 là true vì cả hai bên đều là số 5. "a" === "a" là true vì cùng chuỗi "a".

Object thì khác. === không so sánh nội dung bên trong, mà so sánh identity — tức là "có phải là cùng một object trong bộ nhớ không".

Khi viết [] === [], bên trái tạo ra một array mới ở một vùng nhớ. Bên phải tạo ra một array mới ở vùng nhớ khác. Hai vùng nhớ khác nhau, hai identity khác nhau. Nên false.

Còn nếu viết:

js
const a = [];
const b = a;
a === b; // true

Vì b không tạo array mới, nó chỉ trỏ đến cùng array với a. Cùng identity nên true.

Nói chung: Primitive so sánh giá trị, Object so sánh identity. Vì vậy {} === {} cũng là false — hai object riêng biệt.

💡 Dễ nhớ: Primitive giống như tiền mặt — hai tờ 100k thì bằng nhau vì cùng giá trị. Object giống như chìa khóa nhà — hai chìa khóa nhìn giống hệt nhau nhưng mở hai căn nhà khác nhau. === không hỏi "có giống nhau không", mà hỏi "có phải là cùng một cái không".

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

  • Đồng nghiệp có tự dự đoán được {} === {} là false không?
  • Họ có hiểu tại sao const a = []; a.push(1) được nhưng const s = "a"; s.toUpperCase() lại không sửa được s không?
  • Nếu họ hỏi "vậy === so sánh reference à?" — bạn sửa lại bằng từ identity được không?
⚠️ Lưu ý: Reference ≠ Identity

Reference và Identity — đừng nhầm là một.

Hai khái niệm này liên quan rất chặt, nhưng không phải là một.

text
const a = { x: 1 };

Variable / Binding: a
      │
      │ reference
      ▼
┌───────────────┐
│   Object      │
│  Identity #1  |
│               │
│   { x: 1 }    │
└───────────────┘
  • Identity là danh tính của chính object — trả lời câu hỏi:

“Đây có phải là cùng một object không?” Identity #1 chính là object { x: 1 }.

  • Reference là mối liên hệ từ một binding đến object — trả lời câu hỏi:

“Binding này đang tham chiếu đến object nào?”

Ví dụ:

js
const a = { x: 1 };
const b = a;

Có thể hình dung:

text
a ── reference ──┐
                 ├──→ Identity #1
b ── reference ──┘      { x: 1 }

a và b là hai binding khác nhau, nhưng cùng reference đến một identity.

js
a === b; // true

Ngược lại:

js
const a = { x: 1 };
const b = { x: 1 };

thì:

text
a ──reference──→ Identity #1
                  { x: 1 }


b ──reference──→ Identity #2
                  { x: 1 }

Nội dung giống nhau, nhưng là hai object khác nhau, nên có hai identity khác nhau.

text
a === b; // false

💡 Cách nhớ

Reference = "đang trỏ tới đâu?"

Identity = "đó là object nào?"

Vì vậy, khi nói về === với Object, nên nói:

=== kiểm tra object identity: hai bên có phải đang tham chiếu đến cùng một object hay không.

Đừng đồng nhất reference = identity. Một identity có thể được nhiều binding cùng reference tới:

text
a ──┐
    ├── reference ──→ Identity #1
b ──┘

Đó chính là lý do:

js
const a = [];
const b = a;

a === b; // true

14. Assessment (Đánh giá) ​

Objective (Mục tiêu)Format (Hình thức đánh giá)Depth
Phân biệt Primitive và ObjectClassification (Phân loại)L2
Giải thích identityExplain (Giải thích)L2
Dự đoán typeof và ===Prediction (Dự đoán)L2
Debug object comparison bugDebug (Gỡ lỗi)L3

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

  • [ ] Có thể phân loại một value bất kỳ thành Primitive hoặc Object.
  • [ ] Có thể giải thích tại sao [] === [] là false bằng khái niệm identity.
  • [ ] Có thể dự đoán đúng typeof cho 8/10 value phổ biến (bao gồm cả null và []).
  • [ ] Có thể giải thích tại sao primitive là immutable và object là mutable.
  • [ ] Có thể debug tình huống so sánh object bằng === và nhận ra đó là identity comparison.

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

Previous (Trước): Execution Flow — Bạn đã biết engine parse rồi execute code. Giờ bạn hiểu engine thao tác với value trong quá trình đó.

Current (Hiện tại): Value Mental Model — Bạn biết JavaScript có hai loại value: Primitive (immutable, no identity) và Object (mutable, has identity). Bạn biết typeof và === behavior khác nhau tùy loại.

Next (Tiếp theo):

  • Lesson 0.2.2 — Variables & Bindings: Bạn sẽ học variable không phải là "hộp chứa value" mà là "nhãn dán gắn vào value". Từ đó hiểu const vs let vs var và tại sao const object vẫn cho phép mutation.
  • Lesson 0.2.3 — Primitive Types: Đào sâu từng primitive type: Number, String, Boolean, Null, Undefined, BigInt, Symbol.
  • Lesson 0.2.4 — Object Values: Object, Array, Function, Date, RegExp, Map, Set — chi tiết hơn về object category.
  • Lesson 0.6.8 — Spread & Shallow Copy: Identity và reference sẽ quay lại khi bạn học copy object.
  • Stage 2 — Object Model: Prototype, internal slots, object mechanics đầy đủ.
📴 Offline Mode — Content served from cache