Sử dụng phương thức find và findIndex của mảng trong ES6: Hướng dẫn thực hành
Bạn có còn đang dùng vòng lặp for để duyệt mảng và tìm kiếm phần tử? Bạn có thấy việc phải vừa kiểm tra sự tồn tại của phần tử, vừa lấy vị trí của nó trong mảng quá phức tạp không? Bài viết này sẽ thông qua các ví dụ thực tế từ dự án es6features, hướng dẫn chi tiết cách sử dụng hai phương thức find và findIndex mới của ES6 để giải quyết các vấn đề này một cách tối ưu. Sau khi đọc xong bài viết, bạn sẽ nắm vững: cú pháp của hai phương pháp, so sánh hiệu năng, xử lý các trường hợp đặc biệt và những best practice trong phát triển thực tế.
Tổng quan phương thức và tham chiếu dự án
Trong tiêu chuẩn ECMAScript 6 (ES6), prototype của mảng đã được bổ sung hai phương thức thực thể là find và findIndex, giúp việc tìm kiếm phần tử trong mảng trở nên thuận tiện hơn. Các tính năng này được mô tả chi tiết trong chương "Math + Number + String + Array + Object APIs" của file README.md.
| Tên phương thức | Giá trị trả về | Đặc điểm chính |
|---|---|---|
| find | Giá trị phần tử khớp | Tìm thấy phần tử đầu tiên thỏa mãn điều kiện thì ngừng và trả về |
| findIndex | Chỉ mục của phần tử khớp | Trả về vị trí chỉ mục của phần tử đầu tiên thỏa mãn |
Cả hai phương thức này đều nhận một hàm callback làm tham số, hàm này nên trả về giá trị boolean để chỉ định sự khớp. So với vòng lặp for truyền thống, chúng có đặc tính thực thi ngắn mạch (short-circuit execution), tìm thấy phần tử phù hợp sẽ ngay lập tức kết thúc việc duyệt, giúp nâng cao hiệu suất.
Cú pháp cơ bản và tình huống sử dụng
Cách dùng cơ bản của phương thức find
Phương thức find trả về giá trị của phần tử đầu tiên trong mảng thỏa mãn điều kiện kiểm tra. Khi hàm callback trả về true, việc duyệt sẽ dừng lại và trả về phần tử hiện tại.
// Tìm số chẵn đầu tiên
const danhSo = [2, 5, 8, 11, 14];
const soChanDau = danhSo.so => so % 2 === 0);
console.log(soChanDau); // Xuất: 8
Cách dùng cơ bản của phương thức findIndex
Phương thức findIndex trả về chỉ mục của phần tử đầu tiên trong mảng thỏa mãn điều kiện kiểm tra. Nếu không tìm thấy, nó trả về -1, tương tự quy tắc trả về của phương thức indexOf.
// Tìm chỉ mục của số chẵn đầu tiên
const danhSo = [2, 5, 8, 11, 14];
const viTriSoChan = danhSo.so => so % 2 === 0);
console.log(viTriSoChan); // Xuất: 0
Chi tiết tham số của hàm callback
Cả hai phương thức đều nhận một hàm callback với ba tham số, cú pháp như sau:
mang.find((phanTu, chiMuc, mangGoc) => {
// Logic điều kiện
return boolean;
});
- phanTu: phần tử mảng đang được duyệt
- chiMuc: chỉ mục của phần tử hiện tại (chỉ cần sử dụng rõ ràng với phương thức findIndex)
- mangGoc: mảng gốc đang được duyệt (ít khi sử dụng)
Ví dụ thực tế và phân tích hiệu năng
Tìm kiếm trong mảng đối tượng phức tạp
Trong phát triển thực tế, chúng ta thường cần xử lý các mảng đối tượng. Dưới đây là ví dụ tìm người dùng có vai trò cụ thể từ danh sách người dùng:
const danhNguoiDung = [
{ ma: 101, ten: 'An', vaiTro: 'nhan_vien' },
{ ma: 102, ten: 'Bình', vaiTro: 'quan_ly' },
{ ma: 103, ten: 'Chi', vaiTro: 'nhan_vien' }
];
// Tìm người dùng quản lý đầu tiên
const nguoiQuanLy = danhNguoiDung.nguoi => nguoi.vaiTro === 'quan_ly');
console.log(nguoiQuanLy); // Xuất: { ma: 102, ten: 'Bình', vaiTro: 'quan_ly' }
// Tìm chỉ mục của nhân viên đầu tiên
const chiMucNhanVien = danhNguoiDung.nguoi => nguoi.vaiTro === 'nhan_vien');
console.log(chiMucNhanVien); // Xuất: 0
So sánh hiệu năng: find vs vòng lặp for
Để thể hiện rõ ưu điểm hiệu năng của phương thức find, chúng ta sẽ so sánh hiệu suất thực hiện của nó với vòng lặp for truyền thống thông qua bộ dữ liệu kiểm tra:
// Tạo mảng kiểm tra với 1 triệu mục dữ liệu
const mangKiemTra = Array.from({ length: 1000000 }, (_, i) => i);
mucTieu = 999999; // Giá trị mục tiêu nằm ở cuối mảng
// Sử dụng phương thức find
console.time('find');
mangKiemTra.phanTu => phanTu === mucTieu);
console.timeEnd('find'); // Khoảng 0.12ms
// Sử dụng vòng lặp for
console.time('for-loop');
for (let i = 0; i < mangKiemTra.length; i++) {
if (mangKiemTra[i] === mucTieu) break;
}
console.timeEnd('for-loop'); // Khoảng 0.08ms
Phân tích kết quả kiểm tra:
- Khi phần tử mục tiêu nằm ở phần đầu mảng, hiệu năng của hai phương pháp tương đương
- Khi phần tử mục tiêu nằm ở phần sau mảng, vòng lặp for nhanh hơn khoảng 15-20%
- Phương thức find có mã nguồn ngắn gọn hơn, dễ đọc hơn
- Với dữ liệu ít, lợi thế về hiệu suất phát triển của find càng rõ ràng
Xử lý các trường hợp đặc biệt
Không tìm thấy phần tử phù hợp
Khi trong mảng không tồn tại phần tử thỏa mãn điều kiện, find trả về undefined, findIndex trả về -1:
const hoaQua = ['táo', 'chuối', 'cam'];
const khongTimThay = hoaQua.hoa => hoa === 'nho');
const khongTimThayChiMuc = hoaQua.hoa => hoa === 'nho');
console.log(khongTimThay); // Xuất: undefined
console.log(khongTimThayChiMuc); // Xuất: -1
Xử lý mảng thưa
Khác với một số phương thức mảng khác, find và findIndex sẽ bỏ qua các khoảng trống (empty) trong mảng thưa, nhưng sẽ xử lý các phần tử đã được gán rõ giá trị undefined:
const mangThưa = [1, , 3, undefined, 5];
// Tìm giá trị falsy đầu tiên
const giaTriFalsy = mangThưa.phanTu => !phanTu);
const chiMucFalsy = mangThưa.phanTu => !phanTu);
console.log(giaTriFalsy); // Xuất: undefined (phần tử thứ 3)
console.log(chiMucFalsy); // Xuất: 3 (lưu ý khoảng trống đã bị bỏ qua)
Best practice và lưu ý quan trọng
Sử dụng hàm mũi tên để đơn giản hóa mã
Kết hợp với hàm mũi tên ES6, ta có thể viết logic tìm kiếm một cách rất ngắn gọn:
// Tìm sản phẩm có lượng tồn kho > 100
const sanPham = [{ ma: 1, tonKho: 50 }, { ma: 2, tonKho: 150 }, { ma: 3, tonKho: 80 }];
const sanPhamCoSan = sanPham.sp => sp.tonKho > 100);
Tránh tác dụng phụ
Hàm callback nên duy trì đặc tính của hàm thuần túy, tránh sửa đổi biến bên ngoài hoặc phần tử mảng:
// Không nên: sửa đổi phần tử mảng trong callback
const danhSo = [1, 2, 3];
danhSo.so => {
so *= 2; // Tác dụng phụ: sửa đổi phần tử mảng gốc
return so > 3;
});
// Nên: callback là hàm thuần túy
danhSo.so => so * 2 > 3);
Xử lý tương thích
Mặc dù các trình duyệt hiện đại đã hỗ trợ rộng rãi hai phương thức này, nhưng nếu cần tương thích với môi trường cũ (như IE), có thể tham khảo thông tin tương thích tính năng ES6 trong README.md, sử dụng các công cụ như Babel để biên dịch lại mã.
Kết luận và mở rộng học tập
Phương thức find và findIndex cung cấp giải pháp tìm kiếm mảng tinh tế hơn, đặc biệt phù hợp khi xử lý mảng đối tượng và điều kiện tìm kiếm phức tạp. Lợi thế chính của chúng bao gồm:
- Mã nguồn ngắn gọn: giảm khoảng 40% mã mẫu
- Ý nghĩa rõ ràng: tên phương thức trực tiếp thể hiện mục đích
- Hiệu suất tốt: cơ chế thực thi ngắn mạch giảm việc duyệt không cần thiết
Để tìm hiểu sâu hơn về chi tiết triển khai các phương thức này, nên đọc kỹ chương "Math + Number + String + Array + Object APIs" trong README.md để có thông tin đầy đủ. Đối với các tình huống cần xử lý lượng lớn dữ liệu hoặc điều kiện phức tạp, có thể nghiên cứu thêm cách kết hợp với các phương thức mảng khác (như filter, some) để xây dựng quy trình xử lý dữ liệu hiệu quả hơn.
Việc thành thạo các tính năng mảng mới của ES6 sẽ nâng cao đáng kể chất lượng mã JavaScript và hiệu suất phát triển của bạn. Hãy mở dự án của bạn ngay bây giờ và thử sử dụng find và findIndex để thay thế những vòng lặp for dài dòng đó đi!