Skip to content

Lesson 0.4.6 — for...of vs for...in ​

Bài 0.4.6 — for...of vs for...in
Enumerable properties vs Iterable values • 26 phút
0:00 / 0:00

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

FieldValue
Stage0 — JavaScript Language Foundation
Module0.4 — Operators & Control Flow
Lesson0.4.6 — for...of vs for...in
CompetencyC01.4 — Control Flow
DepthL3 (Use)
Prerequisites0.4.5 Loops, 0.2.4 Object Values, 0.6.2 Arrays
Estimated Cognitive LoadMedium

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

Bạn viết đoạn code sau để in các số trong mảng:

js
const scores = [85, 90, 78];
for (const key in scores) {
  console.log(key);
}
// Output: "0", "1", "2" ❌

Bạn mong đợi 85, 90, 78, nhưng nhận được "0", "1", "2". Tại sao?

Vì for...in không lặp qua giá trị. Nó lặp qua tên thuộc tính (property keys) của object. Với mảng, các key là "0", "1", "2".

Đây là một trong những bug phổ biến nhất của Junior JavaScript — dùng for...in trên mảng. Lesson này dạy bạn:

  • for...in làm gì và tại sao nó không phù hợp với mảng.
  • for...of làm gì và tại sao nó phù hợp với mảng/string.
  • Khi nào for...in thực sự hữu ích (object properties).

Preview

Bạn sẽ học: for...in lặp qua tên thuộc tính (keys), kể cả trên prototype. for...of lặp qua giá trị (values), chỉ hoạt động với iterable. Array là iterable, object thường không phải.

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

Trước khi học lesson này, bạn phải:

  • [ ] Viết được for loop cơ bản (0.4.5).
  • [ ] Biết mảng là object có index là key (0.2.4, 0.6.2).
  • [ ] Hiểu Object.keys trả về gì (0.6.6).
  • [ ] Biết object có properties (0.6.5).

Nếu thiếu một trong các kỹ năng trên, quay lại lesson tương ứng trước.

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

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

  1. Phân biệt for...in (keys/properties) và for...of (values).
  2. Dự đoán output của cả hai với array, string, và object.
  3. Giải thích tại sao for...in trên array là anti-pattern.
  4. Chọn đúng loại vòng lặp cho từng kiểu dữ liệu.
  5. Nhận diện bug do nhầm lẫn for...in với for...of.

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

Hãy hình dung một chiếc tủ đựng đồ:

text
for...in  : "Tôi muốn biết tên từng ngăn kéo."
            → "topDrawer", "middleDrawer", "bottomDrawer"

for...of  : "Tôi muốn lấy đồ từng ngăn kéo."
            → "socks", "shirts", "pants"

Với mảng:

text
Array: ["apple", "banana", "cherry"]

for...in  → "0", "1", "2"  (tên ngăn — string keys)
for...of  → "apple", "banana", "cherry"  (đồ trong ngăn — values)

Điểm then chốt

for...in lặp qua tất cả enumerable properties — không chỉ array elements. Nếu ai đó thêm Array.prototype.customMethod = ..., for...in sẽ duyệt qua cả "customMethod". Đây là lý do chính không dùng for...in trên array.

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

Essential (Bắt buộc) ​

Khái niệmÝ nghĩaDùng cho
for...inLặp qua enumerable property keys (string)Object properties
for...ofLặp qua iterable valuesArray, String, Map, Set, v.v.
EnumerableProperty có thể được liệt kê bằng for...inQuyết định for...in có "thấy" không
IterableObject có thể lặp qua từng giá trịQuyết định for...of có hoạt động không

Supporting (Hỗ trợ) ​

  • Object.hasOwn / hasOwnProperty: kiểm tra property thuộc object hay prototype (dùng với for...in).
  • Object.keys(), Object.values(), Object.entries(): alternatives để lặp object.

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

  • Iterator Protocol (Stage 2): cơ chế bên dưới khiến for...of hoạt động.
  • Prototype chain (Stage 2): lý do for...in có thể duyệt qua properties không mong muốn.

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

  • Custom iterators (Stage 2).
  • Symbol.iterator (Stage 2).
  • Prototype chain đào sâu (Stage 2).
  • Generators (Stage 2).

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

Ví dụ 1: Array ​

js
const fruits = ["apple", "banana", "cherry"];

Bước 1 — for...in:

js
for (const key in fruits) {
  console.log(key, typeof key);
}
// "0" string
// "1" string
// "2" string

key là string — "0", "1", "2". Không phải số. Nếu bạn cần index số, phải parse.

Bước 2 — for...of:

js
for (const fruit of fruits) {
  console.log(fruit);
}
// "apple"
// "banana"
// "cherry"

fruit là giá trị tại mỗi vị trí. Không cần dùng fruits[i].

Bước 3 — Khi nào cần cả index lẫn value?

js
for (const [index, fruit] of fruits.entries()) {
  console.log(index, fruit);
}
// 0 "apple"
// 1 "banana"

Hoặc dùng for thông thường nếu không cần destructuring.

Ví dụ 2: Object ​

js
const user = { name: "Alice", age: 30 };

Bước 1 — for...in (phù hợp):

js
for (const key in user) {
  console.log(key, user[key]);
}
// "name" "Alice"
// "age" 30

Bước 2 — for...of (KHÔNG hoạt động):

js
for (const item of user) {
  console.log(item);
}
// TypeError: user is not iterable ❌

Object thông thường không iterable. Muốn dùng for...of, phải chuyển qua Object.entries().

js
for (const [key, value] of Object.entries(user)) {
  console.log(key, value);
}
// "name" "Alice"
// "age" 30

Ví dụ 3: String ​

js
const word = "hi";

for (const char of word) {
  console.log(char);
}
// "h"
// "i"

String là iterable — for...of duyệt qua từng ký tự.

Code Review Lens

Khi review code, nếu thấy for...in trên array, đây là red flag. Hỏi: "Bạn muốn key hay value? Nếu value, dùng for...of."

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

WARNING

Đừng chạy code. Đoán output và giải thích.

Câu 1 ​

js
const arr = [10, 20];
arr.foo = "extra";

for (const x in arr) {
  console.log(x);
}

Dự đoán của bạn:

  • In ra những gì?
  • "foo" có xuất hiện không? Tại sao?

Câu 2 ​

js
const arr = [10, 20];
arr.foo = "extra";

for (const x of arr) {
  console.log(x);
}

Dự đoán của bạn:

  • In ra những gì?
  • "extra" có xuất hiện không? Tại sao?

Câu 3 ​

js
const obj = { a: 1, b: 2 };

for (const x of obj) {
  console.log(x);
}

Dự đoán của bạn:

  • Output?
  • Tại sao?

Câu 4 ​

js
const str = "AB";
for (const ch in str) {
  console.log(ch);
}

Dự đoán của bạn:

  • Output?
  • ch là string hay number?
[Đáp án & Giải thích]

Câu 1:

  • Output: "0", "1", "foo"
  • "foo" xuất hiện vì for...in lặp qua tất cả enumerable properties, kể cả properties do ngườì dùng thêm vào mảng (array là object).

Câu 2:

  • Output: 10, 20
  • "extra" không xuất hiện. for...of lặp qua iterable values theo index số (0, 1). Nó không "thấy" foo vì foo không phải array element.

Câu 3:

  • Output: TypeError: obj is not iterable
  • Object thông thường không implement iterable protocol. for...of không hoạt động trên plain object.

Câu 4:

  • Output: "0", "1"
  • ch là string. String là object-like, nên for...in duyệt qua index properties "0", "1".

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

Level 1 — Guided (Hướng dẫn) ​

Dùng for...of để tính tổng các số trong mảng:

js
const numbers = [1, 2, 3, 4, 5];
let sum = 0;

// Dùng for...of
for (/* implement */) {
  /* implement */
}

console.log(sum); // 15
[Gợi ý]
js
for (const num of numbers) {
  sum += num;
}

Level 2 — Partial Scaffold (Khung mẫu) ​

Hoàn thành hàm countProperties. Sử dụng for...in để đếm số lượng own properties của object, không tính các properties được kế thừa từ prototype.

js
function countProperties(obj) {
  let count = 0;

  // Dùng for...in + hasOwnProperty
  for (/* implement */) {
    if (/* implement */) {
      count++;
    }
  }

  return count;
}

// Test
console.log(countProperties({ a: 1, b: 2 })); // 2
Đáp án tham khảo
js
function countProperties(obj) {
  let count = 0;

  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      count++;
    }
  }

  return count;
}

Giải thích

for...in duyệt qua các enumerable properties của object, bao gồm:

  • properties thuộc trực tiếp obj;
  • properties được kế thừa từ prototype.

Vì bài yêu cầu chỉ đếm own properties, cần kiểm tra:

js
obj.hasOwnProperty(key);

Method này trả về true khi key thuộc trực tiếp obj, và false khi key được kế thừa từ prototype.

Ví dụ:

js
const parent = {
  age: 20
};

const obj = Object.create(parent);
obj.name = "An";

for (const key in obj) {
  console.log(key);
}

// name
// age

Ở đây:

js
obj.hasOwnProperty("name"); // true
obj.hasOwnProperty("age");  // false

Do đó chỉ name được tính vào kết quả.

⚠️ Edge case của obj.hasOwnProperty()

Trong các object thông thường, cách viết:

js
obj.hasOwnProperty(key);

hoạt động tốt và dễ đọc.

Tuy nhiên, không nên mặc định rằng mọi object đều có thể gọi hasOwnProperty() an toàn.

Có hai edge case quan trọng.

Edge case 1 — Object ghi đè hasOwnProperty

Object có thể có một property tên hasOwnProperty:

js
const obj = {
  name: "An",
  hasOwnProperty: "hello"
};

obj.hasOwnProperty("name");
// ❌ TypeError: obj.hasOwnProperty is not a function

Lý do:

js
obj.hasOwnProperty

không còn trỏ tới method được kế thừa từ Object.prototype.

Nó đã bị property "hello" của chính obj ghi đè.

Edge case 2 — Object không có Object.prototype

Object được tạo bằng Object.create(null) sẽ không có prototype:

js
const obj = Object.create(null);

obj.name = "An";

obj.hasOwnProperty("name");
// ❌ TypeError

Object này không kế thừa hasOwnProperty() từ Object.prototype.

💡 Cách an toàn hơn

Có hai cách phổ biến để kiểm tra own property mà không phụ thuộc vào obj.hasOwnProperty().

1. Object.prototype.hasOwnProperty.call()

js
Object.prototype.hasOwnProperty.call(obj, key);

Thay vì gọi:

js
obj.hasOwnProperty(key);

ta lấy trực tiếp method từ Object.prototype rồi dùng call() để truyền obj vào.

Cách này xử lý được cả trường hợp object ghi đè hasOwnProperty:

js
const obj = {
  name: "An",
  hasOwnProperty: "hello"
};

Object.prototype.hasOwnProperty.call(obj, "name");
// true

Và cả object được tạo bằng Object.create(null):

js
const obj = Object.create(null);

obj.name = "An";

Object.prototype.hasOwnProperty.call(obj, "name");
// true

Cách này đặc biệt hữu ích trong code cũ hoặc khi cần tương thích với các môi trường JavaScript cũ.

2. Object.hasOwn()

Từ ES2022, JavaScript cung cấp API:

js
Object.hasOwn(obj, key);

Ví dụ:

js
const obj = {
  name: "An"
};

Object.hasOwn(obj, "name");
// true

API này không phụ thuộc vào obj.hasOwnProperty, nên cũng xử lý được cả hai edge case:

js
const obj = {
  name: "An",
  hasOwnProperty: "hello"
};

Object.hasOwn(obj, "name");
// true

js
const obj = Object.create(null);

obj.name = "An";

Object.hasOwn(obj, "name");
// true

Vì vậy, trong code hiện đại, Object.hasOwn() thường là lựa chọn nên ưu tiên.

So sánh

CáchObject ghi đè hasOwnPropertyObject.create(null)Khuyến nghị
obj.hasOwnProperty(key)❌❌Đơn giản, phù hợp bài học
Object.prototype.hasOwnProperty.call(obj, key)✅✅An toàn, tương thích rộng
Object.hasOwn(obj, key)✅✅Ưu tiên trong code mới

Trong phạm vi bài học

Mục tiêu của bài này là làm quen với:

text
for...in
    ↓
duyệt enumerable properties
    ↓
hasOwnProperty()
    ↓
lọc chỉ own properties

Vì vậy, đáp án tham khảo vẫn dùng:

js
for (const key in obj) {
  if (obj.hasOwnProperty(key)) {
    count++;
  }
}

Nhưng khi viết code thực tế, nên ưu tiên:

js
for (const key in obj) {
  if (Object.hasOwn(obj, key)) {
    count++;
  }
}

Ghi nhớ: for...in có thể duyệt cả properties được kế thừa từ prototype. hasOwnProperty() và Object.hasOwn() giúp lọc ra chỉ những properties thuộc trực tiếp object.

Level 3 — Independent (Độc lập) ​

Viết hàm sumNumericValues. Nhận một object, dùng for...in để tính tổng tất cả own properties có giá trị là number. Bỏ qua properties không phải number.

js
function sumNumericValues(obj) {
  // Implement here — dùng for...in + hasOwn + typeof
}

// Test
console.log(sumNumericValues({ a: 10, b: "hello", c: 20, d: true }));
// Expected: 30
console.log(sumNumericValues({ x: 5, y: 5 }));
// Expected: 10
[Đáp án tham khảo]
js
function sumNumericValues(obj) {
  let sum = 0;
  for (const key in obj) {
    if (Object.hasOwn(obj, key) && typeof obj[key] === "number") {
      sum += obj[key];
    }
  }
  return sum;
}

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

Edge 1: for...in trên array với prototype pollution ​

js
Array.prototype.bad = "pollution";
const arr = [1, 2];

for (const key in arr) {
  console.log(key);
}
// "0", "1", "bad" ❌

Tại sao fail: for...in duyệt qua tất cả enumerable properties, kể cả trên prototype. Đây là lý do chính for...in không dùng cho array.

Fix: Dùng for...of, for thông thường, hoặc array methods.

Edge 2: for...in trên string ​

js
for (const idx in "hi") {
  console.log(idx, typeof idx);
}
// "0" string
// "1" string

Caveat: for...in trên string trả về index dạng string, không phải ký tự. Nếu cần ký tự, dùng for...of.

Edge 3: Sparse array ​

js
const arr = [1, , 3]; // index 1 là "empty"

for (const val of arr) {
  console.log(val);
}
// 1
// undefined
// 3

Caveat: for...of duyệt qua cả "empty slots" và trả về undefined. for...in thì bỏ qua empty slots (vì không có property tại index đó).

Edge 4: for...of trên null / undefined ​

js
for (const x of null) { }
// TypeError: null is not iterable

Tại sao fail: null và undefined không phải iterable. Phải guard trước khi dùng for...of.

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

Symptom (Triệu chứng): Hàm sumScores trả về kết quả sai khi mảng có method tùy chỉnh.

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

js
function sumScores(scores) {
  let total = 0;
  for (const item in scores) {
    total += scores[item];
  }
  return total;
}

// Trong một file khác, library thêm vào Array.prototype
Array.prototype.last = function () { return this[this.length - 1]; };

console.log(sumScores([10, 20, 30]));
// Actual: "60function () { return this[this.length - 1]; }" (string)
// Expected: 60

Evidence:

scores["last"] là function object. Khi cộng vào total (number, lúc này đã là 60), JavaScript chuyển function thành string ("function ()..."), rồi thực hiện string concatenation: "60" + "function ()..." → kết quả là string kỳ lạ. Đây là coercion từ Module 0.3 quay lại.

Hypothesis (Giả thuyết): Dùng for...in trên array là anti-pattern. Phải đổi thành for...of.

Verification (Xác minh):

js
function sumScores(scores) {
  let total = 0;
  for (const score of scores) {
    total += score;
  }
  return total;
}

console.log(sumScores([10, 20, 30]));
// → 60 ✅

Root Cause (Nguyên nhân gốc rễ):for...in lặp qua enumerable properties của object. Array là object, nên for...in duyệt qua cả properties trên prototype chain. for...of chỉ lặp qua iterable values theo index số, không bị ảnh hưởng bởi prototype.

Fix (Sửa lỗi):

  • Không bao giờ dùng for...in để lặp array.
  • Dùng for...of, for thông thường, hoặc array methods (reduce, forEach).

Prevention (Phòng ngừa):

  • Code review: cấm for...in trên array.
  • Lint rule: ESLint no-for-in-array.
  • Nếu buộc phải dùng for...in trên object, luôn kiểm tra Object.hasOwn.

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

INFO

Ở depth L3, bài tập thiết kế tập trung vào quyết định đơn giản giữa các lựa chọn.

Context (Bối cảnh): Bạn cần viết hàm printReport(data).

Constraints (Ràng buộc):

  • Nếu data là array: in từng giá trị.
  • Nếu data là object: in từng cặp key: value.

Question:

  1. Với array, bạn chọn for...in hay for...of? Giải thích.
  2. Với object, bạn chọn for...in hay for...of? Giải thích.
  3. Viết code cho cả hai nhánh.
[Đáp án tham khảo]
  1. Array → for...of (lấy values, không bị prototype pollution).
  2. Object → for...in (lấy keys), hoặc Object.entries() + for...of.
  3. Code:
    js
    function printReport(data) {
      if (Array.isArray(data)) {
        for (const item of data) {
          console.log(item);
        }
      } else {
        for (const key in data) {
          if (Object.hasOwn(data, key)) {
            console.log(`${key}: ${data[key]}`);
          }
        }
      }
    }

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

Bối cảnh: Bạn review PR của đồng nghiệp. Họ viết:

js
function formatItems(items) {
  const result = [];
  for (const i in items) {
    result.push(`Item ${i}: ${items[i]}`);
  }
  return result;
}

Vấn đề:

  1. items là array. i là string "0", "1" — không phải number.
  2. Nếu một library nào đó thêm vào Array.prototype, hàm này sẽ include cả method đó.

Câu hỏi:

  1. Tại sao i là string gây vấn đề?
  2. Bạn refactor như thế nào?
  3. Nếu items có thể là object hoặc array, bạn xử lý thế nào?
[Đáp án tham khảo]
  1. i là string nên "Item " + i nối chuỗi — vẫn đúng, nhưng nếu cần tính toán số học (ví dụ i + 1), "0" + 1 = "01" thay vì 1.
  2. Refactor:
    js
    function formatItems(items) {
      const result = [];
      for (let i = 0; i < items.length; i++) {
        result.push(`Item ${i}: ${items[i]}`);
      }
      return result;
    }
    Hoặc dùng for...of nếu không cần index.
  3. Dùng Array.isArray() để phân nhánh, hoặc dùng Object.entries() cho cả hai.

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

Level B — Challenge (Thử thách)

  1. Tự viết trước: Viết một đoạn giải thích ngắn (3–5 câu) về tại sao không nên dùng for...in trên array. Tập trung vào prototype và type của key.
  2. Đưa cho AI review: Dùng prompt: "Review my explanation of why for...in should not be used on arrays in JavaScript. Is it accurate? What's missing?"
  3. So sánh: AI có nhắc đến enumerable properties không? AI có cảnh báo về string keys không?
  4. Verify: Kiểm tra lại bằng MDN — search "for...in".

Gợi ý

AI thường nói: "Use for...of for arrays, for...in for objects." Điều này đúng nhưng là rule-of-thumb. Hãy kiểm tra xem AI có giải thích tại sao for...in duyệt qua prototype không. AI cũng có thể không nhắc đến việc key là string, không phải number.

[Đáp án tham khảo]
  • Bạn nghĩ: for...in lặp qua tất cả enumerable properties của object. Array là object, nên nó cũng lặp qua properties trên prototype chain. Nếu ai đó thêm Array.prototype.something, vòng lặp sẽ duyệt qua cả "something". Hơn nữa, key trả về là string "0", "1" — không phải number. Nếu cần index số để tính toán, phải parse, dễ gây bug.

  • AI trả lờì: "for...in iterates over all enumerable properties, including those on the prototype chain. It's better to use for...of for arrays because it only iterates over values." — Đúng nhưng thiếu depth.

  • So sánh: AI đúng về prototype, nhưng có thể không nhắc đến:

    • String keys: AI hiếm khi nhấn mạnh key là string, không phải number.
    • Sparse array behavior: AI có thể không nhắc đến việc for...in bỏ qua empty slots còn for...of thì không.
    • Performance: AI có thể không nhắc đến for...in chậm hơn vì phải kiểm tra prototype chain.
  • Điểm AI nói sai hoặc quá mơ hồ:

    • "Better to use" là subjective. Không giải thích mechanism (enumerable properties + prototype chain).
    • AI có thể không nhắc đến Object.hasOwn như cách an toàn nếu buộc phải dùng for...in.
  • Kết luận: Nếu bạn chỉ ra được rằng for...in trên array có 3 vấn đề (prototype pollution, string keys, sparse array inconsistency) — bạn đã hiểu sâu hơn AI. Điều này sẽ quay lại ở Stage 2 (Prototype chain), Stage 6 (TypeScript — iterating over object types), và Stage 8 (React — iterating over lists).

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

Yêu cầu: Giải thích cho một đồng nghiệp Junior trong 2 phút:

"Tại sao tôi không nên dùng for...in để lặp qua mảng? Và khi nào for...in thực sự hữu ích?"

Dùng đúng terminology: enumerable properties, prototype chain, iterable, keys vs values.

Mô phỏng
  • Bạn nói: for...in lặp qua tên của mọi thuộc tính enumerable — không chỉ array elements. Nếu ai đó thêm method vào Array.prototype, for...in sẽ duyệt qua cả method đó. Key trả về cũng là string "0", "1" — không phải number. Với mảng, dùng for...of để lấy giá trị, hoặc for thông thường để lấy index số. for...in chỉ hữu ích khi bạn muốn duyệt qua properties của object thuần — ví dụ object config hoặc dictionary.

💡 Hãy tưởng tượng for...in như việc lục tung cả nhà kho để tìm đồ — bạn tìm thấy cả thứ không mong muốn. for...of như việc lấy đồ từng ngăn theo thứ tự — chỉ lấy đúng thứ bạn cần.

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

  • Đồng nghiệp có hiểu tại sao for...in trên array có thể duyệt qua prototype properties không?
  • Bạn có thể dự đoán output của for...in và for...of với sparse array không?
  • Bạn có thể giải thích tại sao for...of không hoạt động trên plain object không?

15. Assessment (Đánh giá) ​

Objective (Mục tiêu)Hình thức đánh giáTiêu chí
Phân biệt for...in vs for...ofClassification (Phân loại)Chọn đúng ≥ 4/5 scenarios
Dự đoán outputPrediction (Dự đoán)Đúng ≥ 8/10 cases
Giải thích anti-patternExplain (Giải thích)Nhắc đến prototype và string keys
Chọn đúng vòng lặpDecision (Quyết định)Chọn đúng cho array/object/string
Nhận diện bugDebug (Gỡ lỗi)Tìm ra for...in trên array trong 2/2 cases

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

  • [ ] Có thể phân biệt for...in (keys/properties) và for...of (values) trong 4/5 tình huống.
  • [ ] Có thể dự đoán đúng output của cả hai với array, string, và object.
  • [ ] Có thể giải thích tại sao for...in trên array là anti-pattern — nhắc đến prototype chain và string keys.
  • [ ] Có thể chọn đúng loại vòng lặp cho array, string, và plain object.
  • [ ] Có thể nhận diện và sửa bug do dùng for...in trên array trong code thực tế.

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

Previous (Trước): Loops (0.4.5) → Object Values (0.2.4) → Arrays (0.6.2)

Current (Hiện tại): for...of vs for...in — phân biệt lặp qua keys (enumerable properties) và values (iterable protocol).

Next (Tiếp theo): Array Iteration methods (0.6.3) → Object Utilities (0.6.6) → Iterator Protocol & Prototype Chain (Stage 2) → React list rendering (Stage 8) → TypeScript narrowing with in operator (Stage 6)

📴 Offline Mode — Content served from cache