Lập trình · 21/10/2026

JavaScript Object.keys: vì sao thứ tự khóa khác thứ tự thêm?

Danh sách trạng thái hiển thị sai thứ tự dù dữ liệu đã được thêm đúng trình tự? Khi dùng mã số làm khóa của Object, Object.keys có thể trả thứ tự khác với thứ tự thêm. Điều này dễ xuất hiện khi biến danh sách từ API thành bảng tra cứu rồi dùng các khóa để dựng giao diện.

JavaScript Object.keys: vì sao thứ tự khóa khác thứ tự thêm?

Danh sách trạng thái hiển thị sai thứ tự dù dữ liệu đã được thêm đúng trình tự? Khi dùng mã số làm khóa của Object, Object.keys có thể trả thứ tự khác với thứ tự thêm. Điều này dễ xuất hiện khi biến danh sách từ API thành bảng tra cứu rồi dùng các khóa để dựng giao diện.

1. Khóa dạng chỉ số xuất hiện trước

const labels = {};
labels['10'] = 'Review';
labels['2'] = 'Draft';
labels.featured = 'Featured';
labels['01'] = 'Pinned';
console.log(Object.keys(labels).join('|')); // 2|10|featured|01

Với ordinary object, khóa là array index được liệt kê theo số tăng dần trước các khóa chuỗi còn lại. Nhóm chuỗi còn lại theo thứ tự tạo thuộc tính. Khóa '01' không phải array index, dù có thể chuyển thành số. Quy tắc được định nghĩa trong ECMAScript OrdinaryOwnPropertyKeys.

Object.keys chỉ trả các khóa chuỗi enumerable thuộc chính đối tượng; nó không lấy thuộc tính kế thừa, khóa Symbol hay thuộc tính không enumerable. Xem MDN Object.keys. Bài này xét object thông thường, không suy rộng sang Proxy hoặc mọi loại đối tượng đặc biệt.

2. Ghi đè giá trị khác với xóa rồi tạo lại

const menu = { home: 'Home', help: 'Help', contact: 'Contact' };
menu.help = 'Support';
console.log(Object.keys(menu).join('|')); // home|help|contact
delete menu.help;
menu.help = 'Support';
console.log(Object.keys(menu).join('|')); // home|contact|help

Hai thao tác đều cho cùng giá trị cuối của help, nhưng kết quả liệt kê khác nhau trong ví dụ. Vì vậy, một thao tác cập nhật dữ liệu có thể vô tình đổi vị trí mục nếu nó được triển khai bằng xóa rồi thêm. Đừng dùng vị trí tình cờ của một bảng tra cứu làm quy tắc ưu tiên nghiệp vụ.

3. Giữ danh sách hiển thị bằng mảng

const rows = [
  { id: '10', label: 'Review' },
  { id: '2', label: 'Draft' }
];
const byId = Object.fromEntries(rows.map(row => [row.id, row]));
console.log(rows.map(row => row.label).join('|')); // Review|Draft
console.log(Object.keys(byId).join('|')); // 2|10

Trong thiết kế này, rows lưu trình tự hiển thị, byId phục vụ tra cứu. Dựng giao diện từ rows thay vì chuyển ngược các khóa byId thành danh sách. Nếu API có thể trả ID trùng, cần quyết định rõ từ chối hay hợp nhất: mảng vẫn chứa cả hai hàng trong khi bảng tra cứu có thể chỉ giữ một giá trị cho khóa đó.

Nếu giao diện cần thứ tự theo mức ưu tiên hoặc ngày tạo, hãy biểu diễn trường đó trong dữ liệu rồi sắp xếp theo quy tắc cụ thể. Thứ tự thêm không tự thay thế cho yêu cầu sắp xếp của sản phẩm.

4. Map phù hợp khi cần duyệt theo thứ tự chèn

const menu = new Map([['10', 'Review'], ['2', 'Draft']]);
menu.set('10', 'Reviewed');
console.log([...menu.keys()].join('|')); // 10|2
menu.delete('10');
menu.set('10', 'Reviewed');
console.log([...menu.keys()].join('|')); // 2|10
console.log(Object.keys(Object.fromEntries(menu)).join('|')); // 2|10

Map duyệt theo thứ tự chèn; cập nhật một khóa hiện có không đưa nó xuống cuối. Xóa rồi chèn lại tạo vị trí mới. Đối chiếu MDN Map.

Chuyển Map thành Object không giữ nguyên mọi bảo đảm về thứ tự. Nếu cần gửi một danh sách có thứ tự qua API, dùng mảng các bản ghi hoặc cặp khóa–giá trị theo contract, thay vì mong bên nhận diễn giải thứ tự thành viên object giống JavaScript.

5. Kiểm tra ngay tại chỗ chuyển đổi dữ liệu

  • Test khóa '2', '10' và '01' cùng lúc.
  • Phân biệt cập nhật với xóa rồi tạo lại.
  • Giữ nguồn thứ tự và nguồn tra cứu rõ ràng.
  • Kiểm tra ID trùng trước khi tạo bảng tra cứu.
  • Test đầu ra sau khi đổi Map hoặc mảng thành Object.

Tám khối mã Việt–Anh được chạy bằng Node.js; có thêm kiểm tra biên array index và các loại khóa bị Object.keys bỏ qua. Đây là kiểm thử dữ liệu trong bộ nhớ, chưa phải kiểm thử giao diện trình duyệt hoặc contract API thực tế.

Thảo luận

Bình luận 0

Đăng nhập để bình luận

Bạn cần có tài khoản để tham gia thảo luận và trả lời độc giả khác.

Đăng nhậpĐăng ký

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