Cả hai đều lặp, nhưng lặp qua cái gì là khác biệt cốt lõi.
for...in — Lặp key (property name)
- Lặp qua tên thuộc tính liệt kê được (enumerable) của object.
- Bao gồm cả thuộc tính kế thừa (prototype chain).
- Thứ tự không đảm bảo (tùy engine).
- Key luôn là string (dù mảng index là số).
Ví dụ với mảng
<script>
Object.prototype.inherited = 'proto';
const arr = ['Apple', 'Microsoft', 'Tesla'];
arr.custom = 'extra';
for (const key in arr) {
document.write(key + '<br>');
}
// Output: "0", "1", "2", "custom", "inherited"
</script>
Lọc chỉ own property
for (const key in arr) {
if (arr.hasOwnProperty(key)) { // hoặc Object.hasOwn(arr, key)
document.write(key + '<br>');
}
}
// Output: "0", "1", "2", "custom"
for...of — Lặp value (giá trị)
- Lặp qua các giá trị của iterable object (Array, String, Map, Set, NodeList, arguments...).
- Tuân thủ protocol
Symbol.iterator. - Không lặp property kế thừa.
- Không làm việc trực tiếp với plain object ({}) — cần
Object.keys/values/entries.
Ví dụ với mảng
<script>
const arr = ['Apple', 'Microsoft', 'Tesla'];
for (const value of arr) {
document.write(value + '<br>');
}
// Output: "Apple", "Microsoft", "Tesla"
</script>
Ví dụ với String, Map, Set
for (const ch of 'abc') { console.log(ch); } // 'a', 'b', 'c'
const map = new Map([['a', 1], ['b', 2]]);
for (const [k, v] of map) { console.log(k, v); }
const set = new Set([1,2,3]);
for (const v of set) { console.log(v); }
Bảng so sánh
| Đặc điểm | for...in | for...of |
|---|---|---|
| Lặp qua | Property keys (string) | Iterable values |
| Kế thừa prototype | Có | Không |
| Đối tượng hỗ trợ | Mọi object | Chỉ iterable (Array, Map, Set, String...) |
| Thứ tự | Không đảm bảo | Đảm bảo (theo iterator) |
| Break/continue | Hỗ trợ | Hỗ trợ |
| Phù hợp | Duyệt property object | Duyệt giá trị mảng, collection |
Khi nào dùng cái nào?
- Duyệt mảng/Map/Set/String → for...of (an toàn, rõ ràng).
- Duyệt property object (debug, copy, serialize) → for...in +
hasOwnProperty. - Cần index + value trong mảng →
for (const [i, v] of arr.entries()).