Đổi for...of thành for...in không chỉ thay cú pháp: vòng lặp có thể nhận tên thuộc tính thay vì giá trị và lấy thêm dữ liệu từ prototype. Trước khi sửa vòng lặp, xác định rõ muốn duyệt phần tử của danh sách hay thuộc tính của object.
1. for...in có thể lấy cả khóa kế thừa
const defaults = { theme: 'dark' };
const settings = Object.create(defaults);
settings.language = 'vi';
const keys = [];
for (const key in settings) keys.push(key);
console.log(keys.join(', ')); // language, theme
console.log(JSON.stringify(Object.entries(settings))); // [["language","vi"]]
for...in duyệt khóa chuỗi enumerable, gồm cả khóa kế thừa. Object.entries chỉ lấy các cặp khóa–giá trị chuỗi enumerable thuộc chính object. Trong ví dụ, theme được đọc qua prototype chứ không phải thuộc tính riêng của settings.
Nếu cần gửi “những tùy chọn người dùng đã lưu”, hai cách duyệt có thể tạo payload khác nhau. Mặc định kế thừa chưa chắc phải được ghi như lựa chọn tường minh. Hãy quyết định có đưa mặc định vào payload không, rồi chọn cách lấy trường phù hợp.
2. Với mảng, khóa và giá trị là hai thứ khác nhau
const rows = ['A', 'B'];
rows.note = 'cached';
const keys = [];
const values = [];
for (const key in rows) keys.push(key);
for (const value of rows) values.push(value);
console.log(JSON.stringify(keys)); // ["0","1","note"]
console.log(JSON.stringify(values)); // ["A","B"]
console.log(typeof keys[0]); // string
Với array iterator chuẩn, for...of lấy các giá trị phần tử. for...in liệt kê cả note vì đây là thuộc tính enumerable; các khóa chỉ mục của nó là chuỗi. Vì vậy, thay vòng lặp có thể làm code xử lý '0' thay cho 'A'.
Nếu cần cả vị trí và giá trị phần tử, cân nhắc rows.entries() rồi duyệt bằng for...of. Đây là phương thức của mảng, khác với Object.entries(rows): cách sau vẫn liệt kê thuộc tính note trong ví dụ này.
3. “Thuộc object” chưa có nghĩa “sẽ được liệt kê”
const marker = Symbol('marker');
const record = { title: 'Draft' };
Object.defineProperty(record, 'internal', { value: 7 });
record[marker] = 'local';
console.log(JSON.stringify(Object.entries(record))); // [["title","Draft"]]
console.log(Object.hasOwn(record, 'internal')); // true
console.log(Object.hasOwn(record, marker)); // true
Thuộc tính internal được tạo mà không bật enumerable; marker là khóa Symbol. Cả hai thuộc record nhưng không xuất hiện trong Object.entries. Vì thế, danh sách entries không phải danh sách mọi thuộc tính.
Nếu cần công cụ kiểm tra toàn bộ khóa riêng, có thể dùng API phản chiếu phù hợp như Reflect.ownKeys. Còn với payload nghiệp vụ, thường cần chọn danh sách trường được phép và xác thực giá trị, thay vì gửi tất cả những gì vừa liệt kê được. Liệt kê đúng phạm vi không thay thế kiểm tra schema.
4. Object thường không tự trở thành iterable
Object literal thông thường không có iterator nên không thể chỉ đổi thành for...of settings. Để duyệt cặp khóa–giá trị riêng enumerable, dùng for...of trên Object.entries(settings). Nếu object có iterator tùy biến, nội dung iterator trả về do object đó quyết định, không tự đồng nhất với thuộc tính enumerable.
Các ví dụ dùng object và mảng thông thường, không thay đổi cấu trúc trong khi duyệt. Tránh thêm, xóa thuộc tính hoặc đổi prototype giữa vòng lặp nếu chưa có contract cụ thể; việc thử một ví dụ đơn giản không chứng minh hành vi cho mọi Proxy hay object đặc biệt.
5. Chọn vòng lặp theo dữ liệu cần lấy
| Nhu cầu | Cách phù hợp để bắt đầu |
|---|---|
| Giá trị phần tử mảng thông thường | for...of array |
| Chỉ mục và giá trị phần tử | for...of array.entries() |
| Cặp thuộc tính riêng, chuỗi, enumerable | for...of Object.entries(object) |
| Cố ý xem khóa enumerable qua prototype | for...in, với phạm vi được hiểu rõ |
Trước khi sửa, thử ít nhất một mảng có thuộc tính phụ và một object có prototype mang dữ liệu. Xác nhận bên nhận cần tên khóa hay giá trị, và có cần mặc định kế thừa hay không.
Sáu khối mã Việt–Anh đã chạy bằng Node.js, kèm kiểm tra array.entries, Object.entries trên mảng và object không iterable. Chưa tích hợp form cấu hình hoặc API thực tế.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.