Bạn chuyển một đoạn xử lý danh sách từ Python sang JavaScript và viết array[-1] để lấy phần tử cuối. Kết quả có thể là undefined dù mảng vẫn có dữ liệu. Cú pháp nhìn quen nhưng JavaScript không diễn giải chỉ số âm trong ngoặc vuông như phép đếm từ cuối mảng.
Bài đối chiếu MDN ngày 09/11/2026, chạy ví dụ trên Node.js 24.18.1. Tiêu chí so sánh gồm ý nghĩa chỉ số, trường hợp ngoài phạm vi, cách xử lý dữ liệu rỗng và độ rõ khi review. Không có benchmark hoặc kết luận về tốc độ trên browser và runtime mobile.
So trực tiếp ba cách truy cập
const colors = ['red', 'green', 'blue'];
console.log(colors.at(-1));
console.log(colors[colors.length - 1]);
console.log(colors[-1]);
console.log(colors.at(-2));
console.log(colors.at(-4));
Kết quả lần lượt là blue, blue, undefined, green và undefined. Array.at hỗ trợ chỉ số tương đối: -1 là cuối, -2 là phần tử kế trước. Với ngoặc vuông, -1 trở thành tên thuộc tính dạng chuỗi; nó không tự cộng với length.
Cách tính array[array.length - 1] vẫn hữu ích và thể hiện rõ chỉ số không âm cần đọc. at làm ý định đếm từ cuối ngắn hơn. Nếu toàn bộ code chỉ truy cập chỉ số dương đã xác định, đổi sang at không tự mang lại một lợi ích nghiệp vụ mới.
Thuộc tính có tên -1 không trở thành phần tử cuối
colors[-1] = 'metadata';
console.log(colors[-1]);
console.log(colors.at(-1));
console.log(colors.length);
Ba giá trị là metadata, blue và 3. Đoạn này cố tình gắn một thuộc tính để chứng minh hai địa chỉ khác nhau. Nó không phải khuyến nghị lưu metadata trên array; một object riêng thường làm cấu trúc dữ liệu dễ đọc hơn.
Quan sát thêm rằng at chỉ đọc giá trị ở vị trí được chọn, không xóa phần tử như pop. Nếu cần xem phần tử cuối mà vẫn giữ danh sách, tránh dùng pop rồi thêm lại chỉ để lấy một giá trị. Nếu nhiệm vụ thật sự là lấy và loại khỏi hàng đợi, đó lại là hợp đồng khác.
Mảng rỗng cần một quyết định rõ ràng
function lastEvent(events) {
if (!Array.isArray(events)) throw new TypeError('Expected an array');
if (events.length === 0) return {kind: 'empty'};
return {kind: 'item', value: events.at(-1)};
}
console.log(JSON.stringify(lastEvent([])));
console.log(JSON.stringify(lastEvent([0])));
console.log(JSON.stringify(lastEvent([false])));
Kết quả phân biệt {kind:'empty'} với hai trường hợp có item mang giá trị 0 và false. Helper không dùng phép kiểm tra truthy để quyết định dữ liệu tồn tại. Nó cũng không kiểm tra nội dung event; tên hàm ở đây chỉ minh họa cấu trúc kết quả có trạng thái.
Nếu array có phần tử undefined hoặc có lỗ ở vị trí cuối, trạng thái item vẫn có thể mang value undefined. Ứng dụng cần hợp đồng dữ liệu chặt hơn thì kiểm tra schema trước hoặc thêm quy tắc vào helper. Một giá trị trả về undefined từ at tự nó không giải thích được mọi nguyên nhân.
Phần tử cuối chưa chắc là sự kiện mới nhất
at(-1) đọc theo thứ tự hiện có trong array. Nó không so timestamp, không tải dữ liệu còn thiếu ở trang tiếp theo và không sắp xếp lại danh sách. Nếu API trả dữ liệu mới nhất trước, phần tử đầu có thể mới hơn phần tử cuối. Hãy chốt thứ tự dữ liệu trước khi đặt tên biến là latestEvent.
Nếu phần tử là object, kết quả là tham chiếu đến object ấy. Sửa thuộc tính của object nhận được có thể thay đổi thứ bạn nhìn thấy trong array. at không tự tạo bản sao sâu hoặc đóng băng dữ liệu; yêu cầu bất biến cần được xử lý ở tầng tổ chức dữ liệu.
Chi phí áp dụng và cách chọn
Trong môi trường Node của bài, hai cách đọc đều dùng API có sẵn, không thêm package. Với browser hoặc runtime khác, kiểm tra hỗ trợ trong môi trường mục tiêu trước khi thay hàng loạt. Đừng dùng một lần chạy Node làm bằng chứng rằng mọi bản runtime cũ đều có at.
Chọn at khi chỉ số âm giúp diễn đạt trực tiếp ý định đếm từ cuối. Chọn ngoặc vuông với chỉ số tính từ đầu khi codebase cần cách truy cập quen thuộc hoặc đang thao tác tên thuộc tính. Dù chọn cách nào, thêm ca mảng rỗng, một phần tử và chỉ số ngoài phạm vi vào kiểm tra để tránh biến undefined thành một giả định sai về dữ liệu.


