Tìm không thấy bản ghi, nhưng giao diện lại mở phần tử cuối danh sách. Một nguyên nhân có thể là code đưa trực tiếp kết quả findIndex vào Array.at. findIndex trả -1 khi không tìm thấy, còn at(-1) lại có nghĩa hợp lệ: lấy phần tử cuối.
Hai phương thức đều làm đúng hợp đồng của mình. Lỗi xuất hiện ở bước nối chúng mà chưa xử lý giá trị báo “không tìm thấy”. Bài viết dùng dữ liệu trong bộ nhớ, không gọi API hoặc cập nhật bản ghi thật.
1. Chỉ số âm của at có ý nghĩa riêng
const names = ['Lan', 'Minh', 'Ha'];
console.log(names.at(0)); // Lan
console.log(names.at(-1)); // Ha
console.log(names.at(-2)); // Minh
console.log(names[-1] === undefined); // true
at(-1) đếm từ cuối mảng, còn names[-1] đọc thuộc tính có tên "-1". Với mảng thông thường trong ví dụ, thuộc tính đó không tồn tại. Không thay cú pháp truy cập bằng at một cách máy móc nếu chỉ số âm đang được dùng làm mã báo lỗi.
at chỉ đọc phần tử, không lấy nó ra khỏi mảng như pop. Nếu cần đọc phần tử cuối mà giữ nguyên danh sách, nó diễn đạt ý định ngắn gọn.
2. Bẫy khi đưa -1 từ findIndex vào at
const users = [
{ id: 'a', name: 'Lan' },
{ id: 'b', name: 'Minh' }
];
const index = users.findIndex(user => user.id === 'missing');
console.log(index); // -1
console.log(users.at(index).name); // Minh
Code không ném lỗi vì phần tử cuối tồn tại. Điều này khiến lỗi khó thấy hơn một truy cập undefined: màn hình vẫn có dữ liệu nhưng là dữ liệu sai đối tượng. Kiểm thử chỉ với ID tồn tại sẽ không phát hiện trường hợp này.
Nguyên tắc tương tự áp dụng cho indexOf: -1 báo không tìm thấy không được chuyển thành chỉ số âm hợp lệ mà không có một quyết định rõ ràng.
3. Kiểm tra sentinel trước, hoặc lấy trực tiếp bằng find
function findUser(users, id) {
if (!Array.isArray(users) || typeof id !== 'string') {
throw new TypeError('Expected users and a string ID');
}
const index = users.findIndex(user => user.id === id);
return index === -1 ? undefined : users.at(index);
}
const users = [{ id: 'a', name: 'Lan' }, { id: 'b', name: 'Minh' }];
console.log(findUser(users, 'missing') === undefined); // true
console.log(findUser(users, 'a').name); // Lan
console.log(users.find(user => user.id === 'missing') === undefined); // true
Hàm giả định từng phần tử là bản ghi user đã được xác nhận đúng schema. Nó chỉ kiểm tra mảng ngoài và kiểu ID; không phải validator đầy đủ cho mọi dữ liệu API hoặc mảng thưa. Nếu chỉ cần object chứ không cần vị trí, find thường diễn đạt yêu cầu trực tiếp hơn.
Bên gọi cần quyết định khi không tìm thấy: hiển thị trạng thái phù hợp, báo lỗi hoặc chuyển sang luồng khác. Không mặc định chọn phần tử cuối hay phần tử đầu để tránh màn hình trống nếu điều đó làm sai ý nghĩa thao tác.
4. Ngoài phạm vi cũng trả undefined
const values = [undefined, 'B'];
console.log(values.at(0) === undefined); // true
console.log(values.at(99) === undefined); // true
console.log([].at(-1) === undefined); // true
console.log(values.length); // 2
Chỉ nhìn giá trị undefined chưa đủ phân biệt phần tử có giá trị undefined với truy cập ngoài phạm vi. Nếu sự khác biệt đó quan trọng, kiểm tra chỉ số và cấu trúc dữ liệu theo contract.
at có chuyển đổi đối số chỉ số, nên một API nhận chỉ số từ ngoài cần xác nhận kiểu và miền giá trị trước. Đừng để chuỗi rỗng hoặc một giá trị không mong muốn âm thầm trở thành vị trí khác. Với trường hợp đọc tương đối từ cuối có chủ đích, hãy cho phép chỉ số âm bằng quy tắc riêng.
5. Đọc object không tạo bản sao
Nếu phần tử là object, at trả tham chiếu tới object đang có trong mảng. Sửa thuộc tính qua kết quả có thể ảnh hưởng nơi khác đang dùng cùng dữ liệu. “Không sửa mảng khi đọc” không đồng nghĩa “mọi thao tác tiếp theo đều độc lập với nguồn”.
Với ứng dụng có phân trang, phần tử cuối của trang hiện tại cũng không tự là bản ghi mới nhất trong toàn bộ dữ liệu. Muốn lấy bản mới nhất cần thứ tự và phạm vi do API công bố, không chỉ một lời gọi at(-1).
6. Bộ ca kiểm thử tối thiểu
- ID tồn tại ở đầu, giữa và cuối danh sách.
- ID không tồn tại và danh sách rỗng.
- findIndex hoặc indexOf trả -1 trước khi gọi at.
- Chỉ số dương hoặc âm vượt phạm vi.
- Phần tử undefined nếu schema cho phép.
- Tham chiếu object dùng chung và thứ tự dữ liệu từ API.
Các đoạn code đã chạy bằng Node.js local, có kiểm tra bổ sung bản ghi không tìm thấy, kiểu đầu vào và tham chiếu object. Chưa kiểm thử giao diện hay API sản phẩm. Tài liệu đối chiếu: Array.prototype.at và Array.prototype.findIndex.
Chỉ số âm hữu ích khi được dùng có chủ đích. Khi nối các phương thức, xử lý giá trị báo thất bại trước khi biến nó thành một vị trí hợp lệ.




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